ArrayList in Java is a dynamic array data structure implementation of the List interface, a part of the Java Collections Framework
ArrayList has the following features
Provides insertion-ordered iteration
Provides zero-based index operations (get, add, set, remove). Get, set operations run in constant-time performance. Add operation runs in amortized constant time. Other operations run in linear time
Permits null and duplicated elements
Elements are compared base on their equals method implementation
ArrayList is not thread-safe as it is an unsynchronized implementation. In multi-threading environment with at least one thread modifies the list, it must be synchronized externally
ArrayList capacity may count on the performance when you need to add a large number of items than the default capacity (10)
Let's walk through this tutorial to explore them in more detail examples
The class hierarchy
ArrayList
implements the List
interface which extends from Collection
and Iterable
interfaces hierarchically
ArrayList vs Array and LinkedList
Array is a fixed size while ArrayList is a resizable array data structure implementation. Array provides run-time type safety while ArrayList provides compile time type safety. Learn more
LinkedList is a doubly linked list data structure implementation, more flexible than ArrayList as LinkedList can also work as a stack or queue
In term of efficiency, LinkedList consumes a bit more memory because it has to allocate a node object for each element in the list
Declare an ArrayList
As a result from the class hierarchy, you can declare and create an ArrayList with the following ways
@Test
public void declareAndCreate() {
List<Integer> lst1 = new ArrayList<>();
assertThat(lst1).isInstanceOf(ArrayList.class);
ArrayList<Integer> lst2 = new ArrayList<>();
}
Create and Initialize
- Provide either Set.of or List.of factory method, since Java 9, or Arrays.asList factory method to the ArrayList(Collection) constructor to create and init an ArrayList in one line at the creation time
@Test
public void initWithListOfAndSetOf() {
List<Integer> arrayList1 = new ArrayList<>(List.of(3, 1, 2));
assertThat(arrayList1).contains(3, 1, 2);
List<Integer> arrayList2 = new ArrayList<>(Set.of(5, 4, 6));
assertThat(arrayList2).contains(5, 4, 6);
}
- You can also use add or addAll method to initialize an ArrayList after the creation time
@Test
public void initWithAdd() {
// Create a new ArrayList
List<Integer> lst = new ArrayList<>();
// Add elements to ArrayList
lst.add(3);
lst.add(1);
lst.add(2);
// Can add null element
lst.add(null);
// Can add duplicate element
lst.add(2);
assertThat(lst).hasSize(5);
// The output ordering will be same as the insertion oder
System.out.println(lst);
}
- You may like to initialize an ArrayList with a capacity when going to add a large number of items to it
@Test
public void initWithCapacity() {
List<Integer> arrayList = new ArrayList<>(1000);
for (int i = 0; i < 1000; i++) {
arrayList.add(i);
}
assertThat(arrayList).hasSize(1000);
}
Iterate over an ArrayList
- You can iterate over an ArrayList by using either forEach(Consumer), since Java 8, or for-each and other index-loops (while, do-while, for-index)
@Test
public void forEachConsumer() {
List<Integer> lst = new ArrayList<>(List.of(1, 2, 3));
lst.forEach(e -> System.out.printf("%d ", e));
}
- Iterator and ListIterator can also be used to iterate
@Test
public void iterator() {
List<Integer> lst = new ArrayList<>(List.of(1, 2, 3));
Iterator<Integer> iterator = lst.iterator();
while (iterator.hasNext()){
System.out.printf("%d ", iterator.next());
}
}
Retrieve and Filter
- Retrieve an ArrayList element by using get(index)
@Test
public void get() {
List<Integer> lst = new ArrayList<>(List.of(1, 2, 3));
int firstElement = lst.get(0);
assertThat(firstElement).isEqualTo(1);
}
Since Java 8, you can use Stream API to filter and retrieve elements from an ArrayList
The following example uses Stream API to get the first element with findFirst() and filter elements with filter(Predicate)
@Test
public void filterAndRetrieve() {
List<Integer> lst = new ArrayList<>(List.of(1, 2, 3));
Integer firstElement = lst.stream().findFirst().orElse(null);
assertThat(firstElement).isEqualTo(1);
Integer[] arr = lst.stream().filter(e -> e >= 2).toArray(Integer[]::new);
assertThat(arr).contains(2, 3);
}
Examine, Add, Update and Remove
- You can use
contains
method to examine if an element existing in an ArrayList,add
methods to append and insert elements into the list,set
method to update / replace element andremove
methods to remove element from the list
@Test
public void containsAddUpdateRemoveSingleElement() {
List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3));
Boolean contained = arrayList.contains(3);
assertThat(contained).isTrue();
arrayList.add(4); // appends an element
arrayList.add(0, 10); // inserts at index 0
arrayList.add(null);
assertThat(arrayList).containsExactly(10, 1, 2, 3, 4, null);
arrayList.set(0, 100); // update at index 0
assertThat(arrayList).containsExactly(100, 1, 2, 3, 4, null);
arrayList.remove(1); // removes at index 1
assertThat(arrayList).containsExactly(100, 2, 3, 4, null);
arrayList.remove(Integer.valueOf(2)); // removes an element
assertThat(arrayList).containsExactly(100, 3, 4, null);
}
- ArrayLists also support the above operations in bulk with containsAll, addAll, Java 8+ replaceAll, removeAll, Java 8+ removeIf
@Test
public void containsAddUpdateRemoveMultipleElements() {
List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3));
Boolean contained = arrayList.containsAll(List.of(1, 2));
assertThat(contained).isTrue();
arrayList.addAll(List.of(4, 5));
arrayList.addAll(0, List.of(10, 100));
assertThat(arrayList).containsExactly(10, 100, 1, 2, 3, 4, 5);
arrayList.replaceAll(e -> ++e);
assertThat(arrayList).containsExactly(11, 101, 2, 3, 4, 5, 6);
arrayList.removeAll(List.of(1, 11, 101));
assertThat(arrayList).containsExactly(2, 3, 4, 5, 6);
arrayList.removeIf(e -> e >= 3);
assertThat(arrayList).contains(2);
}
Objects comparing in ArrayList
Internally, operations such as contains(Object) and remove(Object) compare objects based on their equals method implementation
In the following example,
list.contains(new Book(1, "a"))
should return true but false,list.remove(new Book(1, "a"))
should success but not due to lack of equals implementation on the user defined class
@Test
public void objectsComparingProblem(){
List<Book> list = new ArrayList<>();
list.add(new Book(1, "a"));
Boolean contained = list.contains(new Book(1, "a"));
assertThat(contained).isFalse();
list.remove(new Book(1, "a"));
assertThat(list).hasSize(1);
}
static class Book {
int id;
String title;
Book(int id, String title) {
this.id = id;
this.title = title;
}
int getId() {
return id;
}
String getTitle() {
return title;
}
}
- You can fix the above problem by implement the equals method to the custom class as the below example
@Test
public void objectsComparingFixed(){
List<BookFixed> list = new ArrayList<>();
list.add(new BookFixed(1, "a"));
Boolean contained = list.contains(new BookFixed(1, "a"));
assertThat(contained).isTrue();
list.remove(new BookFixed(1, "a"));
assertThat(list).hasSize(0);
}
static class BookFixed {
int id;
String title;
BookFixed(int id, String title) {
this.id = id;
this.title = title;
}
int getId() {
return id;
}
String getTitle() {
return title;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BookFixed bookFixed = (BookFixed) o;
return id == bookFixed.id &&
Objects.equals(title, bookFixed.title);
}
}
In practice, consider to override hashCode when overriding equals as your defined classes may be used elsewhere in a hash table based collections such as HashSet and HashMap
Sort an ArrayList
Sort an ArrayList of objects by using its instance's sort(Comparator)
method in conjunction with Comparable and Comparator interfaces to control over the sort order
@Test
public void sortMultipleFields() {
Book book1 = new Book(1, "b");
Book book2 = new Book(2, "c");
Book book3 = new Book(3, "c");
List<Book> list = Arrays.asList(book1, book2, book3);
list.sort(Comparator
.comparing(Book::getTitle, Comparator.reverseOrder())
.thenComparing(Book::getId, Comparator.reverseOrder()));
assertThat(list).containsExactly(book3, book2, book1);
}
Conclusion
In this tutorial, we learned about ArrayList class hierarchy, features and operations. You can find below the full source code