Generics, Collections, and Core Data Structures

Working with `List`

ReadingPreview

আপনি একটি free preview lesson দেখছেন।

Lesson Overview

একটি application-এ অনেক সময় multiple values একই order-এ রাখতে হয়।

Examples:

Course lessons
Learner names
Quiz questions
Content items
Enrollment history

এ ধরনের data-এর জন্য Java-তে সবচেয়ে commonly used collection abstraction হলো:

List<E>

List:

  • Elements order ধরে রাখে
  • Index দিয়ে element access করতে দেয়
  • Duplicate values allow করে
  • Mutable বা immutable হতে পারে
  • Generic type safety provide করে

Example:

List<String> learnerNames =
        List.of(
                "Subu",
                "Sumu",
                "Nur"
        );

এই lesson-এ আমরা শিখব:

  • List কী
  • ArrayList
  • Mutable এবং immutable list
  • Element add, read, update এবং remove করা
  • Index
  • Duplicate elements
  • Iteration
  • Searching
  • contains(), indexOf(), এবং lastIndexOf()
  • List.of()
  • List.copyOf()
  • remove() overload-এর common trap
  • Defensive copying
  • Domain class-এর মধ্যে list safely own করা

Learning Objectives

এই lesson শেষ করার পর আপনি পারবেন:

  • List declare এবং initialize করতে
  • Mutable ArrayList তৈরি করতে
  • Elements add, read, update এবং remove করতে
  • Index-based access safely ব্যবহার করতে
  • Enhanced for loop দিয়ে list iterate করতে
  • List-এর মধ্যে value search করতে
  • Mutable এবং immutable list distinguish করতে
  • List.of() এবং List.copyOf() ব্যবহার করতে
  • remove(int) এবং remove(Object)-এর difference বুঝতে
  • Internal list safely expose করতে
  • List কখন appropriate collection তা explain করতে

What Is a List?

List একটি ordered collection।

Ordered মানে elements insertion sequence অনুযায়ী position ধরে রাখে।

List<String> names =
        List.of(
                "Subu",
                "Sumu",
                "Nur"
        );

Positions:

Index 0 → Subu
Index 1 → Sumu
Index 2 → Nur

List index সবসময়:

0

থেকে শুরু হয়।


List Preserves Order

List<String> lessonTitles =
        List.of(
                "Introduction",
                "Classes and Objects",
                "Inheritance"
        );

Iteration করলে same order পাওয়া যায়:

Introduction
Classes and Objects
Inheritance

Course lessons-এর মতো ordered data-এর জন্য List natural choice।


List Allows Duplicate Values

List<String> names =
        List.of(
                "Subu",
                "Sumu",
                "Subu"
        );

Valid।

List uniqueness enforce করে না।

যদি duplicate values invalid হয়, applicationকে:

  • Validate করতে হবে
  • Set ব্যবহার করতে হবে
  • Domain rule enforce করতে হবে

Set পরবর্তী lesson-এ শেখানো হবে।


The List Interface

List নিজে একটি interface।

List<String> names;

Object তৈরি করতে concrete implementation প্রয়োজন।

Common implementation:

ArrayList

Example:

List<String> names =
        new ArrayList<>();

Field বা variable type হিসেবে interface:

List<String>

ব্যবহার করা common।

Implementation:

new ArrayList<>()

Why Use List as the Variable Type?

Prefer:

List<String> names =
        new ArrayList<>();

Instead of:

ArrayList<String> names =
        new ArrayList<>();

Reason:

  • Caller common List contract-এর ওপর depend করে
  • Implementation later change করা easier
  • Variable unnecessary implementation details expose করে না

Use concrete ArrayList type only when ArrayList-specific behavior genuinely required।

Most application code-এর জন্য List type যথেষ্ট।


Importing List and ArrayList

import java.util.ArrayList;
import java.util.List;

Without imports, full names ব্যবহার করতে হয়:

java.util.List<String> names =
        new java.util.ArrayList<>();

Imports code readable রাখে।


Creating an Empty Mutable List

List<String> learnerNames =
        new ArrayList<>();

