Backed Collections and Array-Backed Lists in Java - Quiz

Total: 6 questions

1. 

What is a backed collection (a collection view) in Java, and which methods return views?

A backed collection (a view) is a collection without its own storage: every read and write is forwarded to the source collection or array. Changes to the source are immediately visible through the view, and changes made through the view go into the source. A view is created in O(1), without copying anything. An array-backed list is the special case where the source is an array.

Views are returned by:

  • Arrays.asList(array) — a fixed-size view of an array;
  • list.subList(from, to) — part of a list with indexes [from, to);
  • map.keySet(), values(), entrySet() — removing from them removes the pair from the map, add() throws UnsupportedOperationException;
  • headSet()/subSet()/tailSet() and headMap()/subMap()/tailMap() — ranges of a TreeSet and a TreeMap;
  • descendingMap(), descendingSet(), list.reversed() (Java 21) — the same collection in reverse order;
  • Collections.unmodifiableList() and the other wrappers — read-only, but changes to the source collection are visible through them.
2. 

Why does set() work on a list from Arrays.asList(), while add() and remove() throw UnsupportedOperationException?

Arrays.asList(array) returns a fixed-size list that holds a reference to the array itself, not a copy. The length of an array cannot change, so everything that changes the size is forbidden, while everything that works in place is allowed and modifies the array:

  • get(), size(), contains(), indexOf() — read the array;
  • set(i, e), list.sort(...), Collections.shuffle(list) — work and modify the array; a write array[i] = ... is also visible in the list right away;
  • add(), add(i, e), remove(int), addAll() — UnsupportedOperationException;
  • remove(Object), removeIf(), clear() — throw only if there is something to remove: remove("pear") for a missing element returns false.

The class of such a list is java.util.Arrays$ArrayList, a private nested class of Arrays, not java.util.ArrayList: list instanceof ArrayList is false. Unlike List.of(), a null element is allowed. If you need a mutable list, make a copy: new ArrayList<>(Arrays.asList(array)).

3. 

What does Arrays.asList(new int[]{1, 2, 3}) return, and how do you turn an int[] into a List<Integer>?

It returns a List<int[]> with one element — the array itself, so size() is 1. The generic parameter T in asList(T... a) cannot be a primitive, so the whole int[] becomes a single varargs argument. The line List<Integer> list = Arrays.asList(new int[]{1, 2, 3}) does not compile.

With an Integer[] or listed values Arrays.asList(1, 2, 3) you get a list of three numbers. For an array of primitives, use a stream:

int[] ints = {1, 2, 3};
List<Integer> fixed = Arrays.stream(ints).boxed().toList();                        // Java 16, unmodifiable
List<Integer> mutable = Arrays.stream(ints).boxed().collect(Collectors.toList()); // mutable

The stream result is a copy: it has no link to the source array.

4. 

How do you remove a range of list elements with subList(), and when does a sublist throw ConcurrentModificationException?

list.subList(from, to) is a view of the elements from index from inclusive to to exclusive. Everything done through the sublist (set(), add(), remove(), clear()) is reflected in the source list immediately. That is why the standard way to remove a range is list.subList(from, to).clear():

List<String> list = new ArrayList<>(List.of("a", "b", "c", "d", "e"));
list.subList(1, 4).clear();
System.out.println(list); // [a, e]

But if you change the size of the source list bypassing the sublist (list.add(), list.remove()), the sublist becomes invalid: any access to it (get(), size(), toString()) throws ConcurrentModificationException. Replacing an element with list.set() is not a structural modification and does not break the sublist.

Wrong bounds are checked right away: subList(3, 1) throws IllegalArgumentException, and subList(0, 10) on a five-element list throws IndexOutOfBoundsException. A sublist of a list from Arrays.asList() is fixed-size too: its clear() throws UnsupportedOperationException. Also remember that a sublist keeps a reference to the whole source list: copy a long-lived fragment with new ArrayList<>(list.subList(0, 10)).

5. 

How does subMap() of a TreeMap work as a backed collection, and what happens when you put() a key outside the range?

map.subMap(fromKey, toKey) returns a view of the keys from fromKey inclusive to toKey exclusive. Entries added to the source map inside the range show up in the view, and entries added or removed through the view change the source map. Keys outside the range are not visible in the view.

SortedMap<String, String> map = new TreeMap<>(Map.of("a", "apricot", "c", "cherry"));
SortedMap<String, String> sub = map.subMap("b", "g");
map.put("b", "banana");     // visible in sub
sub.put("d", "date");       // went into map
map.put("w", "watermelon"); // not visible in sub
sub.put("p", "pear");       // IllegalArgumentException: key out of range

You cannot add a key outside the view's range through the view: put() throws IllegalArgumentException: key out of range — such a key is added to the source map. Bounds are checked by the sort order, and strings are compared by character codes: sub.put("B", "blueberry") fails too, because uppercase “B” (U+0042) comes before lowercase “b” (U+0062). headMap() and tailMap() behave the same way.

6. 

What is the difference between a view and a copy: which of Arrays.asList(), new ArrayList<>(...), List.of(), List.copyOf() and Collections.unmodifiableList() see changes to the source?

A view holds a reference to the source and sees its changes; a copy gets its own data at creation time and no longer depends on the source.

  • Arrays.asList(array) — a view: after array[0] = "apple" the list changes too;
  • new ArrayList<>(Arrays.asList(array)) — a mutable copy;
  • List.of(array) (Java 9) and List.copyOf(list) (Java 10) — unmodifiable copies: changes to the source array or list are not visible in them;
  • Collections.unmodifiableList(list) — the opposite: a read-only view; you cannot modify through it, but changes to the source list are visible through it.

Immutability and a link to the source are different properties: List.of() is unmodifiable but is not a view. If you need an independent “snapshot” of any view, copy it: new ArrayList<>(list.subList(1, 4)), new TreeMap<>(map.subMap("b", "g")), new HashSet<>(map.keySet()) or List.copyOf(...).

Page 1 of 1