Java Arrays and Collections: List, Set, Map, Deque and Sequenced Collections (1Z0-831)
Java arrays and the Collections Framework are the two ways a program stores groups of values: an array is a fixed-length, indexed block of elements, while List, Set, Map and Deque are resizable interfaces from java.util with distinct rules for order, duplicates and lookup. Oracle 1Z0-831 objective 5.1 asks you to create each of them — plus the sequenced collections added in Java 21 — and to add, remove, update, retrieve and sort their elements. This lesson covers array creation and the Arrays utility methods, the difference between modifiable, fixed-size and unmodifiable lists, list, set and map operations and their return values, sorting with Comparable and Comparator, TreeSet and TreeMap navigation, Deque stack and queue methods, and the first/last/reversed() methods of sequenced collections — with the exceptions each one can throw.
On this page10 sections
- Creating arrays: size, initializers, defaults and length
- Copying, comparing, sorting and searching arrays
- Four kinds of list: ArrayList, Arrays.asList, List.of and unmodifiable views
- Adding, updating, retrieving and removing list elements
- Sorting lists with Comparable and Comparator
- Sets: HashSet, LinkedHashSet and TreeSet
- Maps: put, get, remove, merge and compute
- TreeMap navigation and range views
- Deque as a stack and as a queue
- Sequenced collections: first, last and reversed()
- Create one- and multidimensional arrays, predict default values, and copy, compare, sort and search them with java.util.Arrays.
- Distinguish ArrayList, Arrays.asList, List.of/List.copyOf and Collections.unmodifiableList by what they allow, whether they accept null and whether they follow their source.
- Trace List, Set and Map operations by their return values, including remove(int) versus remove(Object), put, putIfAbsent, getOrDefault, merge and compute.
- Sort collections with natural ordering and chained Comparators, and explain how equals/hashCode and compareTo decide duplicates in HashSet and TreeSet.
- Use NavigableSet and TreeMap lookups and range views, and Deque methods as a stack and as a queue, including which methods throw and which return null.
- Apply the SequencedCollection, SequencedSet and SequencedMap methods, including live reversed() views and the cases that throw UnsupportedOperationException.
Creating arrays: size, initializers, defaults and length
An array is a fixed-length, indexed container created either with new and a size or with an initializer list — never both. int[] a = new int[3]; and int[] b = {1, 2, 3}; compile; new int[3] {1, 2, 3} does not, and the bare-brace form {1, 2, 3} is only allowed in a declaration (elsewhere write new int[] {1, 2, 3}).
Elements of a new array always start at their default value, even inside a method where local variables have none:
| Element type | Default |
|---|---|
byte short int long | 0 (0L) |
float double | 0.0 |
char | '\u0000' |
boolean | false |
Any reference type (String, Integer, arrays) | null |
length is a field (no parentheses) holding the element count, so valid indexes run from 0 to length - 1. The last element is a[a.length - 1]; a[a.length] compiles but throws ArrayIndexOutOfBoundsException at runtime.
Where the brackets go
The brackets of a declaration may sit on the type, on the variable name, or be split between them, and every pair counts. int[] a and int a[] both declare an int[]; int[][] g, int g[][] and int[] g[] all declare an int[][]. In a multi-variable declaration the type-side brackets apply to every name, but name-side brackets only to their own name: int[] p, q[]; makes p an int[] and q an int[][].
Multidimensional and ragged arrays
Java has arrays of arrays. new int[3][4] builds an outer array of length 3 whose every row has length 4. new int[3][] builds only the outer array: its three rows are null until you assign them, and rows may have different lengths (a ragged array). Only the first dimension is required: new int[3][] compiles, but new int[][2] does not (the compiler reports ']' expected), because a later dimension cannot be fixed while an earlier one is unknown. Reading grid[2].length before row 2 is assigned throws NullPointerException, not ArrayIndexOutOfBoundsException. Assigning one row into another slot (grid[1] = grid[0];) makes both slots refer to the same row object — writes through one are seen through the other.
Copying, comparing, sorting and searching arrays
An array variable holds a reference, so assignment shares the array while clone() or Arrays.copyOf creates a new one. Arrays do not override equals, hashCode or toString: a.equals(b) is identity (same as ==), and printing an array shows a type code and hash such as [I@1b6d3586. Use the java.util.Arrays helpers for contents.
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] a = {5, 1, 4};
int[] alias = a;
int[] copy = a.clone();
alias[0] = 9;
System.out.println(a[0] + " " + copy[0]);
System.out.println(a.equals(copy) + " "
+ Arrays.equals(new int[] {1}, new int[] {1}));
Arrays.sort(a);
System.out.println(Arrays.toString(a) + " "
+ Arrays.binarySearch(a, 4) + " "
+ Arrays.binarySearch(a, 6));
int[][] grid = new int[3][];
grid[0] = new int[2];
System.out.println(grid.length + " " + grid[0].length
+ " " + grid[1]);
}
}Output (run on JDK 25):
9 5
false true
[1, 4, 9] 1 -3
3 2 null| Method | What it does |
|---|---|
Arrays.toString(a) / deepToString(a) | Element-by-element text; deepToString for nested arrays |
Arrays.equals(a, b) / deepEquals | true when same length and equal elements in order |
Arrays.copyOf(a, n) / copyOfRange(a, from, to) | New array; pads with defaults when n is longer; range is half-open |
Arrays.sort(a) / sort(a, from, to) | Sorts in place (returns void); natural order, or a Comparator for object arrays; range is from inclusive, to exclusive |
Arrays.binarySearch(a, key) | Index of the key in a sorted array; if absent, -(insertion point) - 1; result undefined on an unsorted array |
Arrays.mismatch(a, b) | First index where they differ; -1 if identical; the shorter length when one is a proper prefix of the other |
Arrays.compare(a, b) | Lexicographic order sign: negative, zero or positive; a proper prefix is smaller |
In the output above, 6 is missing from [1, 4, 9]; its insertion point (the index of the first element greater than 6) is 2, so binarySearch returns -(2) - 1 = -3.
clone() is shallow. Cloning a 2D array copies only the outer array; both copies share the same row arrays, so copy[0][0] = 7 changes the original too, while copy[0] = new int[2] does not.
Strings sort and compare by UTF-16 code unit, which is case-sensitive: digits come before uppercase letters, which come before lowercase letters. That applies to Arrays.sort, Arrays.compare on String[], TreeSet<String> and List.sort(null) alike.
Four kinds of list: ArrayList, Arrays.asList, List.of and unmodifiable views
Lists that look the same differ in whether you can change their elements, their size, and whether they follow another object. This table is the core of many code-reading items:
| Created with | set | add/remove | Nulls | Linked to a source? |
|---|---|---|---|---|
new ArrayList<>(...) | Yes | Yes | Allowed | No — an independent copy |
Arrays.asList(arr) | Yes | No — UnsupportedOperationException (fixed size) | Allowed | Yes — writes through to the array both ways |
List.of(...) / List.copyOf(c) | No — UnsupportedOperationException | No — UnsupportedOperationException | Rejected — NullPointerException, even from contains(null) | No — a snapshot |
Collections.unmodifiableList(l) | No | No | Whatever the source allows | Yes — a read-only view that shows later changes to l |
import java.util.*;
public class Main {
public static void main(String[] args) {
String[] arr = {"m", "k", "p"};
List<String> view = Arrays.asList(arr);
view.set(2, "a");
arr[0] = "t";
System.out.println(view + " " + Arrays.toString(arr));
Collections.sort(view);
System.out.println(Arrays.toString(arr));
try {
view.remove(0);
} catch (UnsupportedOperationException e) {
System.out.println("asList remove: UOE");
}
List<Integer> src = new ArrayList<>(List.of(7, 8));
List<Integer> live = Collections.unmodifiableList(src);
List<Integer> frozen = List.copyOf(src);
src.remove(0);
System.out.println(live + " " + frozen);
try {
List.of(3, 1, 2).sort(null);
} catch (UnsupportedOperationException e) {
System.out.println("List.of sort: UOE");
}
}
}Output (run on JDK 25):
[t, k, a] [t, k, a]
[a, k, t]
asList remove: UOE
[8] [7, 8]
List.of sort: UOESorting and sub-lists
Sorting rearranges elements without changing the size, so it follows the set column of the table: Collections.sort(view) or view.sort(null) on an Arrays.asList list succeeds and reorders the backing array too, while sort on a List.of or List.copyOf list throws UnsupportedOperationException (as does sorting an unmodifiable view). subList(from, to) returns a half-open view of the list, not a copy: creating it never changes anything and works on any list, even an unmodifiable one, but later changes through it reach the original (list.subList(0, 2).clear() removes the first two elements) and follow the original's own rules, so a mutator through a sub-list of List.of(...) throws UnsupportedOperationException.
Two traps around Arrays.asList: passing a primitive array (int[]) produces a one-element List<int[]>, because generics cannot hold primitives; and wrapping it in new ArrayList<>(Arrays.asList(arr)) makes a resizable copy that no longer writes through to arr.
Adding, updating, retrieving and removing list elements
A List is an ordered, zero-indexed collection that allows duplicates. The methods behave as follows:
add(e)appends and returnstrue;add(i, e)inserts atiand shifts later elements right.imay equalsize()(append) but not exceed it.set(i, e)replaces and returns the element it replaced;get(i)reads. Both require0 <= i < size(), otherwiseIndexOutOfBoundsException.indexOf(o)/lastIndexOf(o)return the first / last matching index or-1;contains(o)usesequals.remove(int index)removes by position and returns the element;remove(Object o)removes the first equal element and returns aboolean—false, with no exception, when nothing matches.removeAll(c)removes every element that isequalsto some element ofc, andretainAll(c)keeps only those. Matching is whole-element equality, not a prefix or pattern:removeAll(List.of("x"))removes"x"but leaves"x1". UseremoveIf(predicate)for any other condition.
With a List<Integer>, list.remove(1) picks the exact-match remove(int) overload and removes index 1; to remove the value 1, pass an object: remove(Integer.valueOf(1)). If the variable's declared type is Collection<Integer>, only remove(Object) exists, so the same call autoboxes and removes the value.
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>(List.of(5, 15, 25, 35));
nums.remove(2);
nums.remove(Integer.valueOf(5));
System.out.println(nums);
List<String> s = new ArrayList<>(List.of("red", "blue"));
s.add(0, "cyan");
String old = s.set(1, "pink");
System.out.println(old + " " + s + " " + s.indexOf("blue"));
s.removeIf(x -> x.startsWith("c"));
System.out.println(s + " " + s.remove("gold"));
}
}Output (run on JDK 25):
[15, 35]
red [cyan, pink, blue] 2
[pink, blue] falseRemoving while iterating
Changing a list's size during a for-each loop (other than through the iterator) makes the next next() call throw ConcurrentModificationException. One well-known exception: removing the second-to-last element shrinks the list so that hasNext() returns false, the loop ends quietly, and the last element is never visited. Safe ways to remove are list.removeIf(predicate) and an explicit Iterator with it.remove(). A forward index loop that calls remove(i) does not throw, but it skips the element that slides into position i.
Sorting lists with Comparable and Comparator
A list sorts either by its elements' natural order (Comparable.compareTo) or by a Comparator. list.sort(null) and Collections.sort(list) use natural order — String, the wrapper types and the date/time types are all Comparable; a class that isn't makes Collections.sort(list) fail to compile and list.sort(null) throw ClassCastException.
Comparators are built by chaining:
Comparator.comparing(Book::title),comparingInt(Book::pages)— sort by a key..thenComparing(...)— consulted only when the earlier comparison is a tie..reversed()— reverses the whole chain built so far, i.e. everything to its left, not what you add after it.Comparator.naturalOrder()/reverseOrder(), andCollections.reverseOrder().
So with a Book record, comparing(Book::title).reversed().thenComparingInt(Book::pages) orders titles Z to A and then, among books with the same title, the shorter book first; moving .reversed() to the end of the chain would reverse both keys, putting the longer book first as well. A comparator returns a negative number, zero or a positive number; zero means "equal for ordering", which matters greatly for sorted sets and maps.
Sets: HashSet, LinkedHashSet and TreeSet
A Set holds no duplicate elements: add returns false and leaves the set unchanged when an equal element is already present, and remove returns whether anything was removed. What "equal" means depends on the implementation:
| Set | Order | Duplicate test | Nulls |
|---|---|---|---|
HashSet | None guaranteed | hashCode, then equals(Object) | One allowed |
LinkedHashSet | Insertion order (a SequencedSet) | hashCode + equals | One allowed |
TreeSet | Sorted (a NavigableSet) | compareTo/compare returning 0 | Rejected with natural ordering |
equals and hashCode
Hash-based collections find an element by its hash bucket first and then by equals(Object), so a class must override both consistently. Overriding only equals (or only hashCode) lets two "equal" objects both be added. A method written as equals(Point p) is an overload, not an override, and collections never call it. Records generate equals, hashCode and toString from their components, so equal records collapse to one element. Mutating a field used by hashCode while the object sits in a HashSet or as a HashMap key strands it in the wrong bucket: contains and remove then fail to find it.
TreeSet uses comparison, not equals
A TreeSet needs Comparable elements or a Comparator; adding a non-comparable element throws ClassCastException — even the very first add, because the set compares it with itself. Uniqueness comes from the comparison: elements the comparator calls equal (result 0) are duplicates even if equals disagrees. The same comparison is used for every lookup, not only for add: contains and remove find an element when the comparator returns 0, whatever its spelling. The element added first is the one kept, and a later "equal" element is simply rejected (add returns false), so the stored spelling never changes. A length comparator therefore keeps only one word per length, and a case-insensitive set keeps whichever capitalisation arrived first:
import java.util.*;
public class Main {
public static void main(String[] args) {
TreeSet<Integer> t = new TreeSet<>(List.of(5, 15, 25, 35));
System.out.println(t.ceiling(16) + " " + t.higher(25) + " "
+ t.floor(34) + " " + t.lower(5));
System.out.println(t.headSet(25) + " " + t.tailSet(15)
+ " " + t.subSet(5, 25) + " " + t.headSet(25, true));
Set<String> byLen =
new TreeSet<>(Comparator.comparing(String::length));
byLen.add("apple");
byLen.add("kiwi");
byLen.add("melon");
System.out.println(byLen);
Set<String> ci = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
ci.add("Tea");
System.out.println(ci.add("TEA") + " " + ci.contains("tea")
+ " " + ci);
System.out.println(new TreeSet<>(List.of(9, 4))
.equals(new LinkedHashSet<>(List.of(4, 9))));
}
}Output (run on JDK 25):
25 35 25 null
[5, 15] [15, 25, 35] [5, 15] [5, 15, 25]
[kiwi, apple]
false true [Tea]
trueThe last line shows that Set.equals compares elements only: two sets are equal when they have the same size and each contains every element of the other, regardless of implementation or iteration order. A TreeSet, a LinkedHashSet, a HashSet and a Set.of(...) holding the same elements are all equal to each other (the same is true of List.equals across list types, but lists also compare order).
NavigableSet lookups and ranges
ceiling(e) is the smallest element >= e and floor(e) the largest <= e; higher and lower are the strict versions. They return null when nothing qualifies. headSet(to) excludes to, tailSet(from) includes from, and subSet(from, to) is half-open — the boolean overloads (headSet(to, true)) set inclusivity explicitly. These ranges are live views of the set. pollFirst()/pollLast() remove and return the ends (or null when empty), and descendingSet() is a reverse view.
Maps: put, get, remove, merge and compute
A Map stores key-to-value mappings with unique keys; putting an existing key replaces its value. The return values are what code-reading questions trace:
put(k, v)returns the previous value, ornullif there was none.get(k)returnsnullfor a missing key (and for a key mapped tonull— usecontainsKeyto tell them apart).remove(k)returns the removed value;remove(k, v)removes only if currently mapped tovand returns aboolean.getOrDefault(k, d)returnsdonly when the key is absent; a key mapped tonullreturnsnull.putIfAbsent(k, v)storesvwhen the key is absent or mapped to null, and returns the old value.merge(k, v, fn)putsvif the key is absent (or null), otherwise storesfn(old, v).compute(k, fn),computeIfAbsent(k, fn)andcomputeIfPresent(k, fn)store the function's result.
For merge and all three compute methods, a function result of null removes the mapping (or adds nothing). computeIfAbsent(k, x -> new ArrayList<>()).add(v) is the standard idiom for grouping values under one list per key, because it returns the existing or newly stored value.
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, Integer> m = new HashMap<>();
System.out.print(m.put("a", 1) + " ");
System.out.print(m.put("a", 2) + " ");
m.merge("a", 5, Integer::sum);
m.merge("b", 5, Integer::sum);
m.computeIfPresent("b", (k, v) -> null);
m.computeIfAbsent("c", k -> 3);
System.out.println(new TreeMap<>(m));
Map<Integer, List<String>> byLen = new TreeMap<>();
for (String w : List.of("sun", "moon", "sky", "star")) {
byLen.computeIfAbsent(w.length(),
k -> new ArrayList<>()).add(w);
}
System.out.println(byLen);
Map<String, String> n = new HashMap<>();
n.put("q", null);
System.out.println(n.get("q") + " " + n.containsKey("q")
+ " " + n.putIfAbsent("q", "z") + " " + n.get("q"));
}
}Output (run on JDK 25):
null 1 {a=7, c=3}
{3=[sun, sky], 4=[moon, star]}
null true null zUnmodifiable map factories
Map.of(k1, v1, …), Map.ofEntries(Map.entry(k, v), …) and Map.copyOf(m) create unmodifiable maps: every mutator (put, remove, merge…) throws UnsupportedOperationException, a null key or value throws NullPointerException (also from Map.entry), and a duplicate key in Map.of/ofEntries throws IllegalArgumentException. Their iteration order is unspecified. HashMap and LinkedHashMap allow one null key and null values; TreeMap with natural ordering rejects a null key.
Iterating and updating through views
entrySet(), keySet() and values() are live views of the map. Iterate entrySet() to read key and value together; entry.setValue(v) updates the map, and removing through a view (values().remove(x), keySet().removeIf(...)) deletes the whole entry. Views cannot add: keySet().add(k) throws UnsupportedOperationException, since there is no value. Reassigning the loop variable (v = v * 2 in for (Integer v : m.values())) changes nothing in the map. The same holds for a lambda: in m.forEach((k, v) -> v = v * 10) the assignment changes only the lambda's own parameter, and the map is unchanged. values() is declared as a plain Collection<V>, which has no replaceAll method (that belongs to List and Map), so m.values().replaceAll(v -> v * 2) does not compile (cannot find symbol). To transform every value, call replaceAll((k, v) -> ...) on the map itself, or setValue on each entry.
Deque as a stack and as a queue
A Deque (double-ended queue) allows insertion and removal at both ends; ArrayDeque is the usual implementation and LinkedList also implements it. Used as a stack, push, pop and peek all work at the head; used as a queue, offer/add insert at the tail while poll/remove/peek work at the head. In short: push = addFirst, offer = offerLast, pop = removeFirst, poll = pollFirst.
| Operation | Throws on failure | Returns special value on failure |
|---|---|---|
| Insert at head | addFirst(e), push(e) | offerFirst(e) (returns false if a capacity-bounded deque is full) |
| Insert at tail | addLast(e), add(e) | offerLast(e), offer(e) |
| Remove head | removeFirst(), remove(), pop() | pollFirst(), poll() → null |
| Remove tail | removeLast() | pollLast() → null |
| Examine head | getFirst(), element() | peekFirst(), peek() → null |
| Examine tail | getLast() | peekLast() → null |
"Failure" means different things for the two halves of the table. An insert fails only when a capacity-bounded deque is full: addFirst/push/add then throw IllegalStateException while offer… return false. ArrayDeque and LinkedList are unbounded, so their inserts never fail, and an empty deque never makes an insert throw. A remove or examine fails when the deque is empty: the throwing methods throw NoSuchElementException and the others return null. Do not confuse the no-argument remove() (removes the head, throws when empty) with remove(Object o), inherited from Collection, which removes the first equal element and returns false when there is none — it never throws for a missing element.
ArrayDeque rejects null elements with NullPointerException from every insert method, because null is the "empty" signal of poll/peek; LinkedList accepts null. A deque's toString lists elements from head to tail.
import java.util.*;
public class Main {
public static void main(String[] args) {
Deque<String> d = new ArrayDeque<>();
d.offerFirst("m");
d.add("n");
d.push("o");
d.addLast("p");
System.out.println(d + " " + d.peekFirst() + " "
+ d.peekLast());
System.out.println(d.pollLast() + " " + d.remove() + " " + d);
System.out.println(d.remove("zz") + " " + d);
d.clear();
System.out.println(d.poll() + " " + d.peek());
try {
d.remove();
} catch (NoSuchElementException e) {
System.out.println("remove() on empty: NoSuchElement");
}
}
}Output (run on JDK 25):
[o, m, n, p] o p
p o [m, n]
false [m, n]
null null
remove() on empty: NoSuchElementSequenced collections: first, last and reversed()
Sequenced collections (Java 21+) are the interfaces SequencedCollection, SequencedSet and SequencedMap, which give every collection with a defined encounter order the same end-access methods. List, Deque, LinkedHashSet, SortedSet/TreeSet, LinkedHashMap and SortedMap/TreeMap all implement them; HashSet and HashMap do not. Because these are interface methods, they are only callable when the variable's declared type is sequenced: Set<String> s = new LinkedHashSet<>(); s.addFirst("x"); does not compile.
SequencedCollection:addFirst,addLast,getFirst,getLast,removeFirst,removeLast,reversed(). On an empty collection,get/removeFirst/Last throwNoSuchElementException(a bad index on a list throwsIndexOutOfBoundsExceptioninstead).SequencedSet(LinkedHashSet): a plainaddof an existing element keeps its position, butaddFirst/addLastmove an existing element to that end.SequencedMap:putFirst,putLast(moving an existing key in aLinkedHashMap),firstEntry,lastEntry,pollFirstEntry,pollLastEntry,sequencedKeySet(),sequencedValues(),sequencedEntrySet(),reversed(). The entries fromfirstEntry/lastEntry/poll…Entryare unmodifiable snapshots —setValuethrowsUnsupportedOperationException.
reversed() is a live view
reversed() returns a reverse-ordered view that reads and writes through to the original: the view's first element is the original's last, so view.addFirst(x) appends to the original and view.removeLast() removes the original's first element. For a sorted set, the reversed view still accepts add and remove, and new elements land in sorted position.
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>(List.of("p", "q", "r"));
List<String> rev = list.reversed();
rev.addLast("o");
rev.removeFirst();
System.out.println(list + " " + rev + " "
+ list.getFirst() + " " + list.getLast());
LinkedHashMap<String, Integer> lm = new LinkedHashMap<>();
lm.put("x", 1);
lm.put("y", 2);
lm.putFirst("y", 3);
System.out.println(lm + " " + lm.firstEntry() + " "
+ lm.reversed());
try {
new TreeSet<>(Set.of("b")).addLast("z");
} catch (UnsupportedOperationException e) {
System.out.println("TreeSet.addLast: UOE");
}
}
}Output (run on JDK 25):
[o, p, q] [q, p, o] o q
{y=3, x=1} y=3 {x=1, y=3}
TreeSet.addLast: UOEWhere sequenced methods throw
| Call | Result |
|---|---|
A positional insert on a sorted set (TreeSet or any other SortedSet) | UnsupportedOperationException — position comes from the ordering |
A positional put on a sorted map (TreeMap or any other SortedMap) | UnsupportedOperationException |
Any mutator on an unmodifiable collection or on a view of one, reversed() included | UnsupportedOperationException — a view of an unmodifiable collection is unmodifiable |
addFirst/addLast on a map's sequencedKeySet() view | UnsupportedOperationException — no value to pair with. (On keySet(), declared as a plain Set, addFirst does not even compile; its add throws UnsupportedOperationException.) |
removeFirst() on an Arrays.asList list | UnsupportedOperationException — fixed size |
Tip. Objective 5.1 is examined through short, complete programs that build an array or collection, change it and print it, so expect to state the output, whether the code compiles, or which exception is thrown. Array items probe default values, length versus last index, null rows in ragged arrays, shallow clone of 2D arrays, equals versus Arrays.equals, and binarySearch, compare and mismatch results. List items mix Arrays.asList, List.of, List.copyOf and unmodifiable views, the remove(int)/remove(Object) overloads, the return value of set, and removal inside loops. Set and map items hinge on missing or inconsistent equals/hashCode, comparators that return 0, return values of put, remove, putIfAbsent and getOrDefault, null results from merge and compute, view write-through and factory-map restrictions. Navigation and sequenced items ask for floor/ceiling/higher/lower results, range-view bounds and liveness, which Deque end a method uses and how it behaves when empty, and what reversed(), addFirst and putFirst do on lists, LinkedHashSet, TreeSet, LinkedHashMap and TreeMap — including the calls that throw UnsupportedOperationException.
- New array elements default to 0, 0.0, false, '\u0000' or null; the last index is length - 1; int[] g[] is an int[][]; only the first dimension of new int[3][] is required, and its unassigned rows are null.
- Arrays don't override equals or toString — use Arrays.equals, Arrays.toString and Arrays.compare/mismatch; clone() of a 2D array shares the rows.
- Arrays.binarySearch needs a sorted array and returns -(insertion point) - 1 for a missing key.
- Arrays.asList is fixed-size and writes through to its array (set and sort work); List.of/List.copyOf are unmodifiable (sort throws too) and reject null; unmodifiableList and subList are live views.
- On a List<Integer>, remove(1) removes index 1; set returns the replaced element; remove inside for-each usually throws ConcurrentModificationException — use removeIf or Iterator.remove.
- thenComparing only breaks ties, and reversed() reverses the whole comparator chain to its left.
- HashSet/HashMap need consistent equals(Object) and hashCode; TreeSet uses its comparator for add, contains and remove, keeping the first "equal" element; Set.equals compares elements only.
- put returns the old value; getOrDefault ignores null values but putIfAbsent overwrites them; a null result from merge or compute* removes the mapping.
- Deque: push/pop/peek work at the head, offer/add at the tail; poll/peek return null on empty, pop/remove()/element throw, remove(Object) just returns false; ArrayDeque rejects null.
- Sequenced collections add getFirst/getLast/addFirst/addLast/reversed(); reversed() writes through, and sorted sets/maps and unmodifiable views throw UnsupportedOperationException on positional inserts.
Frequently asked questions
What is the difference between Arrays.asList and List.of in Java?
Arrays.asList returns a fixed-size list backed by the array: set works and writes through to the array, but add and remove throw UnsupportedOperationException, and null elements are allowed. List.of returns an unmodifiable list that is independent of any array: set, add and remove all throw UnsupportedOperationException, and null elements — even in contains(null) — throw NullPointerException.
Why does list.remove(1) remove the second element of a List<Integer> instead of the value 1?
List has two remove methods, remove(int index) and remove(Object o). The argument 1 is an int, so the compiler picks remove(int) as an exact match without boxing, and the element at index 1 is removed. To remove the value 1, call remove(Integer.valueOf(1)). If the variable is declared as Collection<Integer>, only remove(Object) exists, so the same call removes the value.
When does removing an element inside a for-each loop throw ConcurrentModificationException?
Removing an element through the list itself (list.remove) while a for-each loop iterates it makes the iterator's next call throw ConcurrentModificationException. If the removed element was the second-to-last one, the size drops so hasNext returns false and the loop ends without an exception, silently skipping the last element. Use removeIf or an Iterator's remove method instead.
How does a TreeSet decide that two elements are duplicates?
A TreeSet uses its Comparator, or the elements' compareTo method, rather than equals. If the comparison returns 0, the new element is treated as a duplicate and add returns false, even when equals would say the two objects differ. Elements must be Comparable or a Comparator must be supplied, otherwise add throws ClassCastException.
What does Arrays.binarySearch return when the key is not found?
It returns -(insertion point) - 1, where the insertion point is the index of the first element greater than the key (or the array length if all elements are smaller). The result is always negative for a missing key. The array must already be sorted in the same order; on an unsorted array the result is undefined.
What happens when a Map.merge or compute function returns null?
For merge, compute, computeIfAbsent and computeIfPresent, a null result from the function removes the existing mapping for that key, or adds nothing if there was none. Any non-null result is stored as the key's new value and returned.
Which Deque methods throw on an empty deque and which return null?
On an empty Deque, pop, remove, removeFirst, removeLast, element, getFirst and getLast throw NoSuchElementException. poll, pollFirst, pollLast, peek, peekFirst and peekLast return null. ArrayDeque does not allow null elements at all, so any insert of null throws NullPointerException. Inserts never fail because a deque is empty; they fail only when a capacity-bounded deque is full, where add/push throw IllegalStateException and offer returns false.
What are sequenced collections in Java?
Sequenced collections are the SequencedCollection, SequencedSet and SequencedMap interfaces added in Java 21 for collections with a defined encounter order, such as List, Deque, LinkedHashSet, TreeSet, LinkedHashMap and TreeMap. They add getFirst, getLast, addFirst, addLast, removeFirst, removeLast (putFirst, putLast, firstEntry and pollFirstEntry for maps) and reversed(), which returns a live reverse-ordered view. Sorted sets and maps throw UnsupportedOperationException from addFirst/addLast and putFirst/putLast.
Source
This lesson covers the "Using Arrays and Collections to Store and Retrieve Data" domain of the official 1Z0-831 exam guide. Vendors revise their guides — check the source for the current version.
- Java SE 25 Developer Professional (1Z0-831) exam page and topics — Oracle University
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.