Initially:

learnerNames.size()

returns:

0

Check empty:

learnerNames.isEmpty()

returns:

true

Adding Elements

Use:

add(E element)

Example:

learnerNames.add(
        "Subu"
);

learnerNames.add(
        "Sumu"
);

learnerNames.add(
        "Nur"
);

List now:

[Subu, Sumu, Nur]

add() usually returns boolean

boolean added =
        learnerNames.add(
                "Jalisa"
        );

For ArrayList, successful add generally returns:

true

Adding at a Specific Index

learnerNames.add(
        1,
        "Sakib"
);

Before:

[Subu, Sumu, Nur]

After:

[Subu, Sakib, Sumu, Nur]

Existing elements shift right।

Valid insertion indexes:

0 through size()

If list size is 3, valid add indexes:

0, 1, 2, 3

Index 3 adds at the end।


Reading an Element by Index

Use:

get(int index)

Example:

String firstLearner =
        learnerNames.get(
                0
        );

If list:

[Subu, Sumu, Nur]

Result:

Subu

Index Range

For a list of size 3:

Valid indexes:
0
1
2

Last valid index:

list.size() - 1

Example:

String lastLearner =
        learnerNames.get(
                learnerNames.size() - 1
        );

Do not do this on an empty list।

For empty list:

size() - 1

becomes:

-1

and access fails।


Invalid Index

learnerNames.get(
        10
);

If index does not exist, Java throws:

IndexOutOfBoundsException

Common causes:

  • Using size() as a reading index
  • Negative index
  • Empty list থেকে first or last item read করা
  • Loop condition using <= instead of <

Updating an Existing Element

Use:

set(int index, E element)

Example:

learnerNames.set(
        1,
        "Jalisa"
);

Before:

[Subu, Sumu, Nur]

After:

[Subu, Jalisa, Nur]

set() previous value return করে।

String previous =
        learnerNames.set(
                1,
                "Jalisa"
        );

Result:

Sumu

set() Does Not Add a New Position

If list size 3, this is invalid:

learnerNames.set(
        3,
        "Sakib"
);

set() existing index replace করে।

New element add করতে:

learnerNames.add(
        "Sakib"
);

Removing by Index

String removed =
        learnerNames.remove(
                1
        );

Before:

[Subu, Sumu, Nur]

After:

[Subu, Nur]

Returned value:

Sumu

Following elements shift left।


Removing by Value

boolean removed =
        learnerNames.remove(
                "Sumu"
        );

If value found:

true

If not found:

false

Only first matching occurrence remove হয়।

Example:

[Subu, Sumu, Subu]

After:

names.remove(
        "Subu"
);

Result:

[Sumu, Subu]

A Common remove() Trap with Integer

Suppose:

List<Integer> scores =
        new ArrayList<>();

scores.add(
        10
);

scores.add(
        20
);

scores.add(
        30
);

Now:

scores.remove(
        1
);

This removes index 1, not value 1

Result:

[10, 30]

Because overload selected:

remove(int index)

Removing an Integer Value

To remove value 20:

scores.remove(
        Integer.valueOf(
                20
        )
);

This selects:

remove(Object value)

Result:

[10, 30]

Remember:

remove(1)

means index।

remove(Integer.valueOf(1))

means value।


List Size

int size =
        learnerNames.size();

For:

[Subu, Sumu, Nur]

Result:

3

size() is not the last index।

Last index:

size() - 1

Checking Whether a List Is Empty

if (
        learnerNames.isEmpty()
) {
    System.out.println(
            "No learners found."
    );
}

Prefer:

isEmpty()

over:

size() == 0

Both work, but isEmpty() communicates intention better।


Clearing a Mutable List

learnerNames.clear();

After:

[]

Then:

learnerNames.isEmpty()

returns:

true

Use clear() carefully inside domain objects।

For example, clearing all course lessons may violate business rules।


Checking Whether a Value Exists

boolean containsSubu =
        learnerNames.contains(
                "Subu"
        );

contains() uses equality comparison।

For custom objects, behavior depends on:

