Serguey Asael Shinder: stream.toList() is not collect(Collectors.toList()): one is unmodifiable
Since Java 16, Stream has a toList() method, and it is tempting to replace every collect(Collectors.toList()) with it. The two look the same and usually behave the same, until someone adds to the result:
List<String> a = names.stream().filter(n -> !n.isBlank()).collect(Collectors.toList());
a.add("x"); // works today
List<String> b = names.stream().filter(n -> !n.isBlank()).toList();
b.add("x"); // UnsupportedOperationException

What each one promises.
Stream.toList()): "The returned List is unmodifiable; calls to any mutator method will always cause UnsupportedOperationException to be thrown." It keeps encounter order. The javadoc also says the result "may be value-based", so identity-sensitive operations such as==and synchronisation on it are unreliable.Collectors.toList()): "There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned." Today it hands you a list you can add to, but the contract does not say so.Collectors.toUnmodifiableList()) (Java 10): unmodifiable, and it "disallows null values and will throw NullPointerException if it is presented with a null value".
The three-way difference in one line. toList() is unmodifiable and accepts nulls; toUnmodifiableList() is unmodifiable and rejects them; Collectors.toList() promises nothing about mutability.
Where it bites. The refactoring is usually done mechanically, and the add that fails is often not next to the stream: it is in a method several calls away that received the list and assumed it could append. The failure appears at run time, in a code path the refactoring did not touch.
The rule. If the caller needs to modify the result, say so in the code: collect(Collectors.toCollection(ArrayList::new)), which the toList() javadoc itself points to "if more control over the returned object is required". Use toList() where the list is a read-only result, and let the exception catch anyone who assumed otherwise.
No JDK runs on the machine this note was written on; the behaviour described is the one specified in the linked javadoc.