Collection Framework - ArrayList

Here is a overview diagram on what Collection Framework contains.

updated diagram

Difference between built-in array and ArrayList is, the size of ArrayList can be modified dynamically, whereas built-in array has fixed size.

When to use ArrayList over LinkedList:

When we have more get/set operations at any place in the list.

Syntax:

ArrayList<Integer> list = new ArrayList<>(List.of(5, 1, 3, 10, 9));
list.add(100);
list.get(3);
list.set(3, 100);
list.sort(Comparator.naturalOrder());

Constructor Summary

ConstructorsDescription
ArrayList()Constructs an empty list with an initial capacity of ten.
ArrayList(Collection<? extends E> c)Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator.
ArrayList(int initialCapacity)Constructs an empty list with the specified initial capacity.

Method Summary:

MethodComplexity
add(E e)O(1) Worst Case is O(n) for resize, but amortized cost O(1)
add(int index, E element)O(n)
addFirst(E e)O(n)
addLast(E e)O(1) , amortized cost
remove(int index)O(n)
remove(Object o)O(n)
removeFirst()O(n)
removeLast()O(1)
get(int index)O(1)
getFirst()O(1)
getLast()O(1)
set(int index, E element)O(1)
contains(Object o)O(n)
size()O(1)
toArray(T[] a)O(n)
indexOf(Object o)O(n)
lastIndexOf(Object o)O(n)
sort(Comparator<? super E> c)O(nlogn), typically a modified merge sort
forEach(Consumer<? super E> action)-

Initialization:

ArrayList<String> list = new ArrayList<>();
ArrayList<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));

List<String> anotherList = Arrays.asList("A", "B", "C");
ArrayList<String> list = new ArrayList<>(anotherList);

String[] array = {"A", "B", "C"};
List<String> list = Arrays.asList(array);
ArrayList<String> arrayList = new ArrayList<>(list); 

Operations:

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

list.add("first string");
list.add("first string");
list.add("third string");
list.add("fourth string");
list.add("fifth string");
System.out.println(list); //[first string, first string, third string, fourth string, fifth string]

list.set(1, "second string");
System.out.println(list); // [first string, second string, third string, fourth string, fifth string]

System.out.println(list.getFirst()); //first string
System.out.println(list.get(1)); //second string
System.out.println(list.getLast()); //fourth string

int size = list.size();
System.out.println(size); // 5

System.out.println(list.contains("first string")); // true
System.out.println(list.indexOf("first string")); // 0
System.out.println(list.lastIndexOf("first string")); // 0

list.remove(1);
list.removeFirst();
list.removeLast();

System.out.println(list.size()); // 2

// Additional Operation
String[] arr = list.toArray(new String[0]);
for (String s : arr) {
    System.out.println(s); // third string
}

list.add(1, "Add in specific position");
System.out.println(list); //[third string, Add in specific position, fourth string]

list.addAll(List.of("add all 1", "all all 2"));
System.out.println(list);

Sorting:

ArrayList<Integer> list = new ArrayList<>(List.of(20, 10, 50, 30, 100, 90, 500, 150));
// Sort using List interface
list.sort(Comparator.reverseOrder());
list.sort(Comparator.naturalOrder());

// Sort Collections utility class
Collections.sort(list);
Collections.sort(list, Collections.reverseOrder());

// ********** Custom Comparator *************

// Comparator: Anonymous inner class approach
Comparator<Integer> customComparator = new Comparator<>() {
    @Override
    public int compare(Integer a, Integer b) {
        return b.compareTo(a); // Change the order of a and b for descending
    }
};
Collections.sort(list, customComparator);


// Comparator: Using lambda expression
Comparator<Integer> customComparator2 = (a, b) -> a.compareTo(b);
list.sort(customComparator2);

Question: When I make a call, list.contains(Object) or list.indexOf(Object) or list.lastIndexOf(Object), how does java compare the object in case of String or other types ?

Answer:
The methods internally rely on the equals(Object obj) method of the elements to determine if they match the given object.

  1. String overrides the equals method to compare the actual sequence of characters in two String objects.
  2. For custom objects, if you haven't overridden equals method, it will inherit the default equals method from Object, which is simply compares memory addresses.
class Person {
    String name;
    Person(String name) { this.name = name; }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return name.equals(person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

List<Person> people = Arrays.asList(new Person("Alice"), new Person("Bob"));
boolean contains = people.contains(new Person("Alice")); // Uses Person.equals(), returns true