equals()

যদি equals() correctly implemented না হয়, logically equal objects may not match।


contains() with Custom Objects

Suppose:

Course first =
        new Course(
                1L,
                "Java"
        );

Course second =
        new Course(
                1L,
                "Java"
        );

Then:

List<Course> courses =
        new ArrayList<>();

courses.add(
        first
);

Call:

courses.contains(
        second
);

Result depends on Course.equals()

Without override, equality সাধারণত object identity-based।

Then result may be:

false

Even though field values same।

This is why equality design collections-এর জন্য important।


Finding the First Index

int index =
        learnerNames.indexOf(
                "Sumu"
        );

If found:

1

If not found:

-1

Do not assume non-negative result।

Check:

if (index >= 0) {
    System.out.println(
            "Learner found."
    );
}

Finding the Last Index

If duplicates exist:

List<String> names =
        List.of(
                "Subu",
                "Sumu",
                "Subu"
        );
names.indexOf(
        "Subu"
);

returns:

0
names.lastIndexOf(
        "Subu"
);

returns:

2

Iterating with Enhanced for

for (
        String learnerName
        : learnerNames
) {
    System.out.println(
            learnerName
    );
}

Use enhanced for when:

  • Every element process করতে হবে
  • Index প্রয়োজন নেই
  • Structure modify করা হবে না

Iterating with an Index

for (
        int index = 0;
        index < learnerNames.size();
        index++
) {
    String learnerName =
            learnerNames.get(
                    index
            );

    System.out.println(
            index
            + ": "
            + learnerName
    );
}

Use index loop when:

  • Position প্রয়োজন
  • Current element replace করতে হবে
  • Neighboring element compare করতে হবে
  • Sequence number print করতে হবে

The <= Loop Mistake

Wrong:

for (
        int index = 0;
        index <= learnerNames.size();
        index++
) {
    System.out.println(
            learnerNames.get(
                    index
            )
    );
}

When index == size(), no element exists।

Correct:

index < learnerNames.size()

Iterating with forEach

learnerNames.forEach(
        learnerName ->
                System.out.println(
                        learnerName
                )
);

This uses a lambda expression।

Lambdas later formally শেখানো হবে।

For now, enhanced for loop বেশি readable হলে সেটিই prefer করুন।


Searching Manually

Suppose learner name case-insensitively search করতে হবে।

contains() exact equality use করে।

Manual search:

public static boolean containsIgnoreCase(
        List<String> values,
        String expected
) {
    if (
            values == null
            || expected == null
    ) {
        return false;
    }

    for (
            String value
            : values
    ) {
        if (
                value != null
                && value.equalsIgnoreCase(
                        expected
                )
        ) {
            return true;
        }
    }

    return false;
}

Usage:

boolean found =
        containsIgnoreCase(
                learnerNames,
                "subu"
        );

Finding an Object by a Field

public static Course findById(
        List<Course> courses,
        long courseId
) {
    if (courses == null) {
        return null;
    }

    for (
            Course course
            : courses
    ) {
        if (
                course != null
                && course.getId()
                == courseId
        ) {
            return course;
        }
    }

    return null;
}

This works for small collections।

Later:

  • Map
  • Optional
  • Streams
  • Repository patterns

আরও expressive alternatives provide করবে।


Creating a List with List.of()

List<String> learnerNames =
        List.of(
                "Subu",
                "Sumu",
                "Nur"
        );

Benefits:

  • Concise
  • Fixed content
  • Cannot add, remove, set, or clear
  • Does not allow null

List.of() Is Immutable

This fails at runtime:

learnerNames.add(
        "Jalisa"
);

Throws:

UnsupportedOperationException

Also invalid:

learnerNames.set(
        0,
        "Sakib"
);

And:

learnerNames.remove(
        "Subu"
);

final Does Not Make a List Immutable

final List<String> names =
        new ArrayList<>();

This prevents reassigning the variable:

names =
        new ArrayList<>();

Invalid।

But list contents can still change:

names.add(
        "Subu"
);

Valid।

final reference immutability নয়।


Mutable Copy of List.of()

List<String> names =
        new ArrayList<>(
                List.of(
                        "Subu",
                        "Sumu"
                )
        );

Now:

names.add(
        "Nur"
);

works।

The new ArrayList copies the elements।


Creating an Immutable Copy

List<String> copy =
        List.copyOf(
                names
        );

copy structure modify করা যায় না।

copy.add(
        "Nur"
);

throws:

UnsupportedOperationException

List.copyOf() Rejects Null Elements

List<String> names =
        new ArrayList<>();

names.add(
        null
);

Then:

List.copyOf(
        names
);

throws:

NullPointerException

This can help enforce non-null list elements at boundaries।


Immutable List Does Not Make Its Elements Immutable

Suppose:

List<Course> courses =
        List.copyOf(
                mutableCourses
        );

The list structure cannot change।

But if Course objects are mutable:

courses.get(0)
        .changeTitle(
                "New Title"
        );

may still work।

List immutability means:

Cannot add/remove/replace elements

It does not automatically freeze each object।


Copying Is Usually Shallow

List.copyOf(
        courses
);

copies references, not complete independent object graphs।

Original list and copy may refer to the same Course objects।

Original list ─┐
               ├── Course object
Copied list ───┘

Mutating shared element affects what both lists observe।


ArrayList Basics

ArrayList is:

  • Ordered
  • Index-based
  • Duplicate-friendly
  • Dynamically resizable
  • Efficient for common append and read operations
  • Not synchronized by default

Beginner mental model:

ArrayList is usually the default mutable List implementation unless requirements suggest otherwise।

Do not choose it because of premature performance assumptions only।

Choose it because its behavior matches ordered mutable data।


Adding Many Elements

Use:

addAll(...)

Example:

List<String> firstGroup =
        new ArrayList<>();

firstGroup.add(
        "Subu"
);

List<String> secondGroup =
        List.of(
                "Sumu",
                "Nur"
        );

firstGroup.addAll(
        secondGroup
);

Result:

[Subu, Sumu, Nur]

Inserting Many Elements at an Index

firstGroup.addAll(
        1,
        List.of(
                "Jalisa",
                "Sakib"
        )
);

Elements insert হয় starting at index 1

Existing elements shift right।


Removing Multiple Elements

names.removeAll(
        List.of(
                "Subu",
                "Nur"
        )
);

Matching elements remove করে।

Another method:

names.retainAll(
        allowedNames
);

Only values also present in allowedNames remain।

These methods mutable lists require করে।


Comparing Lists

List.equals() generally compares:

  • Same size
  • Same order
  • Pairwise equal elements
List<String> first =
        List.of(
                "Subu",
                "Sumu"
        );

List<String> second =
        List.of(
                "Subu",
                "Sumu"
        );
first.equals(
        second
);

returns:

true

But:

List<String> third =
        List.of(
                "Sumu",
                "Subu"
        );
first.equals(
        third
);

returns:

false

Order matters।


Lists and Null

A mutable ArrayList allows null:

List<String> names =
        new ArrayList<>();

names.add(
        null
);

Later:

names.get(0)
        .toUpperCase();

throws:

NullPointerException

Practical rule:

Unless null has a deliberate meaning, do not put null elements in collections।

Prefer:

  • Empty list
  • Missing result handling
  • Valid object
  • Explicit optional state

Empty List Instead of Null

Weak:

public List<Course> findCourses() {
    return null;
}

Caller:

if (
        courses != null
) {
}

Better:

public List<Course> findCourses() {
    return List.of();
}

Caller can safely iterate:

for (
        Course course
        : courses
) {
}

Returning empty collection generally simplifies callers।


Exposing an Internal Mutable List

Weak domain class:

public final class Course {

    private final List<ContentItem> contentItems =
            new ArrayList<>();

    public List<ContentItem> getContentItems() {
        return contentItems;
    }
}

Caller can directly mutate internal state:

course.getContentItems()
        .clear();

This may bypass:

  • Publication rules
  • Duplicate checks
  • Maximum content count
  • Required ordering constraints
  • Null validation

Defensive Copying

Safer:

public List<ContentItem> getContentItems() {
    return List.copyOf(
            contentItems
    );
}

Caller gets immutable snapshot structure।

Cannot:

course.getContentItems()
        .clear();

Internal list remains controlled।


Copying Constructor Input

Weak:

public Course(
        List<ContentItem> contentItems
) {
    this.contentItems =
            contentItems;
}

Caller still owns same list reference।

List<ContentItem> items =
        new ArrayList<>();

Course course =
        new Course(
                items
        );

items.clear();

Course internal state also changes।


Defensive Input Copy

public Course(
        List<ContentItem> contentItems
) {
    if (contentItems == null) {
        throw new IllegalArgumentException(
                "Content items are required."
        );
    }

    this.contentItems =
            new ArrayList<>(
                    contentItems
            );
}

Now external list mutation course internal list structure affect করবে না।

For immutable internal structure:

this.contentItems =
        List.copyOf(
                contentItems
        );

Which choice is correct depends on whether course later add/remove needs।


Controlled Mutation

public final class Course {

    private final List<ContentItem> contentItems =
            new ArrayList<>();

    private boolean published;

    public boolean addContent(
            ContentItem content
    ) {
        if (content == null) {
            return false;
        }

        if (published) {
            return false;
        }

        if (
                containsContentId(
                        content.getId()
                )
        ) {
            return false;
        }

        contentItems.add(
                content
        );

        return true;
    }

    public boolean removeContent(
            long contentId
    ) {
        if (published) {
            return false;
        }

        for (
                int index = 0;
                index < contentItems.size();
                index++
        ) {
            ContentItem content =
                    contentItems.get(
                            index
                    );

            if (
                    content.getId()
                    == contentId
            ) {
                contentItems.remove(
                        index
                );

                return true;
            }
        }

        return false;
    }

    public List<ContentItem> getContentItems() {
        return List.copyOf(
                contentItems
        );
    }

    private boolean containsContentId(
            long contentId
    ) {
        for (
                ContentItem content
                : contentItems
        ) {
            if (
                    content.getId()
                    == contentId
            ) {
                return true;
            }
        }

        return false;
    }
}

Class list mutation নিজের methods-এর মাধ্যমে control করছে।


Removing During Enhanced for

Dangerous:

for (
        ContentItem content
        : contentItems
) {
    if (
            content.getId()
            == contentId
    ) {
        contentItems.remove(
                content
        );
    }
}

This can throw:

ConcurrentModificationException

কারণ list iteration চলাকালে structure directly modify হচ্ছে।

Safer approaches:

  • Index loop
  • Iterator
  • removeIf()

Iterator later in this module formally covered হবে।


Using an Index Loop for Removal

for (
        int index = 0;
        index < contentItems.size();
        index++
) {
    if (
            contentItems.get(
                    index
            ).getId()
            == contentId
    ) {
        contentItems.remove(
                index
        );

        break;
    }
}

After removal, indexes shift।

If removing multiple items, index handling carefully করতে হয়।


removeIf()

boolean removed =
        contentItems.removeIf(
                content ->
                        content.getId()
                        == contentId
        );

This uses a lambda expression।

Readable হলে useful।

Lambdas formally later শেখানো হবে।

For now, index-based version বুঝে রাখা important।


A Complete Example

Course.java

import java.util.ArrayList;
import java.util.List;

public final class Course {

    private final long id;
    private final String title;
    private final List<String> lessonTitles;

    private boolean published;

    public Course(
            long id,
            String title
    ) {
        if (id <= 0) {
            throw new IllegalArgumentException(
                    "Course ID must be positive."
            );
        }

        if (
                title == null
                || title.isBlank()
        ) {
            throw new IllegalArgumentException(
                    "Course title is required."
            );
        }

        this.id = id;
        this.title = title.strip();

        this.lessonTitles =
                new ArrayList<>();

        this.published = false;
    }

    public boolean addLesson(
            String lessonTitle
    ) {
        if (published) {
            return false;
        }

        if (
                lessonTitle == null
                || lessonTitle.isBlank()
        ) {
            return false;
        }

        String normalizedTitle =
                lessonTitle.strip();

        if (
                lessonTitles.contains(
                        normalizedTitle
                )
        ) {
            return false;
        }

        lessonTitles.add(
                normalizedTitle
        );

        return true;
    }

    public boolean renameLesson(
            int index,
            String newTitle
    ) {
        if (published) {
            return false;
        }

        if (
                index < 0
                || index >= lessonTitles.size()
        ) {
            return false;
        }

        if (
                newTitle == null
                || newTitle.isBlank()
        ) {
            return false;
        }

        String normalizedTitle =
                newTitle.strip();

        if (
                lessonTitles.contains(
                        normalizedTitle
                )
        ) {
            return false;
        }

        lessonTitles.set(
                index,
                normalizedTitle
        );

        return true;
    }

    public boolean removeLesson(
            String lessonTitle
    ) {
        if (published) {
            return false;
        }

        if (
                lessonTitle == null
                || lessonTitle.isBlank()
        ) {
            return false;
        }

        return lessonTitles.remove(
                lessonTitle.strip()
        );
    }

    public boolean publish() {
        if (published) {
            return false;
        }

        if (lessonTitles.isEmpty()) {
            return false;
        }

        published = true;

        return true;
    }

    public void printLessons() {
        for (
                int index = 0;
                index < lessonTitles.size();
                index++
        ) {
            System.out.println(
                    (index + 1)
                    + ". "
                    + lessonTitles.get(
                            index
                    )
            );
        }
    }

    public long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public boolean isPublished() {
        return published;
    }

    public int getLessonCount() {
        return lessonTitles.size();
    }

    public List<String> getLessonTitles() {
        return List.copyOf(
                lessonTitles
        );
    }
}

Main.java

public class Main {

    public static void main(
            String[] args
    ) {
        Course course =
                new Course(
                        1L,
                        "Java and OOP Foundation"
                );

        course.addLesson(
                "Introduction to Java"
        );

        course.addLesson(
                "Classes and Objects"
        );

        course.addLesson(
                "Inheritance"
        );

        course.renameLesson(
                2,
                "Inheritance and Polymorphism"
        );

        course.printLessons();

        System.out.println(
                "Lesson count: "
                + course.getLessonCount()
        );

        System.out.println(
                "Published: "
                + course.publish()
        );

        System.out.println(
                "Can add after publish: "
                + course.addLesson(
                        "Generics"
                )
        );

        System.out.println(
                "Read-only lessons: "
                + course.getLessonTitles()
        );
    }
}

Possible output:

1. Introduction to Java
2. Classes and Objects
3. Inheritance and Polymorphism
Lesson count: 3
Published: true
Can add after publish: false
Read-only lessons: [Introduction to Java, Classes and Objects, Inheritance and Polymorphism]

Design Review

Why Is the Internal List Mutable?

Course creation-এর সময় lessons add, rename এবং remove করতে হয়।

new ArrayList<>()

appropriate।


Why Does the Getter Return List.copyOf()?

Caller course-এর internal structure direct modify করতে পারবে না।


Why Does the Class Reject Duplicate Titles?

This is a course-specific rule।

List itself duplicates allow করে।

Domain class additional invariant enforce করেছে।


Why Is Publication Checked Before Mutation?

Published course-এর lesson structure immutable রাখা current domain rule।

List API নিজে এই business rule জানে না।


Why Use List Instead of Set?

Lesson order matters।

Course-এর first, second এবং third lesson sequence meaningful।

List ordered।

Duplicate prevention class manually enforce করছে।


Choosing List

Use List when:

  • Order matters
  • Index access useful
  • Duplicates may be valid
  • Sequence represents business meaning
  • Append and iteration common operations
  • Elements need stable positions

Examples:

Course lessons
Playlist items
Ordered workflow steps
Question sequence
Activity history

When List May Not Be Best

If primary requirement:

Unique values

consider Set

If primary requirement:

Lookup by unique key

consider Map

If priority queue behavior প্রয়োজন, another collection type may be appropriate।

Collection selection should follow data meaning and operations।


Common Mistakes

Using an Invalid Index

list.get(
        list.size()
);

Last valid index is:

list.size() - 1

Using set() to Append

set() replaces an existing element।

Use add() for new element।


Confusing remove(int) and remove(Object)

Especially with List<Integer>


Assuming final List Is Immutable

final only prevents reference reassignment।


Modifying List.of()

It throws UnsupportedOperationException


Returning an Internal Mutable List

Caller can bypass domain rules।


Storing the Constructor List Reference Directly

External caller can later mutate internal state।


Assuming Immutable List Makes Elements Immutable

Only list structure is protected।


Adding Null Without a Deliberate Policy

Later operations may fail unexpectedly।


Removing Inside Enhanced for

Can cause ConcurrentModificationException


Using List When Unique Lookup Is the Main Need

Repeated linear search may indicate Map or Set is more appropriate।


Practice Exercises

Exercise 1: Create a Mutable Learner List

Create:

List<String> learners

Then:

  • Add Subu
  • Add Sumu
  • Insert Nur at index 1
  • Replace the last element
  • Remove one learner
  • Print all values with indexes

Exercise 2: Handle Invalid Index

Write:

static String getLearnerOrDefault(
        List<String> learners,
        int index,
        String defaultValue
)

Return defaultValue when:

  • List is null
  • Index is negative
  • Index is outside the list

Exercise 3: Remove an Integer Value

Given:

List<Integer> scores =
        new ArrayList<>(
                List.of(
                        10,
                        20,
                        30
                )
        );

Remove value 20, not index 20


Exercise 4: Find a Course

Write:

static Course findCourseById(
        List<Course> courses,
        long courseId
)

Return matching course or null


Exercise 5: Defensive Copy

Create a class:

LearningPath

It owns:

List<Course>

Requirements:

  • Constructor input cannot be null
  • Internal list should not share caller’s mutable list structure
  • Getter should not expose internal mutable list

Exercise 6: Prevent Duplicate IDs

Create:

boolean addCourse(
        Course course
)

Reject:

  • Null course
  • Duplicate course ID

Order must be preserved।


Exercise 7: Immutable vs Mutable

Explain which creation is appropriate:

A

Fixed supported language codes:

EN
BN
ET

B

Course lessons while instructor is editing

C

Published course lesson snapshot

Choose among:

List.of(...)
new ArrayList<>()
List.copyOf(...)

Predict the Result

Question 1

List<String> names =
        new ArrayList<>();

names.add(
        "Subu"
);

names.add(
        "Sumu"
);

System.out.println(
        names.get(
                1
        )
);

Question 2

List<String> names =
        List.of(
                "Subu",
                "Sumu"
        );

names.add(
        "Nur"
);

What happens?


Question 3

List<Integer> scores =
        new ArrayList<>(
                List.of(
                        10,
                        20,
                        30
                )
        );

scores.remove(
        1
);

System.out.println(
        scores
);

Question 4

List<Integer> scores =
        new ArrayList<>(
                List.of(
                        10,
                        20,
                        30
                )
        );

scores.remove(
        Integer.valueOf(
                20
        )
);

System.out.println(
        scores
);

Question 5

List<String> names =
        new ArrayList<>();

System.out.println(
        names.get(
                0
        )
);

Question 6

List<String> original =
        new ArrayList<>();

original.add(
        "Subu"
);

List<String> copy =
        List.copyOf(
                original
        );

original.add(
        "Sumu"
);

System.out.println(
        copy
);

Predict the Result Answers

Answer 1

Sumu

Answer 2

Runtime-এ:

UnsupportedOperationException

List.of() immutable structure return করে।

Answer 3

[10, 30]

Index 1 remove হয়েছে।

Answer 4

[10, 30]

Value 20 remove হয়েছে।

Answer 5

Runtime-এ:

IndexOutOfBoundsException

List empty।

Answer 6

[Subu]

List.copyOf() list structure-এর snapshot copy তৈরি করেছে।

Later original list additions copy-তে আসে না।


Knowledge Check

Question 1

List-এর main characteristics কী?

Question 2

List index কোথা থেকে শুরু হয়?

Question 3

ArrayList কী?

Question 4

add() এবং set()-এর difference কী?

Question 5

get(size()) invalid কেন?

Question 6

contains() custom objects-এর ক্ষেত্রে কিসের ওপর depend করে?

Question 7

indexOf() value না পেলে কী return করে?

Question 8

List.of() mutable কি?

Question 9

List.copyOf() কী করে?

Question 10

final List কি immutable?

Question 11

List duplicate values allow করে কি?

Question 12

remove(1) on List<Integer> কী remove করে?

Question 13

Internal mutable list direct return করা risky কেন?

Question 14

Immutable list-এর elements কি automatically immutable?

Question 15

Empty collection return করা null-এর চেয়ে useful কেন?


Knowledge Check Answers

Answer 1

Ordered, index-based, duplicate-friendly generic collection।

Answer 2

0

Answer 3

List interface-এর common mutable implementation।

Answer 4

add() new element insert করে। set() existing index-এর element replace করে।

Answer 5

Valid last index হলো size() - 1

Answer 6

Element type-এর equals() implementation-এর ওপর।

Answer 7

-1

Answer 8

না।

Structural modification করলে UnsupportedOperationException হয়।

Answer 9

Given collection-এর immutable structural copy তৈরি করে এবং null elements reject করে।

Answer 10

না।

final reference reassignment prevent করে, list content mutation নয়।

Answer 11

হ্যাঁ।

Answer 12

Index 1-এর element।

Answer 13

Caller validation এবং domain rules bypass করে internal state mutate করতে পারে।

Answer 14

না।

Only list structure immutable।

Answer 15

Caller null check ছাড়া safely iterate এবং common collection operations ব্যবহার করতে পারে।


Lesson Summary

এই lesson-এ আমরা শিখেছি:

  • List ordered collection
  • Index 0 থেকে শুরু হয়
  • List duplicate elements allow করে
  • List একটি interface
  • ArrayList common mutable implementation
  • Variable type হিসেবে List use করা implementation coupling কমায়
  • add() new element যোগ করে
  • Indexed add() existing elements shift করে
  • get() index দিয়ে element পড়ে
  • set() existing element replace করে
  • remove() index বা value দিয়ে কাজ করতে পারে
  • List<Integer>-এ remove(int) overload carefully ব্যবহার করতে হয়
  • size() element count return করে
  • isEmpty() empty state communicate করে
  • contains() equality ব্যবহার করে
  • indexOf() first matching index বা -1 return করে
  • lastIndexOf() final matching index return করে
  • Enhanced for simple iteration-এর জন্য useful
  • Index loop position-sensitive processing-এর জন্য useful
  • Invalid index IndexOutOfBoundsException তৈরি করে
  • List.of() immutable fixed list তৈরি করে
  • List.copyOf() immutable structural copy তৈরি করে
  • final list reference listকে immutable করে না
  • Immutable list mutable elements freeze করে না
  • List copies usually shallow
  • ArrayList common ordered mutable data-এর reasonable default
  • Null elements deliberate policy ছাড়া avoid করা ভালো
  • Empty list null-এর চেয়ে safer return value
  • Internal mutable list direct expose করা উচিত নয়
  • Constructor inputs defensively copy করা যেতে পারে
  • Domain class controlled methods-এর মাধ্যমে list invariants enforce করতে পারে
  • Removing during enhanced for unsafe হতে পারে
  • List appropriate যখন order এবং sequence meaningful

Next Lesson

পরবর্তী lesson:

Working with Set

আমরা শিখব:

  • Unique collections
  • HashSet
  • Duplicate rejection
  • add() return value
  • contains() এবং removal
  • Iteration
  • Ordering guarantees
  • equals() এবং hashCode()
  • Mutable object keys/elements-এর risk
  • Duplicate enrollment এবং unique course code examples
  • List বনাম Set