Skip to content

Typos and small errors in "The Collections Framework" tutorials ( https://dev.java/learn/api/collections-framework/ ) #206

Closed

Description

@willy-b

Hello dev.java team!

Thanks so much for all this excellent content.
Just reporting some typos, some possibly outdated content, and some small errors (primarily in the "Storing Elements in Stacks and Queues" article, reported at the bottom of this issue comment).


In "Iterating over the Elements of a Collection", https://dev.java/learn/api/collections-framework/iterating/ , (archived as is at https://web.archive.org/web/20260305204909/https://dev.java/learn/api/collections-framework/iterating/ ),

The Iterable/Iterator implementations (both the record and "plain class" approaches presented) appear to have possible small issues

Image
  • the limit of the hasNext() is not consistent with the next() (when hasNext() returns false, there is still another element to be returned by next(), see below)
  • where one could simply write return index++; to get-and-increment, an unnecessary secondary variable is used
record Range(int start, int end) implements Iterable<Integer> {

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<>() {
            private int index = start;
            
            @Override
            public boolean hasNext() {
                return index < end; // hasNext() uses a half-open end excluded interval
            }

           @Override
            public Integer next() {
                if (index > end) { // next() uses a closed end included interval, should be >= to be consistent; next() can go one further after hasNext() returns false here
                    throw new NoSuchElementException("" + index);
                }
                int currentIndex = index;
                index++;
                return currentIndex; // maybe could just `return index++;` for desired get-and-increment behavior
            }

The inconsistency of where next() stops returning elements and how hasNext() is defined can be demonstrated as follows:

jshell> Range r = new Range(0, 3);
r ==> Range[start=0, end=3]

jshell> var rIter = r.iterator();
rIter ==> Range$1@ff5b51f

jshell> rIter.next();
$4 ==> 0

jshell> rIter.next();
$5 ==> 1

jshell> rIter.next();
$6 ==> 2

jshell> // here the hasNext will start saying false, since they wrote hasNext in a half-open end excluded limi
t

jshell> rIter.hasNext();
$7 ==> false

jshell> // but the next() method supports up to including the limit, so it does have a next element

jshell> rIter.next();
$8 ==> 3

jshell> rIter.hasNext(); // still false
$9 ==> false

jshell> rIter.next(); // now will fail
|  Exception java.util.NoSuchElementException: 4
|        at Range$1.next (#1:16)
|        at Range$1.next (#1:5)
|        at (#10:1)


If you compare that to the default List iterator implementations, that is not the behavior (hasNext() usually returns false only if the next call to next() will throw NoSuchElementException).

The same issues also occur in the "plain class" example that follows.

  • Separately, there appears to be a typo on the line:
for (int i : new Range1(0, 5)) {

in that "Range1" is written when "Range" seems intended.


In the Table of Contents for this tutorial, https://dev.java/learn/api/collections-framework/ (archived as is: https://web.archive.org/web/20260305180836/https://dev.java/learn/api/collections-framework/ ):

  • "Why Choosing a Collection over an Array?" should be "Why Choose a Collection over an Array?"
  • "Using a Collection to Store and Retrieve Elements" is missing a period at the end

In "Storing Data Using the Collections Framework", https://dev.java/learn/api/collections-framework/intro/ (archived as is at: https://web.archive.org/web/20260305183418/https://dev.java/learn/api/collections-framework/intro/ ):

  • The sentence "Is what you need consists in:", I think a different sentence is intended, beginning with "If" instead of "Is"

In "Getting to Know the Collection Hierarchy", https://dev.java/learn/api/collections-framework/organization/
(archived as is https://web.archive.org/web/20260305191553/https://dev.java/learn/api/collections-framework/organization/ ):


In "Storing Elements in a Collection", https://dev.java/learn/api/collections-framework/collection-interface/ (archived as is: https://web.archive.org/web/20260305200446/https://dev.java/learn/api/collections-framework/collection-interface/ ):

  • In the sentence, "You will see more discussions on the right implementation to choose in later in this tutorial",
    "to choose in later in this tutorial" should be "to choose later in this tutorial" (remove extra "in").

In "Creating and Processing Data with the Collections Factory Methods", https://dev.java/learn/api/collections-framework/immutable-collections/ (archived as is at https://web.archive.org/web/20260305231534/https://dev.java/learn/api/collections-framework/immutable-collections/ ):


In "Storing Elements in Stacks and Queues", https://dev.java/learn/api/collections-framework/stacks-queues/ (archived as is at https://web.archive.org/web/20260305234616/https://dev.java/learn/api/collections-framework/stacks-queues/ ):

Image
  • re: the text block on Deque methods:

Moreover, Deque also defines the methods you would expect in any queue or stack class:

push(element): adds the given element to the head of the double ended queue
pop(): removes and return the element at the head of the double ended queue
poll(): does the same at the tail of the double ended queue
peek(): shows you the element at the tail of the double ended queue.

The sentences on Deque poll and peek behavior appear incorrect,
e.g., "poll(): does the same at the tail of the double ended queue", appears to be mistaken in that poll removes from the head, not the tail as written.

Image

Compare this to the official documentation, https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Deque.html#poll()
which says

Retrieves and removes the head of the queue represented by this deque (in other words, the first element of this deque), or returns null if this deque is empty.
This method is equivalent to pollFirst().

The same error occurs for peek, as peek also shows the element at the head, not the tail of the Deque (see https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Deque.html#peek() ).

  • The sentence "In case there is no element to pop, poll, or peek, then a null value is returned by these methods."
    appears mistaken as pop throws NoSuchElementException when the Deque is empty (see https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Deque.html#pop() ).

  • The sentence "There is one difference though: the peek operations are named getFirst() and getLast() in Deque, and element() in Queue."
    seems confusing as "peek" is available in Deque and Queue (as well as the equivalent peekFirst() and also a peekLast()), whereas this sentence suggests they are not.
    There are also the mentioned getFirst()/getLast() in Deque and element() in Queue that also return elements without removing them but which throw NoSuchElementException instead of returning null on empty Deques or Queues.

Finally, this may not be an error, but I think is worth reviewing:
Near the beginning of this article, it is written of stacks and queues that:

These structures are very simple and gives you three main operations.
push(element): adds an element to the queue, or the stack
pop(): removes an element from the stack, that is, the youngest element added
poll(): removes an element from the queue, that is, the oldest element added
peek(): allows you to see the element you will get with a pop() or a poll(), but without removing it from the queue of the stack.

In this explanation, the sentence "push(element): adds an element to the queue, or the stack" might be confusing as the Queue interface actually only includes "add" and "offer" for adding to the queue , NOT push, which comes from the stack interface ( see https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Queue.html ).

From Java 25 SE documentation, Queues have:

Summary of Queue methods
Throws exception Returns special value
Insert add(e) offer(e)
Remove remove() poll()
Examine element() peek()

and Stacks have push, pop, and peek ( https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Stack.html ).
The article does discuss this later, defining push operations for a queue as add and offer, but I think "push(element)" in reference to a queue might be better immediately paired with a clarifying statement that "add" or "offer" are the actual method names, not "push" for a queue.


Depending on available time I may add additional items here as I am still reviewing this series of artices so feel free to leave open even if you address the above (I would also be happy to confirm any changes before closing if desired).

Thanks very much!

Activity

  1. JosePaumard commented on Mar 6, 2026

    @JosePaumard
    Contributor

    Thank you again for your reports! I just pushed fixes.
    The upper bound of the Range class is indeed excluded. I added that in a comment.
    The idea of the section on Queues and Statcks is to first define the basic operations (they do not depend on the language or API you are using), then show the implementations. Indeed, there is no "simple" implementation of the concept of queue nor stack, and the Stack class is an old legacy class that you should not use. Confusion comes from this old class, and from the fact that there is no simple, basic implementation of these concepts.
    I still need to update the CF structure with SequencedCollections. I keep that in mind, and will do that a little later.

  2. willy-b commented on Mar 8, 2026

    @willy-b
    Author

    Thanks Jose!

    Re: your live-site updates above for "The Collections Framework" pages (feel free alternatively to send PRs for review as it may be easier for me to check, but otherwise I am happy to keep reviewing this way using web.archive.org to compare)


    For "Storing Elements in Stacks and Queues", https://dev.java/learn/api/collections-framework/stacks-queues/ (archived as is at https://web.archive.org/web/20260307235916/https://dev.java/learn/api/collections-framework/stacks-queues/ , vs old version at https://web.archive.org/web/20260305234616/https://dev.java/learn/api/collections-framework/stacks-queues/ ):

    And as stated above but I think it also slipped through the cracks,

    • The sentence "There is one difference though: the peek operations are named getFirst() and getLast() in Deque, and element() in Queue."
      seems confusing as "peek" is available in Deque and Queue (as well as the equivalent peekFirst() and also a peekLast()), whereas this sentence suggests they are not.
      There are also the mentioned getFirst()/getLast() in Deque and element() in Queue that also return elements without removing them but which throw NoSuchElementException instead of returning null on empty Deques or Queues.

    For https://dev.java/learn/api/collections-framework/iterating/ after your change above, archived at https://web.archive.org/web/20260306182646/https://dev.java/learn/api/collections-framework/iterating/ (vs old version https://web.archive.org/web/20260305204909/https://dev.java/learn/api/collections-framework/iterating/ ):

    • The next() vs hasNext() discrepancy was acknowledged and fixed for the record Iterable, but the next example of a "plain class" solution, has not been fixed yet; the next() method in the Iterator of the non-record class Range implements Iterable<Integer> snippet needs the same update to throw an NoSuchElementException if index >= end in next() to be consistent with hasNext()

    • mentioned earlier but not updated or commented on in your response above: both the record Range and the "plain class" class Range snippets should probably just return index++; in next() instead of introducing a secondary variable to hold and return the current index before incrementing

    • the class name Range1 in one of the snippets (see below) is not defined and appears a typo where Range was intended (reported above but was not fixed in your last update):

    for (int i : new Range1(0, 5)) {
    

    In the Table of Contents for this tutorial, https://dev.java/learn/api/collections-framework/ (archived as is: https://web.archive.org/web/20260307234704/https://dev.java/learn/api/collections-framework/ , vs old https://web.archive.org/web/20260305180836/https://dev.java/learn/api/collections-framework/ ):

    • The typo I mentioned above that "Why Choosing a Collection over an Array?" should be "Why Choose a Collection over an Array?"

    is not fixed or commented upon yet.


    In "Getting to Know the Collection Hierarchy", https://dev.java/learn/api/collections-framework/organization/
    (archived as is https://web.archive.org/web/20260307235117/https://dev.java/learn/api/collections-framework/organization/ , old version: https://web.archive.org/web/20260305191553/https://dev.java/learn/api/collections-framework/organization/ ):

    Not fixed yet:

    • In the sentence, "You will see more details ont that later", "ont" should be "on"
    • Not reported above, a little thing: In "Of course, all these operations are also available on List and Set. So what does make the difference between a plain instance of Collection and an instance of Set or an instance of List?", "So what does make the difference" might be rephrased "One might ask: What makes the difference", "So, what is the difference", or similar as "So what does make" seems to be very unusual wording.

    Once these already reported items in these articles are confirmed fixed, if it is ok with you, I will submit additional discovered items in this issue (I am preparing them presently separately).

    Thanks again!

  3. JosePaumard commented on Mar 9, 2026

    @JosePaumard
    Contributor

    Thank you again for all this! I just pushed a fix.
    As for the methods of Deque: I fixed and added some elements. I prefer to keep separated the methods xxxFirst() and xxxLast() as I think that it's easier to remember the structured of this API in that way. The push pop poll peek methods are also there, and I added the behavior when the deque is full or empty.
    As for the index++. People often get confused about the ++ really been executed or not. Some speakers even do nice quiz with that ;) So even if it can be written in the way you suggest, I prefer to keep it explicit. Don't worry, the JVM will fix this at runtime.

  4. willy-b commented on Apr 12, 2026

    @willy-b
    Author

    Thanks for fixing most of the items reported!

    However, it looks like one different small error might have come in with your team's last set of updates to https://dev.java/learn/api/collections-framework/stacks-queues/ .

    Re: #206 (comment)
    I had written:

    For "Storing Elements in Stacks and Queues", https://dev.java/learn/api/collections-framework/stacks-queues/ (archived as is at https://web.archive.org/web/20260307235916/https://dev.java/learn/api/collections-framework/stacks-queues/ , vs old version at https://web.archive.org/web/20260305234616/https://dev.java/learn/api/collections-framework/stacks-queues/ ):
    The sentence "In case there is no element to pop, poll, or peek, then a null value is returned by these methods."
    still appears mistaken as pop throws NoSuchElementException when the Deque is empty (see https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/Deque.html#pop() ).

    In https://web.archive.org/web/20260411212316/https://dev.java/learn/api/collections-framework/stacks-queues/
    The sentence has been updated by your team to:

    In case there is no element to pop, poll, or peek, then a NoSuchElementException exception is thrown by these methods.

    Which is unfortunately not quite correct (I asked to update "pop" to note it throws NoSuchElementException instead of null, not to say "peek" or "poll" throw that Exception, which they don't),
    and is also inconsistent with the immediately preceding sentences:

    poll(): does the same at the head of the double ended queue. This method return [sic] null if there is no element to poll. [correct]
    peek(): shows you the element at the head of the double ended queue. This method return null if there is no element to peek. [correct]

    (noting in passing also that in "This method return null if there is no element to poll." in "poll()"'s description , "return" should be "returns")

    The sentence might be rewritten:

    [CURRENT] In case there is no element to pop, poll, or peek, then a NoSuchElementException exception is thrown by these methods.
    [SUGGESTED] In case there is no element to pop a NoSuchElementException is thrown. On the other hand, if there are no elements to poll or peek those methods merely return null without throwing any Exception.

    (or something similar, making clear pop throws a NoSuchElementException, but peek and poll return null when there are no elements remaining)


    I am reviewing https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ presently (and working through the JMH benchmarks described) (article archived as is at https://web.archive.org/web/20260411221339/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ ).

    So far I can report the following:

    In the sentence:

    This is all good, and there is not many differences between both

    • "there is not" should be "there are not"

    • "between both" might be better as "between them"

    Re:

    LinkedList interface and ArrayList are not working in the same way, at all.

    LinkedList is not an interface, so this seems inaccurate. Maybe this should be rewritten to say something like "Despite the shared interface, List, these implementations, LinkedList and ArrayList are not working in the same way, at all".

    Re:

    For ArrayList, both patterns are roughly the same. Using an index is a little more costly, due to the fact that you need to manage this index. You may be wondering why incrementing an int is more costly than having to manage an iterator.
    Well, it turns out that the for-each pattern does not expose the iterator in your source code, so it cannot be used for anything else than iterating over this list. When it comes to optimizing your code, the JIT compiler may see that, and in some cases can optimize this piece of code, and avoid the creation of this iterator.

    You end up with a code that it much faster, because no object is actually created or managed.

    • "that it" in "that it much faster" should be "that is"

    • The description of the speed difference, FOR ARRAYLIST, between the index and the for-each Iterator seems somewhat inconsistent here. First it is said "both patterns are roughly the same", noting "an index is a little more costly", but then it is said that using the for-each results in "code that it [sic] much faster"; perhaps it should say "that is faster" rather than "much faster" (indeed, it is a small advantage to iterate over indexing when using ArrayList compared to the advantage of iterating for a LinkedList which is tremendous).

    Re:

    The complexity of the different operations offered by the List interface is usually described as being O(1), O(n) or even O(ln(n)).

    The article does not mention any logarithmic time [e.g. O(log(n))=O(ln(n))] operations offered specifically by the List interface ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/List.html ) itself, so I think that complexity class should have an example provided from this interface or the mention should be considered to be removed from this sentence.

    Outside of the List interface itself, there are many examples, like Collections.binarySearch which accepts any (already SORTED) List and for RandomAccess lists with constant-time random access (like ArrayList) runs in logarithmic time (O(log(n)) = O(ln(n)) (e.g. in OpenJDK Collections.binarySearch(List<? extends Comparable<? super T>> list, T key), https://github.com/openjdk/jdk/blob/1e2b0d2b67c7ae0843c9adbc641471040fbe7d71/src/java.base/share/classes/java/util/Collections.java#L216 ),

    but binary search is not part of the List interface, nor do ArrayList's indexOf/contains implementations use any tricks on sorted or nearly sorted lists, and use a simple O(n) linear search from the head to the tail of the List looking for the object ( https://github.com/openjdk/jdk/blob/1e2b0d2b67c7ae0843c9adbc641471040fbe7d71/src/java.base/share/classes/java/util/ArrayList.java#L287 , https://github.com/openjdk/jdk/blob/1e2b0d2b67c7ae0843c9adbc641471040fbe7d71/src/java.base/share/classes/java/util/ArrayList.java#L299 , https://github.com/openjdk/jdk/blob/1e2b0d2b67c7ae0843c9adbc641471040fbe7d71/src/java.base/share/classes/java/util/ArrayList.java#L276 ).

    Sort is O(n log (n)) (or at least O(n) if passed an already sorted list or nearly sorted list with a constant number of unsorted elements as it takes O(n) to even check if the list is sorted, and smart sorting implementations do take advantage of this but this is still slower than logarithmic) and lists are not assumed to be sorted in the List interface.

    PriorityQueue ( https://github.com/openjdk/jdk/blob/1e2b0d2b67c7ae0843c9adbc641471040fbe7d71/src/java.base/share/classes/java/util/PriorityQueue.java#L72 ) does NOT implement List (and I am not aware of any Heap-like or alternatively Tree-like data structures which do implement List ), so available List implementations add/remove (and get/set), as far as I am aware, in either O(1) or O(n) time (e.g. see a description of ArrayList time complexities in OpenJDK at https://github.com/openjdk/jdk/blob/1e2b0d2b67c7ae0843c9adbc641471040fbe7d71/src/java.base/share/classes/java/util/ArrayList.java#L42-L48), NOT O(log(n)).

    There are many logarithmic time operations possible on Lists in special cases (like binary search on pre-sorted constant-time RandomAccess Lists) but what operations offered by the List interface itself run in logarithmic time on a Java API implementation is a different question.

    I will report back with more feedback if I find anything after reproducing the JMH benchmarks in https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ and reviewing the whole article.


    Thanks so much!

  5. JosePaumard commented on Apr 13, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! I just pushed a fix.
    This section focuses on the differences between ArrayList and LinkedList, and show that algorithm complexity is not always relevent when it comes to comparing performance of classical list operations.
    So, even if the last part of your comment is very interesting, I prefer to keep it for later, and in another section. BTW there are also interactions with the Vector API, which can be used internally, that would make such a section even more interesting.
    BTW, there is one part of your comment that I'm not sure to understand?

    it is a small advantage to iterate over indexing when using ArrayList compared to the advantage of iterating for a LinkedList which is tremendous
    Again, thank you!

  6. willy-b commented on Apr 14, 2026

    @willy-b
    Author

    Thanks so much for the additional fixes!

    In https://web.archive.org/web/20260413140310/https://dev.java/learn/api/collections-framework/stacks-queues/
    vs https://web.archive.org/web/20260411212316/https://dev.java/learn/api/collections-framework/stacks-queues/ :

    • the erroneous "In case there is no element to pop, poll, or peek, then a NoSuchElementException exception is thrown by these methods." (poll and peek return null and do not throw NoSuchElementException)
      has been removed by you and this is fine since it is redundant with preceding sentences anyway. Thanks!
    Image

    In https://web.archive.org/web/20260413142128/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ vs old version https://web.archive.org/web/20260411221339/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/:

    • The previous "This is all good, and there is not many differences between both" was updated to "This is all good, and there are not many differences between them" as requested
    • "LinkedList interface and ArrayList are not working in the same way, at all.", which I had asked about as LinkedList is NOT an interface,
      has been updated by you to "The LinkedList class and the ArrayList class both implement the List interface, but their implementations are different". Thanks!
    Image

    Here are possible additional issues I found in https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ (archived as is at https://web.archive.org/web/20260413142128/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ ):

    Re:

    For LinkedList the situation is different. Using an iterator is more expensive that in the ArrayList case, mostly due to pointer chasing.

    • "more expensive that" should be "more expensive than"

    But because this efficient algorithm is executed on a hardware that makes pointer chasing very costly,

    • "executed on a hardware" should be "executed on hardware"

    Memory wise, you need to keep in mind that a LinkedList consume much more memory that an ArrayList.

    • "consume" should be "consumes"

    When discussing timings in the section "Iterating with an Index and an Iterator" of https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ (archived as is at https://web.archive.org/web/20260413142128/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ )

    Re dev.java author's statement on LinkedList 1000 element iteration times on their computer (must be compared relative to other timings reported by them throughout the article, hard to absolutely compare without specs):

    [CURRENT] It takes about half a second to iterate [using an index instead of an iterator] over a linked list of 1000 elements, where it takes only 4ms with an iterator.
    LinkedList SIZE Score Error Units
    Iterate iterator 1000 4.950 ± 0.116 ms/op
    Iterate index 1000 584.889 ± 4.396 ms/op

    The ratio between the two measurements shows the expected slowdown for iterating using an index vs using an iterator (bad algorithm for LinkedList). But the 4 milliseconds with an iterator for a LinkedList of length 1000 is much too slow given the around 1 nanosecond times to read first/last and around half a microsecond times to iterate to middle reported elsewhere in the article.
    I think there might be an error here in terms of units. I think this is potentially based on an error of writing microsecond/op (μs/op with a "mu" not "m") as "ms/op" read as milliseconds/op for the time for iterating over the entire 1000 elements and should be instead,

    [PROPOSED] takes about half a millisecond to iterate using an index instead of an iterator over a linked list of 1000 elements, whereas it only takes 5 microseconds with an Iterator
    LinkedList SIZE Score Error Units
    Iterate iterator 1000 4.950 ± 0.116 microseconds/op (μs/op)
    Iterate index 1000 584.889 ± 4.396 microseconds/op (μs/op)

    (Saying "5 microseconds with an iterator" to also adjust for the observation that 4.950μs should be rounded to 5μs not 4μs)

    Recall the earlier tables showing the timings to read to the middle element of an ArrayList or LinkedList using an iterator showing constant time O(1) for ArrayList and linear time O(n) for LinkedList:

    ArrayList SIZE Score Error Units
    Read middle 10 1.571 ± 0.055 ns/op
    Read middle 100 1.616 ± 0.073 ns/op
    Read middle 1000 1.543 ± 0.018 ns/op
    Read middle 10000 1.537 ± 0.010 ns/op
    [unlike the ArrayList, reaching the middle of the LinkedList requires iterating through all the first half of the elements, roughly half the operations required to reach the end]
    LinkedList SIZE Score Error Units
    Read middle 10 3.211 ± 0.023 ns/op
    Read middle 100 31.118 ± 0.321 ns/op
    Read middle 1000 566.079 ± 8.696 ns/op
    Read middle 10000 7836.099 ± 902.666 ns/op

    Those are nanosecond timings. (If "ns" weren't enough to conclude these are nanoseconds, see the sentence "This difference is very small, a fraction of a nanosecond, and not really relevant though" below one of the tables using "ns/op".)
    The table above says iterating to the middle of a 1000 element LinkedList takes 566.079 nanoseconds (roughly half a microsecond) on that author's machine.
    Assuming those are correct, it seems we should expect reading 1000 elements in a LinkedList (using an iterator, not indexing) to take much closer to 2x this read-to-middle number (or around 1100 nanoseconds or 1.1 microseconds) than to 2,000x this number or 1.1 milliseconds (or 4 milliseconds as quoted above, which I believe was intended to be 4 microseconds).

    Again, we can check another table provided by this author,
    showing the constant time O(1) for reading the first element of either ArrayLists or LinkedLists, and confirm the O(1) single operation time on the article's author's machine was around a nanosecond:

    ArrayList SIZE Score Error Units
    Read first 10 1.181 ± 0.022 ns/op
    Read first 100 1.200 ± 0.041 ns/op
    Read first 1000 1.167 ± 0.009 ns/op
    Read first 10000 1.174 ± 0.014 ns/op
    LinkedList SIZE Score Error Units
    Read first 10 1.127 ± 0.030 ns/op
    Read first 100 1.107 ± 0.008 ns/op
    Read first 1000 1.121 ± 0.016 ns/op
    Read first 10000 1.119 ± 0.014 ns/op

    So we expect iterating using an iterator (O(n)) through either the LinkedList or ArrayList to take closer to 1000x the single operation number, or a microsecond, than to 1,000,000x this amount or a millisecond (no reason for a high constant multiplier in this case), as iterating in both cases is O(n) without a large constant (it is true that iterating using indexing instead of an iterator for LinkedList is O(n^2) and we will see indexing vs using an iterator for a LinkedList is indeed ~120x slower, even after fixing the units). Indeed, on my own 4GHz CPU machine, I see nanoseconds for single operations and microseconds for using iterators to go through length 1000 lists (of both LinkedLists and ArrayLists), and hundreds of microseconds to do the slow O(n^2) index iteration of the LinkedList.

    The ArrayList table for the "Iterating with an Index and an Iterator" section also seems to have the same issue for both types of lists writing "ms/op" instead of "microsecond/op" or "μs/op" (with a "mu" (μ) not "m")

    ArrayList SIZE Score Error Units
    Iterate iterator 1000 1.447 ± 0.024 ms/op
    Iterate index 1000 1.986 ± 0.045 ms/op

    **Here it is even more obvious (recall ArrayList read first AND read to middle times were both given as around a nanosecond) as we don't expect any slowdown for any type of looping on ArrayList (unlike LinkedList which has the switch from O(n) to O(n^2) when using an index to iterate instead of an iterator), so these times to loop through 1000 element ArrayLists should be roughly 1000x the single element read times, or 1,000x 1 nanosecond, which is 1 microsecond, not 1 millisecond.

    Repeating for clarity: I think the units in these tables and the sentence quoted above, ms, are written as milliseconds / op but should all be microseconds/op (μs/op).
    Furthermore it should be clarified that iterating through all 1000 elements is considered the single operation (unless it is not intended to be but otherwise not clear why we are stepping up from nanoseconds to a higher unit).

    The unit change seems required to be consistent with nanosecond timings for access 1st, last, middle, etc in earlier sections and consistent with general order of magnitude processor speeds expected these days, gigahertz.
    As a double check assuming gigahertz processors, we would expect timings in the microseconds (not milliseconds) for even 1000 iterations as following a reference in a LinkedList to the next node (moving to the adjacent index in an ArrayList) and accessing the referenced Integer should not take thousands or tens of thousands of processor cycles. Taking 4ms as quoted in the article to loop through all elements in a 1000 element LinkedList using an Iterator means taking 1/250 of a second so for e.g. a 2GHz processor that is 8 million cycles to iterate through e.g. a LinkedList of length 1000 or 8000 cycles per node, i.e. 8000 cycles to follow the reference from 1 LinkedList node to the next, which does not seem right.

    This also applies to:

    LinkedList SIZE ITERATION Score Error Units
    toList then iterate 1000 1 5.182 ± 0.347 ms/op
    toList then iterate 1000 10 14.031 ± 0.793 ms/op
    toList then iterate 1000 100 100.104 ± 7.422 ms/op

    (units here, ms, are again written as milliseconds / op but I suspect should be microseconds/op (μs/op))

    On "Analyzing the Memory Consumption of LinkedList and ArrayList" of https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ (archived as is at https://web.archive.org/web/20260413142128/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ )

    Re:

    If you are in that case [of a single element List] you still have several solutions. Using LinkedList is one of them, but maybe you can also use one of the non-modifiable implementations that you can get with the List.of() methods. These are more efficient than ArrayList, and LinkedList, memory wise: only 26 bytes for just one element. You need to keep in mind that they are unmodifiable though.

    • Can we consider to add the JVM configuration under which List.of(1) produces a 26 byte footprint (excluding the Integer and the shared dummy Object reference used by the underlying implementation class List12 when there is only one element in recent versions of Java)? The memory footprint of these objects are highly configuration dependent (compression on/off for references, compression on/off for class pointers, since Java 25's https://openjdk.org/jeps/519 the -XX:+UseCompactObjectHeaders flag, 32 bit vs 64 bit VM mode, etc).

    Note my JVM is configured (matches default shown on JOL tool website) (NOT using CompactObjectHeaders):

    # VM mode: 64 bits
    # Compressed references (oops): 3-bit shift
    # Compressed class pointers: 0-bit shift and 0x7FA583000000 base
    # Object alignment: 8 bytes
    #                       ref, bool, byte, char, shrt,  int,  flt,  lng,  dbl
    # Field sizes:            4,    1,    1,    2,    2,    4,    4,    8,    8
    # Array element sizes:    4,    1,    1,    2,    2,    4,    4,    8,    8
    # Array base offsets:    16,   16,   16,   16,   16,   16,   16,   16,   16
    

    But as mentioned above there are many other configurations possible, however I didn't see any obvious combination of these that gives 26 bytes for List.of(1) (I see 28 bytes from List.of(1) in most configurations and it appears based on later memory numbers given, e.g. in the final table comparing ArrayList of size 2, Linked List, Default ArrayList, Default trimmed ArrayList as elements are added one by one that the author is also using "Object alignment: 8 bytes" which should make 26 bytes impossible.)

    • Inconsistent memory numbers for non-trimmed ArrayList with default capacity (10) containing 1 element:

    There is one case though, where storing your object in an ArrayList is more costly. If you have only one object to store, then the ArrayList you create may wrap array of size 10.
    It depends on how you have created your ArrayList. And in that case, your ArrayList consumes more memory than a LinkedList: 80 bytes versus 56 bytes.

    I agree, I see 80 bytes in this case (see further down for full detail).

    But the 80 bytes number given above is inconsistent with the table shown a bit later, which says 76 bytes WITHOUT trimToSize() (my comments in square brackets):

    Memory consumption for 1 element
    ArrayList 76 bytes [I see 80 bytes locally, no alignment bytes added but using 8 byte alignment]

    Given the 8 byte alignment suggested by the last table in the article (same as used on my system and in JOL documentation), I don't know how 76 bytes comes up.

    Here is how my system gets to 80 bytes for an ArrayList with capacity 10 and 1 Integer in it, excluding the Integer:

    [An ArrayList with 1 Integer element before trim to size (so default capacity == 10 is in use) == 80 bytes (96 bytes including 16 bytes for the Integer, or 80 bytes without)]
    java.util.ArrayList@68837a77d footprint:
         COUNT       AVG       SUM   DESCRIPTION
             1        56        56   [Ljava.lang.Object;
             1        16        16   java.lang.Integer
             1        24        24   java.util.ArrayList
             3                  96   (total)
    
    [for reference, the memory footprint of an empty Object[] array of capacity 10; array base offset is 16 + 10*4 == 56]
    [Ljava.lang.Object;@4157f54ed footprint:
         COUNT       AVG       SUM   DESCRIPTION
             1        56        56   [Ljava.lang.Object;
             1                  56   (total)
    
    java.util.ArrayList object internals:
    OFF  SZ                 TYPE DESCRIPTION               VALUE
      0   8                      (object header: mark)     N/A
      8   4                      (object header: class)    N/A
     12   4                  int AbstractList.modCount     N/A
     16   4                  int ArrayList.size            N/A
     20   4   java.lang.Object[] ArrayList.elementData     N/A
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    

    Another possible discrepancy is,
    the first memory consumption table shows for a trimmed ArrayList with 1 element 44 bytes:

    Memory consumption for 1 element
    [...]
    ArrayList 44 bytes (after trimToSize())

    But there is another table with a "Default trimmed ArrayList" comparison column further down,
    showing the size of an ArrayList after adding N elements and calling trimToSize(), and in
    that table we see 48 bytes for 1 element (first column is number of elements added):

    ArrayList of size 2 Linked List Default ArrayList Default trimmed ArrayList
    0 40 32 40 40
    1 48 56 80 48

    I see exactly what he has in this later table, 40 bytes for an empty ArrayList (set to capacity 0), and 48 bytes for an ArrayList of capacity 1 (whether by calling trimToSize() or initializing it to that capacity),
    not the 44 bytes shown in the preceding table for "(after trimToSize())" with 1 element. Can we double check the 44 bytes or explain the difference in configuration?

    For an ArrayList with 1 Integer element after trim to size I see, 48 bytes (64 bytes including 16 bytes for the Integer, or 48 bytes without), would be 44 without alignment bytes, getting 4 bytes added for Object[1] array base offset 16 plus 4 bytes is 20 bytes, adds 4 to align with 24:

    java.util.ArrayList@68837a77d footprint:
         COUNT       AVG       SUM   DESCRIPTION
             1        24        24   [Ljava.lang.Object; [array base offset of 16 plus 4 bytes for a reference with 8 byte alignment gives 24]
             1        16        16   java.lang.Integer
             1        24        24   java.util.ArrayList
             3                  64   (total)
    
    java.util.ArrayList object internals:
    OFF  SZ                 TYPE DESCRIPTION               VALUE
      0   8                      (object header: mark)     N/A
      8   4                      (object header: class)    N/A
     12   4                  int AbstractList.modCount     N/A
     16   4                  int ArrayList.size            N/A
     20   4   java.lang.Object[] ArrayList.elementData     N/A
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    

    Matching the 2nd table in the article, not the 1st, but maybe in the 1st table he left off the 4 bytes added to the raw 20 bytes required by the elementData ([Ljava.lang.Object;) for "Object alignment: 8 bytes" though that seems enforced in the 2nd table?


    Thanks!

  7. JosePaumard commented on Apr 15, 2026

    @JosePaumard
    Contributor

    Thank you Willy, espcially for your work on the memory part.
    Fortunately, this section starts with the following: "as different JVM may handle memory differently, and a single JVM can have different strategies depending on the amount of memory it is running in.". And it's even worse if you add the 32 bits / 64 bits differences to the lot. All the numbers have been carefully been reviewed by Stuart Marks, who wrote List.of() among (many) other things. I think the main idea of this section is: "if memory is an issue for you, be careful, and measure your consumption carefully, in your own production environment".

  8. JosePaumard commented on Apr 15, 2026

    @JosePaumard
    Contributor

    As for the benchmarks, indeed these are microseconds and not milliseconds. I just pushed a fix.
    Again, thank you!

  9. willy-b commented on Apr 15, 2026

    @willy-b
    Author

    Excellent!

    (Though a couple of small items remain even dismissing the few concerns in the last comment about possible inconsistencies in a couple of places on memory numbers (which are definitely highly configuration dependent).)


    I can confirm in the latest version https://web.archive.org/web/20260415205036/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ vs the previous https://web.archive.org/web/20260413142128/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ :

    • Typos reported earlier were fixed

      "more expensive that" should be "more expensive than" [fixed]

      "executed on a hardware" should be "executed on hardware" [fixed]

      "consume" should be "consumes" [fixed]

    • The LinkedList iterator vs index times for iterating though 1000 elements have had the units updated (you are using "us" instead of "μs" but I believe it will be clear in context now that text has been updated to "half a millisecond" and "4 microseconds":

    It takes about half a millisecond to iterate over a linked list of 1000 elements, where it takes only 4 microseconds with an iterator.

    LinkedList        SIZE    Score   Error Units
    Iterate iterator  1000    4.950 ± 0.116 us/op
    Iterate index     1000  584.889 ± 4.396 us/op
    
    Image

    Nit: 4.950 rounds to 5, so "5 microseconds" would better match the table value (4.950 μs) unless you want the floor of the average rather than the closest whole number to the average.

    • The units for the toList then iterate numbers have also been fixed:
    LinkedList           SIZE  ITERATION    Score    Error  Units
    toList then iterate  1000          1    5.182 ±  0.347  us/op
    toList then iterate  1000         10   14.031 ±  0.793  us/op
    toList then iterate  1000        100  100.104 ±  7.422  us/op
    
    Image

    (Note another typo I had not reported yet remains near here:

    In the sentence

    "This has an impact on any reference based data structure: linked list of course, but also trie trees, binary trees, read black trees, skip lists, and to a lesser extent, hash maps."

    I suspect "read black trees" should be "red black trees".)


    However, the units for the ArrayList iterator numbers for a 1000 element ArrayList are not fixed yet:

    ArrayList         SIZE    Score   Error Units
    Iterate iterator  1000    1.447 ± 0.024 ms/op
    Iterate index     1000    1.986 ± 0.045 ms/op
    
    Image

    Thanks very much!

  10. JosePaumard commented on Apr 16, 2026

    @JosePaumard
    Contributor

    As for the us vs. \mu s, this is the convention used when the mu letter is not available, which is often the case with fixed sized fonts.
    As for what should be written 584.889 ± 4.396 us/op. This is what JMH gives, so I prefer to keep it that way. Clearly, it should probably written 585 ± 5, meaning that the result is probably between 570 and 600. But that's a discussion for somewhere else ;)
    Again, thank you for your report! I just pushed a fix for the rest.

  11. willy-b commented on Apr 19, 2026

    @willy-b
    Author

    Thanks!


    In the latest version of the Table of Contents for this series https://dev.java/learn/api/collections-framework/ (archived as is at https://web.archive.org/web/20260418234821/https://dev.java/learn/api/collections-framework/ ):

    • In the section description

    Using a Sortedmap to Sort Key-Value Pairs.

    "Sortedmap" should be "SortedMap".


    In the latest version of https://dev.java/learn/api/collections-framework/intro/ (archived as is at https://web.archive.org/web/20260418235025/https://dev.java/learn/api/collections-framework/intro/ ):

    • In the sentence

    The first thing you need to know is that, from a technical point of view, the Collections Framework is a set of interfaces that models different way of storing data in different types of containers.

    "a set of interfaces that models different way of storing data" should be "a set of interfaces that models different ways of storing data" with "ways" instead of "way".

    • In the sentence

    pushing your object to a queue and popping them?

    It may be worth using add and remove, or offer and poll, instead of push and pop when describing a queue as those verbs are usually used with a stack or deque.
    In fact, there are no methods named push and pop on the Queue interface ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Queue.html ),
    though they are present on Stack (class), Deque (interface), and LinkedList (class; often used for Queues but also Stacks) for example. (This is occurring in a different sentence and context than the place I previously brought this up which was at least comparing queues and stacks so may make more sense in other case to try to share terms than here.)

    • In the sentence

    You may be wondering why you should bother learn the Collection Frameworks when you may have the feeling that putting your data in a good old array does the job.

    "bother learn" should be "bother to learn" or "bother learning".


    In the latest version of https://dev.java/learn/api/collections-framework/organization/ (archived as is at https://web.archive.org/web/20260418235404/https://dev.java/learn/api/collections-framework/organization/ ):

    • In the code sample
    for (String element: collection) {
       // do someting with element
    }
    

    "do someting" should be "do something".

    • In the introduction to list:

    The difference between a List of elements and a Collection of elements, is that a List remembers in what order its elements have been added.
    The first consequence is that if you iterate over the elements of a list, the first element you will get is the first that has been added. Then you will get the second one, and so on until all the elements have been seen. So the order you will iterate over the elements is always the same, it is fixed by the order in which these elements have been added. You do not have this guarantee with a plain Collection nor for a Set.

    This is a little simplified in that since Java 21, List now includes addFirst and addLast, so one can add to the head of the List so the last element added by addFirst will be the first in the encounter order (LIFO instead of FIFO behavior is possible with addFirst instead of add/addLast) of the associated iterator for for-each loops or first index in get(0) etc.


    In the latest version of https://dev.java/learn/api/collections-framework/collection-interface/ (archived as is at https://web.archive.org/web/20260418235121/https://dev.java/learn/api/collections-framework/collection-interface/ ):

    • It is said that "add(element)" method is not allowed to fail for a List (when in fact it is not allowed to return false, but it is allowed to fail via Exceptions)

    add(element): adds an element in the collection. This method returns a boolean in case the operation failed. You saw in the introduction that it should not fail for a List, whereas it may fail for a Set, because a set does not allow duplicates.

    While it is true that Collection.add only allows returning false for items rejected as duplicates, which doesn't happen with List, and that if the method returns List will return true, it is not true that "it should not fail for a List", as per the spec https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/List.html#add(E)

    Lists that support this operation may place limitations on what elements may be added to this list. In particular, some lists will refuse to add null elements, and others will impose restrictions on the type of elements that may be added. List classes should clearly specify in their documentation any restrictions on what elements may be added.

    it is just that instead of returning false, Exceptions are used, e.g.:

    Throws:
    UnsupportedOperationException - if the add operation is not supported by this list
    ClassCastException - if the class of the specified element prevents it from being added to this list
    NullPointerException - if the specified element is null and this list does not permit null elements
    IllegalArgumentException - if some property of this element prevents it from being added to this list

    • Missing period at the end of a sentence:

    A remove may fail, for instance, when the item requested for removal is not present in the collection

    • Maybe worth noting that Collection.contains does allow ClassCastException for checking whether a Collection contains an ineligible object though it is an "optional" Exception and e.g. ArrayList and many others just return false (never a compile-time error)

    You can check for the presence of an element in a collection with the contains() method. Note that you can check the presence of any type of element. For instance, it is valid to check for the presence of a User object in a collection of String. This may seem odd, since there is no chance that this check returns true, but it is allowed by the compiler.

    While it is true there is no compile-time error to call Collection.contains(o) where o instanceof T == false,
    per the Collection specification ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Collection.html#optional-restrictions ) Exceptions can be thrown:

    Some collection implementations have restrictions on the elements that they may contain. For example, some implementations prohibit null elements, and some have restrictions on the types of their elements.
    [...]
    Attempting to query the presence of an ineligible element may throw an exception, or it may simply return false; some implementations will exhibit the former behavior and some will exhibit the latter.

    • In the sentence

    If there is more room in the array than needed, then first of the unused cell of the array will be set to null.

    "first" should be "the first" and "cell" should be "cells".


    In the latest version of https://dev.java/learn/api/collections-framework/lists/ (archived as is at https://web.archive.org/web/20260418235516/https://dev.java/learn/api/collections-framework/lists/ ):

    • In the sentence,

    Calling these methods work only for valid indexes.

    "work" should be "works".


    In the latest version of https://dev.java/learn/api/collections-framework/sets/ (archived as is in
    https://web.archive.org/web/20260418235725/https://dev.java/learn/api/collections-framework/sets/ ):

    • Re:

    The Collections Framework gives you one implementation of SortedSet, called TreeSet.

    There is also ConcurrentSkipListSet ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/concurrent/ConcurrentSkipListSet.html ).
    (Skip list data structures can be used in place of balanced tree data structures even outside the context of designing for concurrency but ConcurrentSkipListSet is the SortedSet supported by the Java API for concurrent applications.)

    • The sentence

    ceiling(element), and floor(element) return the greatest element lesser or equal than, or the lowest element greater or equal than the provided element.

    might be clearer if the descriptions were in the same order as the methods being described. "ceiling(element)" returns "the lowest element greater or equal [to] the provided element"
    but "ceiling" comes before "floor" while its description comes after "floor"s description ("the greatest element lesser or equal [to]").


    In the latest version https://dev.java/learn/api/collections-framework/maps/ (archived as is at
    https://web.archive.org/web/20260419000052/https://dev.java/learn/api/collections-framework/maps/ ):

    • In the sentence

    This implementation is not meant to be generally used in application.

    "application" should be "applications".

    • Re:

    The relationship between a key and its bound value follows these two simple rules.
    A key can be bound to only one value
    A value can be bound to several keys.
    [...]

    There is a missing period after value, and several other entries in the subsequent list after "This leads to several consequences for the content of the map.", "you can define the following operations on a map", "You can also define the classic, set-like operations:".

    • In the sentence

    You will see this later on an example.

    "on" should probably be "in".


    In the latest version of https://dev.java/learn/api/collections-framework/working-with-keys-and-values/ (archived as is at https://web.archive.org/web/20260419000633/https://dev.java/learn/api/collections-framework/working-with-keys-and-values/ ):

    • In the sentence

    It will produce the following resut:

    "resut" should be "result".


    In the latest version of https://dev.java/learn/api/collections-framework/maps-and-lambdas/ (archived as is at https://web.archive.org/web/20260419000654/https://dev.java/learn/api/collections-framework/maps-and-lambdas/ ):

    • In the sentences

    If you need finer control, then you can use an overload of this method, which takes the existing value as an argument: replace(key, existingValue, newValue). In this case, the existing value is replaced only if it matches the new value.

    First "existingValue" should probably renamed to be "oldValue" to match the documentation,
    then "the existing value is replaced only if it matches the new value" should be "the current value is only replaced with he newValue argument if the current value matches the oldValue argument" or something similar as the existing value in the map is not compared to he newValue argument (the check gating the update is map.containsKey(key) && Objects.equals(map.get(key), oldValue)).

    • In the sentence

    For computeIfAbsent(), beacause there is no value bound to that key, the remapping function is in fact a simple Function that takes the key as an argument.

    "beacause" should be "because".


    In the latest version of https://dev.java/learn/api/collections-framework/sorted-maps/ (archived as is at https://web.archive.org/web/20260419000557/https://dev.java/learn/api/collections-framework/sorted-maps/ ):

    • In the description of the SortedMap interface, it says

    The SortedMap interface adds the following methods to Map:
    > firstKey() and lastKey(): returns the lowest and the greatest key of your map;
    [...]

    Then in describing the NavigableMap interface below, it says:

    The NavigableMap adds more methods to SortedMap. [...]
    > firstKey(), firstEntry(), lastEntry(), and lastKey(): return the lowest or greatest key or entry from this map.

    That is, firstKey() , lastKey() which are part of the SortedMap interface are introduced again in the subinterface NavigableMap (only firstEntry(), lastEntry(), should be mentioned here; this article has pollFirstEntry(), pollLastEntry() in a separate section )
    which is unnecessary and contrary to the way that the documentation describes it (firstKey, lastKey are not mentioned as updated in any way unlike some other methods see https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/NavigableMap.html ).


    In the latest version of https://dev.java/learn/api/collections-framework/choosing-keys/ (archived as is at https://web.archive.org/web/20260419000855/https://dev.java/learn/api/collections-framework/choosing-keys/ ):

    • In the sentence

    Using mutable key is an antipattern, and you should definitely avoid doing that.

    "key" should be "keys".


    Thanks very much!

  12. JosePaumard commented on Apr 20, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! I just pushed a fix.
    As for the Queue operations: the method names were chosen to avoid name collisions, but the operations are still push pop peek. So I prefer to keep in that way.
    As for the List: operations that apply to the first or last element come from SequencedCollection. It turns out that List can trivialy have these operations, but there is still a difference between both.
    As for the ConcurrentSkip structures, there are outside of the scope of this section. All these structures are super complex, and would require a section on their own to explain them. And using them outside of a concurrent environment would be a mistake.
    And double checked that the fixes were pushed ;)

  13. willy-b commented on Apr 21, 2026

    @willy-b
    Author

    Thanks, I reply to your latest updates below.

    No pressure from me to include me on any related pull requests (not all content on dev.java is even visible to me on GitHub as I am external to the project so I know that is not possible in all cases) but using those features may simplify the reviewing process; also at the end of the ticket I will go through and advise updating the "Last update" timestamps on all pages your team has pushed edits to (your team has not been updating "Last update" dates when modifying pages), but that is out of my control as I am not actually making edits on this project.


    In the latest version of https://dev.java/learn/api/collections-framework/sets/ (archived as is at https://web.archive.org/web/20260420150334/https://dev.java/learn/api/collections-framework/sets/ ),

    there has not been a fix since my last comment and review of the page (archived previously at https://web.archive.org/web/20260418235725/https://dev.java/learn/api/collections-framework/sets/ ).

    I previously commented:

    The sentence

    ceiling(element), and floor(element) return the greatest element lesser or equal than, or the lowest element greater or equal than the provided element.

    might be clearer if the descriptions were in the same order as the methods being described.

    The above has not been updated in https://web.archive.org/web/20260420150334/https://dev.java/learn/api/collections-framework/sets/ vs https://web.archive.org/web/20260418235725/https://dev.java/learn/api/collections-framework/sets/ .

    That is, the descriptions should be: [adding (A,B) and (1,2) labels to the parts to discuss the ordering afterwards]

    (A) "ceiling(element)" returns (1) "the lowest element greater or equal [to] the provided element" (yes, "lowest" occurs in ceiling as it is lowest WHILE BEING greater than or equal to)

    (B) "floor(element)" returns (2) "the greatest element lesser [than or] equal [to]" (yes, "greatest" occurs in floor as it is greatest WHILE BEING less than or equal to)

    But the current sentence still has the ordering "(A) and (B) return (2) or (1)" instead of "(A) and (B) return (1) and (2), respectively". (Alternatively the order of ceiling and floor could be switched as in "(B) and (A) return (2) and (1), respectively" while keeping the order of descriptions.).

    Image

    Re: https://dev.java/learn/api/collections-framework/maps-and-lambdas/ (archived as is at https://web.archive.org/web/20260420151449/https://dev.java/learn/api/collections-framework/maps-and-lambdas/ ),

    I had commented on the previous version https://web.archive.org/web/20260419000654/https://dev.java/learn/api/collections-framework/maps-and-lambdas/ :

    In the sentences

    If you need finer control, then you can use an overload of this method, which takes the existing value as an argument: replace(key, existingValue, newValue). In this case, the existing value is replaced only if it matches the new value.

    First, "existingValue" should probably renamed to be "oldValue" to match the documentation,
    then "the existing value is replaced only if it matches the new value" should be "the current value is only replaced with he newValue argument if the current value matches the oldValue argument" or something similar as the existing value in the map is not compared to he newValue argument (the check gating the update is map.containsKey(key) && Objects.equals(map.get(key), oldValue) ).

    This has been updated in the latest to (archived as it is at https://web.archive.org/web/20260420151449/https://dev.java/learn/api/collections-framework/maps-and-lambdas/ ):

    If you need finer control, then you can use an overload of this method, which takes the existing value as an argument: replace(key, existingValue, newValue). In this case, the existing value is replaced with newValue only if it matches the first argument.

    This is better but it says the "first argument" when it should be the "second argument" (out of 3, first is the key) or "first value argument" (as the 3 arguments are key, oldValue, newValue), and "existingValue" should probably still be renamed "oldValue" to match the official documentation.


    Re: https://dev.java/learn/api/collections-framework/collection-interface/ (archived as it is now at https://web.archive.org/web/20260420150152/https://dev.java/learn/api/collections-framework/collection-interface/ )

    I had commented on the previous version (archived at https://web.archive.org/web/20260418235121/https://dev.java/learn/api/collections-framework/collection-interface/ ), see last comment from me in this ticket, repeated here for clarity:

    It is said that "add(element)" method is not allowed to fail for a List (when in fact it is not allowed to return false, but it is allowed to fail via Exceptions)

    add(element): adds an element in the collection. This method returns a boolean in case the operation failed. You saw in the introduction that it should not fail for a List, whereas it may fail for a Set, because a set does not allow duplicates.

    While it is true that Collection.add only allows returning false for items rejected as duplicates, which doesn't happen with List, and that if the method returns List will return true, it is not true that "it should not fail for a List", as per the spec https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/List.html#add(E)

    Lists that support this operation may place limitations on what elements may be added to this list. In particular, some lists will refuse to add null elements, and others will impose restrictions on the type of elements that may be added. List classes should clearly specify in their documentation any restrictions on what elements may be added.

    it is just that instead of returning false, Exceptions are used, e.g.:

    Throws:
    UnsupportedOperationException - if the add operation is not supported by this list
    ClassCastException - if the class of the specified element prevents it from being added to this list
    NullPointerException - if the specified element is null and this list does not permit null elements
    IllegalArgumentException - if some property of this element prevents it from being added to this list

    Now, in the latest version, https://web.archive.org/web/20260420150152/https://dev.java/learn/api/collections-framework/collection-interface/ ,

    this has been updated to:

    add(element): adds an element in the collection. This method returns a boolean in case the operation failed. A specific List implementation may set restrictions on the elements it allows, for instance, it could choose to refuse nulls

    But this is not quite right.

    Per the Oracle official javadoc: ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Collection.html#add(E) )

    Returns true if this collection changed as a result of the call. (Returns false if this collection does not permit duplicates and already contains the specified element.)

    [...]

    If a collection refuses to add a particular element for any reason other than that it already contains the element, it must throw an exception (rather than returning false). This preserves the invariant that a collection always contains the specified element after this call returns.

    So "This method returns a boolean in case the operation failed" is not right. Implementations must throw an exception for most failures to add an element (all of those except where the element is already in the collection, as e.g. attempting to add a duplicate to a Set).

    So maybe this could be updated to:

    "add(element): adds an element in the collection. This method returns a boolean which is true if the collection changed as a result of the call (calling add on a Set which already contains the element would return false). If the call returns the collection MUST contain the specified element. Thus while collections may place other restrictions on elements added they must throw exceptions rather than return false if refusing to add an element for any reason other than that the collection already contains the element. For example, a specific List implementation may set restrictions on the elements it allows, for instance, it could choose to refuse nulls, but would refuse those with a NullPointerException instead of returning false."

    Separately in https://web.archive.org/web/20260420150152/https://dev.java/learn/api/collections-framework/collection-interface/ ,
    below

    You can check for the presence of an element in a collection with the contains() method. Note that you can check the presence of any type of element.

    the following line has been added by your team to address one of my previous comments (that using contains with the wrong type can still fail but with an exception not a compile-time error):

    Note that the implementation may choose to throw a ClassCastException

    Which is great and now just needs a period at the end (this version has no period after this sentence before the next text).

    (There are still bulleted lists I commented on across pages in this tutorial that have inconsistent use of periods at the end of each bullet, but will not repeat that here as not likely to confuse anyone.)


    Thanks!

  14. 23 remaining items

  15. willy-b commented on Jun 23, 2026

    @willy-b
    Author

    Thanks!


    Re: above I had asked

    is the recommendation of a LinkedList for a Stack or Queue implementation aligned with the JDK documentation recommendation of an ArrayDeque?

    https://web.archive.org/web/20260531223132/https://dev.java/learn/api/collections-framework/lists/
    So if your application needs a Last In, First Out (LIFO, covered later in this tutorial) stack, or a First In, First Out (FIFO, also covered later) waiting queue, then choosing a linked list is probably your best choice.

    should the JDK recommended ArrayDeque be at least mentioned here if not recommended as the best option? As the JDK official documentation puts it: ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/ArrayDeque.html )

    This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue.
    Most ArrayDeque operations run in amortized constant time.

    and you just responded:

    As for LinkedList vs. ArrayDeque: it's hard for the JDK to make any recommendation, because it is hardware dependent. The fact that LinkedList became something you should almost always avoid vs. ArrayList comes from cache lines and cache misses. It was not the case in Java 2 when LinkedList was designed, and may change again in the future. But then, what about the applications running on very low end hardware, that have a different internal architecture?

    Thanks, but there are many places in the existing articles where hardware dependent recommendations in favor of ArrayList are made, e.g. (https://web.archive.org/web/20260622182311/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ )

    As you can see, ArrayList outperforms LinkedList for all the operations but one[, adding at the beginning of the list]. This can be unexpected, because from an algorithm point of view, LinkedList compares better, especially for the insertion operation. But because this efficient algorithm is executed on hardware that makes pointer chasing very costly, this overhead becomes dominant and makes it inefficient.
    [...]
    ArrayList is a good implementation, that outperforms LinkedList in almost all the classical list operations, at least when what you need is a regular list. Even if the LinkedList implementation is implemented with better algorithms, it is a pointer based structure, that suffers from pointer chasing. So iterating is costly, getting an element by its index is costly, which has an impact on all the classical operations: inserting, replacing, deleting. Even if these operations by themselves cost less on LinkedList than on ArrayList, the performance is killed by the fact that accessing the right element is very expensive, due to pointer chasing.

    but we are still not having the JDK documentation recommendation of ArrayDeque over LinkedList even for queues coming in (nor any mention of ArrayDeque as a circular array growing from the middle out meaning adding at the head as well as tail is very fast except infrequently when the array is resized but that is amortized by scaling by a factor), e.g. the page also says

    LinkedList are very good stack or queue implementations. When it comes to regular lists, not so good. They are almost always outperformed by ArrayList.

    When the JDK documentation says ( https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/ArrayDeque.html )

    This class is likely to be faster than Stack when used as a stack, and faster than LinkedList when used as a queue.
    Most ArrayDeque operations run in amortized constant time.

    Feel free to disagree, but I do maintain it would be consistent with the current approach (and the JDK documentation) to recommend ArrayDeque on pages where it is not mentioned (it is already mentioned once on the page on ArrayList vs LinkedList, but not on https://dev.java/learn/api/collections-framework/lists/ (archived as is at https://web.archive.org/web/20260531223132/https://dev.java/learn/api/collections-framework/lists/ )) where it says that even for a queue a LinkedList is the best choice, directly contradicting the ArrayDeque documentation quoted above:

    So if your application needs a Last In, First Out (LIFO, covered later in this tutorial) stack, or a First In, First Out (FIFO, also covered later) waiting queue, then choosing a linked list is probably your best choice.


    That aside, here's another round of items in this tutorial series found during additional review, thanks so much for considering whether to fix them!


    In the latest version of https://dev.java/learn/api/collections-framework/maps-and-lambdas/ (archived as is at https://web.archive.org/web/20260622050455/https://dev.java/learn/api/collections-framework/maps-and-lambdas/ )

    The three methods mentioned are:

    • default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
    • default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
    • default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

    The second point of this description seems inaccurate: compute() and computeIfPresent() do not take as an argument "the value bound to that key".
    What is true is that compute() and computeIfPresent() take a BiFunction as an argument, and that BiFunction takes a value as an argument, which is not the same thing.
    So I would suggest "- the value bound to that key, in the case of compute() and computeIfPresent()" be removed.
    It is already explained below the 3rd current point that for compute() and computeIfPresent() , the BiFunction passed as a 2nd argument to these methods, itself takes the mapped value for a given key as a 2nd argument (to the BiFunction).

    Also in the above "or a mapping function in the case of computeIfAbsent()" should be "or a mapping Function in the case of computeIfAbsent()".


    In the latest version of https://dev.java/learn/api/collections-framework/collection-interface/ (archived as is at https://web.archive.org/web/20260621200029/https://dev.java/learn/api/collections-framework/collection-interface/ )

    • In the sentence

      The add(element) method gives the exceptions you shoud throw.

      "shoud" ought to be "should" .

    • In the sentence

      Then the last batch of methods deal with the collection itself.

      "methods deal" seems right due to proximity, but it may be that "last batch ... deals" is appropriate since the "last batch of methods" is a singular subject, despite "of methods" (it is a single batch).

    • In the sentence

      If there is more room in the array than needed, then first of the unused cell of the array will be set to null.

      "then first of the unused cell of the array will be set to null" should be "the first unused cell of the array will be set to null" OR "the first of the unused cells of the array will be set to null" (either replace "first of the" with "first" OR replace "cell" with "cells").

    • In the sentence

      If you decide to use the Collection interface to model the collections in your application, then choosing ArrayList as you default implementation is your best choice, most of the time.

      "choosing ArrayList as you default implementation" should be "choosing ArrayList as your default implementation" ("you" should be "your" in this context).


    In the latest version of https://dev.java/learn/api/collections-framework/sets/ (archived as is at https://web.archive.org/web/20260621212321/https://dev.java/learn/api/collections-framework/sets/ )


    In the latest version of https://dev.java/learn/api/collections-framework/organization/ (archived as is at https://web.archive.org/web/20260621185555/https://dev.java/learn/api/collections-framework/organization/ )

    • In the heading (in the text, and on the sidebar)

      Sorting the element of a Set with SortedSet and NavigableSet

      "element" should be "elements" .


    In the latest version of https://dev.java/learn/api/collections-framework/stacks-queues/ (archived as is at https://web.archive.org/web/20260621222720/https://dev.java/learn/api/collections-framework/stacks-queues/ ) ,

    • In the sentence

      These structures are very simple and gives you three main operations.

      "and gives you" should be "and give you" .

    • In the sentence

      Inserting objects that do no implement Comparable will throw a ClassCastException.

      "that do no implement" should be "that do not implement", in that "no" should be "not".


    In the latest version of https://dev.java/learn/api/collections-framework/working-with-keys-and-values/ (archived as is https://web.archive.org/web/20260622004402/https://dev.java/learn/api/collections-framework/working-with-keys-and-values/ )

    • In the following sentences

      You can also get different sets defined on a map.

      • keySet(): returns an instance of Set, containing the keys defined in the map
      • entrySet(): returns an instance of Set<Map.Entry>, containing the key/value pairs contained in the map
      • values(): returns an instance of Collection, containing the values present in the map.

      "You can also get different sets" should probably be "You can also get different Collections" as "values(): returns an instance of Collection" not a Set, though the other two return Sets ( because Sets are Collections it is ok to refer to the first two cases as Collection, but not to refer to the third case as a Set).


    In the latest version of https://dev.java/learn/api/collections-framework/sorted-maps/ (archived as is at https://web.archive.org/web/20260622174346/https://dev.java/learn/api/collections-framework/sorted-maps/ )

    • In the sentences

      The JDK provides two extensions of the Map interface: SortedMap and NavigableMap. NavigableMap is an extension of SortedMap. Both interfaces are implemented by the same class: TreeMap. The TreeMap class is a red-black tree, a well-known data structure.

      one can get the impression that TreeMap is the sole implementation provided by the Java Collections Framework, when there is also ConcurrentSkipListMap available which implements both interfaces. It may be worth noting this in passing; skip lists are not just for concurrent situations either. Since there are only two implementations provided in the Java Collections Framework, it may be worth updating this to mention both (it is not a long list), before continuing to focus on TreeMap which is the most commonly used. Or, one could only mention TreeMap but avoid giving the impression it is the one and only by saying something like "A class provided by the Java Collections Framework that implements both interfaces is TreeMap ."


    In the latest version of https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ (archived as is at https://web.archive.org/web/20260622182311/https://dev.java/learn/api/collections-framework/arraylist-vs-linkedlist/ )

    • In the sentence

      In the bench we just ran, the linked list is created with the following code. It is created with a stream.

      "bench" should be "benchmark".

    • In the sentences

      You cannot add an element in an array that is full.
      What the ArrayList implementation does in that case, is that it copies the full array in a larger array, and then adds your element.
      The size of the new array is computed from the size of the current array, and grows with a factor of 1.5.

      It may be dangerous to tell readers that the policy for growing an ArrayList is to multiply by 1.5x, as per the documentation:

      As elements are added to an ArrayList, its capacity grows automatically.
      The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.

      (from https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/ArrayList.html , bold added )
      To achieve a constant amortized time cost, ArrayList could scale by any factor, not just 1.5, or perhaps implement a more complicated growth policy.
      It does CURRENTLY try to scale by 1.5x in OpenJDK (the specification gives them the freedom to change this), but it has a more complicated policy as one approaches the limit on the maximum length of an array.

    • In the sentence

      The fact that the node are distributed randomly in your heap memory has no impact.

      "the node" should be "the nodes".

    • In the sentence

      On the overall, it is wrong to think that the price of a reallocation is a relevant argument to prefer LinkedList over ArrayList.

      "On the overall," should be "Overall,".

    • In the sentence

      This one is a trick question, as different JVM may handle memory differently, and a single JVM can have different strategies depending on the amount of memory it is running in.

      "as different JVM may handle memory differently" should be "as different JVMs may handle memory differently" ("JVM" should be updated to "JVMs" BUT in the first occurrence only).

    • In the sentence

      ArrayList reallocation is a quite costly operation, but that is used very rarely, as that you can avoid if you know how many elements you need to store in your list.

      "as that you can avoid if" should be "and you can avoid this if".


    Thanks very much!

  16. JosePaumard commented on Jun 25, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.
    As for the ArrayDeque discussion. ArrayDeque is mentionned in the next sentence after the one you quote ("So if your application needs a Last In, First Out..."). Also keep in mind that AD cannot be a drop-in replacement of LL, since it does not accept null values, which is also mentionned on this page.
    The page you quote (ArrayList vs. LinkedList) is a performance study (CPU and Memory wise) of both implementation, as List implementations. Reason why ArrayDeque is not on this picture.
    The JDK JavaDoc mentions Stack, which is an old class from JDK 1 that should not be used anymore, and that ArrayDeque that may outperform LinkedList on Queue / Stack operations. A closer look shows that LinkedList is faster on peek, and slower on push and pop. But that would need a much more in depth comparison. Maybe some work for later.

  17. willy-b commented on Jun 29, 2026

    @willy-b
    Author

    Excellent!

    Here are some more items in this tutorial series I have found during additional review that the dev.java team may be interested to fix.


    In the latest version of https://dev.java/learn/api/collections-framework/collection-interface/ (archived as is at https://web.archive.org/web/20260628211938/https://dev.java/learn/api/collections-framework/collection-interface/ ),


    In the latest version of https://dev.java/learn/api/collections-framework/lists/ (archived as is at https://web.archive.org/web/20260629000909/https://dev.java/learn/api/collections-framework/lists/ )

    • In the description of methods added in the ListIterator interface

      The ListIterator interface extends the regular Iterator that you already know. It adds several methods to it.

      ListIterator's add(element) is not described (though set(element) is), which seems like an unintentional omission (should be added).

    • In the sentence

      If the element you are searching is present several times in the list, then you can to use the following pattern to find all of them.

      "you can to use" should be "you can use"

    • In the code snippet under "Reversing a List", there is a typo:

      IO.prinln("Adding an apricot at index 2");

      Obviously "IO.prinln" was intended by the dev.java team to be "IO.println".


    In the latest version of https://dev.java/learn/api/collections-framework/intro/ (archived as is at https://web.archive.org/web/20260628175138/https://dev.java/learn/api/collections-framework/intro/?no_cache=true )

    • In the sentence

      You will learn in this part are the most useful data structures the Collections Framework has to offer, along with the patterns you will be using to manipulate this data in your application.

      "You will learn in this part are the most useful data structures" is not grammatical. There are multiple ways to fix it. One way to rephrase it might be "What you will learn in this part are the most useful data structures."
      Or the word "are" could be removed for "You will learn, in this part, the most useful data structures" (also adding commas around "in this part").

    • In the sentence

      HashTable: This class has been retrofitted to implement the Map interface.

      "HashTable" should be "Hashtable" to match the official capitalization in the JDK (unlike HashMap, which capitalizes Map, Hashtable does NOT capitalize table).

    • In the sentence

      The fact is, in any case, if you have a solution that is simple, that you master well, and that fits your needs, then you should definitely stick with it!

      "that you master well" seems like an unusual phrasing, so you might double check whether or not this was intended to be "that you have mastered well".

    Image

    In the latest version of https://dev.java/learn/api/collections-framework/organization/ (archived as is at https://web.archive.org/web/20260628144813/https://dev.java/learn/api/collections-framework/organization/ )

    • In the sentence

      You may be already familiar with this way of iterating over the elements of a Collection:

      "may be already familiar" seems much less frequently used than "may already be familiar", so you may wish to change this.

    Image
    • In the sentences

      The SortedSet adds several operations to Set. Here is what you can do with a SortedSet.

      • You can get the lowest element, and the largest element of the set
      • You can extract a headSet and a tailSet of all the elements lesser than, or greater than a given element.

      "You can get the lowest element" can mention first() (and since Java 21 also getFirst() ),
      "and [you can get] the largest element" can mention last() (and since Java 21 also getLast() ),
      "or greater than" should be "or greater than or equal to" per the specification for tailSet .

      Finally, "lesser than" would probably be clearer as "less than" ("lesser than" is not typical, see Google ngrams below)

    Image

    In the latest version of https://dev.java/learn/api/collections-framework/sets/ (archived as is at https://web.archive.org/web/20260629004135/https://dev.java/learn/api/collections-framework/sets/ )

    • It's a tiny thing but in the sentences

      A TreeSet is an implementation that needs to compare the objects it contains.
      You can provide a comparator when you build a TreeSet,
      or you implement the Comparable interface for the elements you put in the TreeSet.
      If you do both, then the Comparator takes precedence.

      It seems unintentional that the last occurrence of "TreeSet" is not linked to https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/TreeSet.html like the other two occurrences.
      Similarly, the 2nd occurrence of "Comparator" is hyperlinked, but not the first (so the dev.java team may want to make both hyperlinked to be consistent).

    • In the sentences

      first() and last() returns the lowest and the largest elements of the set
      headSet(toElement) and tailSet(fromElement) returns you subsets containing the elements lower than toElement or greater than or equal to fromElement

      "returns" should be "return" because the subjects are plural (2 methods each).

    • In the sentence

      lower(element), and higher(element) return the greater element lesser than, or the lowest element greater than the provided element.

      "greater element" should be "greatest element", and
      "lesser than" would probably be clearer as "less than" ("lesser than" is not typical, see Google ngrams below)

    Image
    • In the sentence

      pollFirst(), and pollLast() return and removes the lowest or the greatest element of the set.

      "return and removes" should be "return and remove"


    In the latest version of https://dev.java/learn/api/collections-framework/factory-methods/ (archived as is https://web.archive.org/web/20260629013833/https://dev.java/learn/api/collections-framework/factory-methods/ )
    In the dev.java text

    Wrapping a Collection in an Immutable Collection
    The Collections factory class gives you several methods to create immutable wrappers for your collections or maps. The content of the structure is not duplicated; what you get is a wrapper around your structure.
    All the attempts to modify it will raise exceptions.
    All these methods start with unmodifiable, followed by the name of the type of your structure.
    For instance, to create an immutable wrapper of a list, you can call:

    List<String> strings = Arrays.asList("0", "1", "2", "3", "4");
    List<String> immutableStrings = Collections.unmodifiableList(strings);
    

    Just a word of warning: it is not possible to modify your collection through this wrapper. But this wrapper is backed by your collection, so if you modify it by another means, this modification will be reflected in the immutable collection. Let us see that in the following code:

    List<String> strings = new ArrayList<>(Arrays.asList("0", "1", "2", "3", "4"));
    List<String> immutableStrings = Collections.unmodifiableList(strings);
    IO.println(immutableStrings);
    strings.add("5");
    IO.println(immutableStrings);
    

    There appears to be potential confusion between the concepts of umodifiable and immutable as defined in the Collections Framework documentation (see https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/doc-files/coll-overview.html ).

    Per the JDK documentation (from https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/doc-files/coll-overview.html , archived as is at https://web.archive.org/web/20260629140150/https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/doc-files/coll-overview.html )
    > Collections that do not support modification operations (such as add, remove and clear) are referred to as unmodifiable. [...]
    > Collections that additionally guarantee that no change in the Collection object will be visible are referred to as immutable. [...]

    and (from https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Collection.html#unmodview , archived as is at https://web.archive.org/web/20260629140205/https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Collection.html#unmodview )

    [C]hanges to the backing collection might still be possible, and if they occur, they are visible through the unmodifiable view.
    Thus, an unmodifiable view collection is not necessarily immutable.

    So the cited dev.java text (under the current heading "Wrapping a Collection in an Immutable Collection") should use "unmodifiable" instead of "immutable" (like the JDK does) in almost all places when referring to the output of the Collections methods labeled "unmodifiable" (consistent with the framework itself).
    Thus e.g. in the following sentence in the dev.java article https://web.archive.org/web/20260629013833/https://dev.java/learn/api/collections-framework/factory-methods/

    [T]his wrapper is backed by your collection, so if you modify it by another means, this modification will be reflected in the immutable collection.

    in "immutable collection", "collection" should be "wrapper" (or "wrapper collection" or "view collection", as the framework uses "view" instead of "wrapper" in these cases) and "immutable" should be "unmodifiable". The wrapper is an unmodifiable Collection, so you cannot mutate through it, but if it is a view onto a modifiable Collection which is modified directly elsewhere, then the unmodifiable view/wrapper Collection is NOT immutable, only unmodifiable, which is why it changes.

    And the heading

    Wrapping a Collection in an Immutable Collection

    should be updated to "Wrapping a Collection in an Unmodifiable Collection".

    Some places are correct and do not need to change, e.g.:

    If you plan to create an immutable collection using this pattern, defensively copying it first may be a safe precaution.

    is a correct use of "immutable".

    But especially in the code example where the unmodifiableList viewing a mutable Collection is being updated,

    List<String> strings = new ArrayList<>(Arrays.asList("0", "1", "2", "3", "4"));
    List<String> immutableStrings = Collections.unmodifiableList(strings);
    IO.println(immutableStrings);
    strings.add("5");
    IO.println(immutableStrings);
    

    "immutableStrings" should be "unmodifiableStrings" or "unmodifiableViewOfStrings".


    Thanks very much for all the excellent content! It is appreciated, as is the opportunity to give back by reporting potential issues I find while reviewing it.

  18. JosePaumard commented on Jun 30, 2026

    @JosePaumard
    Contributor

    Thanks again for your report! A fix is on its way.
    As for the immutable / unmodifable thing. I removed all reference to immutable, as this is a never-ending debate.

  19. willy-b commented on Jul 2, 2026

    @willy-b
    Author

    Thanks so much!

    Unfortunately, I see what appear to be a few regressions in the dev.java team changes made in response to my comment above ( #206 (comment) ).

    Here I reproduce the relevant parts of my comment with comments inline about what was not changed as expected
    or where errors appear to have been introduced

    (tl;dr dev.java team's new description of a nonexistent "addElement(element)" method on ListIterator instead of "add(element)" as requested for https://dev.java/learn/api/collections-framework/lists/ and e.g. the new sidebar-heading mismatch of "Unmodifiable" in the text and "Imutable" also with one "m" in the sidebar on https://dev.java/learn/api/collections-framework/factory-methods/ ; though the sidebar typo predates this ticket (see archived version at the old URL https://web.archive.org/web/20250805165813/https://dev.java/learn/api/collections-framework/immutable-collections ), also I did not recommend that heading to be changed).


    [Parts not in "[", "]" are from my earlier comment https://github.com/java/devjava-content/issues/206#issuecomment-4834074228]

    In the [previously reported] version of https://dev.java/learn/api/collections-framework/lists/ (archived as [it was] at https://web.archive.org/web/20260629000909/https://dev.java/learn/api/collections-framework/lists/ )

    • In the description of methods added in the ListIterator interface

    The ListIterator interface extends the regular Iterator that you already know. It adds several methods to it.
    hasPrevious() and previous(): to iterate in the descending order rather than the ascending order
    nextIndex() and previousIndex(): to get the index of the element that will be returned by the next next() call, or the next previous() call
    set(element): to update the last element returned by next() or previous(). If neither of these methods have been called on this iterator then an IllegalStateException is raised.

    • ListIterator's add(element) is not described (though set(element) is), which seems like an unintentional omission (should be added).
      [update: there was unfortunately a mistake added by the dev.java team in https://web.archive.org/web/20260701135613/https://dev.java/learn/api/collections-framework/lists/ here:

    addElement(element): to insert the given element in the list. It is inserted right before the element returned by the next call to next(), if any, and after the element returned by a call to previous(), if any. If the list is empty, then the element is simply added to this list. Note that a subsequent call to next() is unaffected, and a subsequent call to previous() will return the inserted element.

    the name of the method is "add" not "addElement" so it should be "add(element)" (as I had written in my comment) NOT "addElement(element)" .
    Also the dev.java team may mention that "This call increases by one the value that would be returned by a call to nextIndex or previousIndex."]


    [Parts not in "[", "]" are from my earlier comment https://github.com/java/devjava-content/issues/206#issuecomment-4834074228]

    In the [previously reported] version of https://dev.java/learn/api/collections-framework/factory-methods/ (archived as [it was] https://web.archive.org/web/20260629013833/https://dev.java/learn/api/collections-framework/factory-methods/ )
    In the dev.java text

    Wrapping a Collection in an Immutable Collection
    The Collections factory class gives you several methods to create immutable wrappers for your collections or maps. The content of the structure is not duplicated; what you get is a wrapper around your structure.
    All the attempts to modify it will raise exceptions.
    All these methods start with unmodifiable, followed by the name of the type of your structure.
    For instance, to create an immutable wrapper of a list, you can call:

    List<String> strings = Arrays.asList("0", "1", "2", "3", "4");
    List<String> immutableStrings = Collections.unmodifiableList(strings);
    

    Just a word of warning: it is not possible to modify your collection through this wrapper. But this wrapper is backed by your collection, so if you modify it by another means, this modification will be reflected in the immutable collection. Let us see that in the following code:

    List<String> strings = new ArrayList<>(Arrays.asList("0", "1", "2", "3", "4"));
    List<String> immutableStrings = Collections.unmodifiableList(strings);
    IO.println(immutableStrings);
    strings.add("5");
    IO.println(immutableStrings);
    

    There appears to be potential confusion between the concepts of unmodifiable and immutable as defined in the Collections Framework documentation (see https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/doc-files/coll-overview.html ).

    Per the JDK documentation (from https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/doc-files/coll-overview.html , archived as is at https://web.archive.org/web/20260629140150/https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/doc-files/coll-overview.html )

    Collections that do not support modification operations (such as add, remove and clear) are referred to as unmodifiable. [...]
    Collections that additionally guarantee that no change in the Collection object will be visible are referred to as immutable. [...]

    and (from https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Collection.html#unmodview , archived as is at https://web.archive.org/web/20260629140205/https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/Collection.html#unmodview )

    [C]hanges to the backing collection might still be possible, and if they occur, they are visible through the unmodifiable view.
    Thus, an unmodifiable view collection is not necessarily immutable.

    So the cited dev.java text (under the current heading "Wrapping a Collection in an Immutable Collection") should use "unmodifiable" instead of "immutable" (like the JDK does) in almost all places when referring to the output of the Collections methods labeled "unmodifiable" (consistent with the framework itself).
    Thus e.g. in the following sentence in the dev.java article https://web.archive.org/web/20260629013833/https://dev.java/learn/api/collections-framework/factory-methods/

    [T]his wrapper is backed by your collection, so if you modify it by another means, this modification will be reflected in the immutable collection.

    in "immutable collection", "collection" should be "wrapper" (or "wrapper collection" or "view collection", as the framework uses "view" instead of "wrapper" in these cases) and "immutable" should be "unmodifiable". The wrapper is an unmodifiable Collection, so you cannot mutate through it, but if it is a view onto a modifiable Collection which is modified directly elsewhere, then the unmodifiable view/wrapper Collection is NOT immutable, only unmodifiable, which is why it changes.
    [confirmed fixed by dev.java team in a slightly different way in https://web.archive.org/web/20260701133510/https://dev.java/learn/api/collections-framework/factory-methods/ ]

    And the heading

    Wrapping a Collection in an Immutable Collection

    should be updated to "Wrapping a Collection in an Unmodifiable Collection".
    [specific item here was fixed by the dev.java team in https://web.archive.org/web/20260701133510/https://dev.java/learn/api/collections-framework/factory-methods/ ,

    BUT other headings were updated which can remain as Immutable (I only recommended that "Wrapping a Collection in an Immutable Collection" be updated, but the others have been updated by the dev.java as well),
    and the first heading, which can (1) stay Immutable is also (2) in any case now mismatched between the text ("Unmodifiable") and the sidebar ("Imutable"[sic, typo by dev.java team]).
    Note the typo in the sidebar is old, and was not introduced during this ticket: see the archived version at the old URL https://web.archive.org/web/20250805165813/https://dev.java/learn/api/collections-framework/immutable-collections .]

    Some places are correct and do not need to change, e.g.:

    If you plan to create an immutable collection using this pattern, defensively copying it first may be a safe precaution.

    is a correct use of "immutable".
    [I disagree with the change that was made here, as this was replaced with "unmodifiable" in https://web.archive.org/web/20260701133510/https://dev.java/learn/api/collections-framework/factory-methods/ , I don't think it makes sense to say "unmodifiable" here as defensive copying is to ensure immutability ; the view is indeed unmodifiable per the Collections Framework definition of not allowing mutation operations on that object, but shows changes in the backing collection (not immutable). It would be immutable if the backing collection were copied before being wrapped with the reference to the mutable backing collection discarded or protected .]

    But especially in the code example where the unmodifiableList viewing a mutable Collection is being updated,

    List strings = new ArrayList<>(Arrays.asList("0", "1", "2", "3", "4"));
    List immutableStrings = Collections.unmodifiableList(strings);
    IO.println(immutableStrings);
    strings.add("5");
    IO.println(immutableStrings);

    "immutableStrings" should be "unmodifiableStrings" or "unmodifiableViewOfStrings". [confirmed fixed by dev.java team in a slightly different way in https://web.archive.org/web/20260701133510/https://dev.java/learn/api/collections-framework/factory-methods/ ]


    Then these are not regressions but rather items either not fixed (apparently unintentionally, not intentional "won't fix" which would be fine) or for which I have remaining comments the dev.java team MAY want to address.


    [Parts not in "[", "]" are from my earlier comment https://github.com/java/devjava-content/issues/206#issuecomment-4834074228]

    In the [previously reported] version of https://dev.java/learn/api/collections-framework/collection-interface/ (archived as [it was] at https://web.archive.org/web/20260628211938/https://dev.java/learn/api/collections-framework/collection-interface/ ),

    • In the sentence

      This result stands for ArrayList, but as you will see in the following, it would not be the same for HashSet.

      it is promised that we will get a complementary example using HashSet , but no such example occurs within the page in the current version.
      [appears NOT fixed yet https://web.archive.org/web/20260701135507/https://dev.java/learn/api/collections-framework/collection-interface/ , maybe just replace "as you will see in the following" with "as you will see later in this tutorial series" or something ]


    [Parts not in "[", "]" are from my earlier comment https://github.com/java/devjava-content/issues/206#issuecomment-4834074228]

    In the [previously reported] version of https://dev.java/learn/api/collections-framework/sets/ (archived as [it was] at https://web.archive.org/web/20260629004135/https://dev.java/learn/api/collections-framework/sets/ )

    • In the sentences

    first() and last() returns the lowest and the largest elements of the set
    headSet(toElement) and tailSet(fromElement) returns you subsets containing the elements lower than toElement or greater than or equal to fromElement

    "returns" should be "return" because the subjects are plural (2 methods each).
    [fixed by the dev.java team in https://web.archive.org/web/20260701140300/https://dev.java/learn/api/collections-framework/sets/
    BUT "return you" might be better as "return to you" , and the 2nd description of SortedSet here (unlike previous page which just got updated) doesn't yet mention getFirst/getLast added in Java 21 when mentioning first/last, so that may be good to add]


    Thanks very much!

  20. JosePaumard commented on Jul 3, 2026

    @JosePaumard
    Contributor

    Thank you again for report! A fix is on its way.
    As for the immutable / unmodifiable stuff. Since you have reflection, nothing is actually immutable in these parts of the API. Plus, with Valhalla round the corner (it is!) the meaning of immutability is changing. I don't know if the Collections Framework wording will be updated, but it will clearly sound dated at some point. So I changed all the wording to unmodifiable,

  21. willy-b commented on Jul 11, 2026

    @willy-b
    Author

    Thanks, most of what I reported above was addressed (I assume most of those ignored are intentional "won't fix" but ask about one of them at the bottom of this comment)!

    Here are some mismatched links I have found in the latest versions of the pages in this tutorial series.


    In the latest version of https://dev.java/learn/api/collections-framework/organization/ (archived as is https://web.archive.org/web/20260710223911/https://dev.java/learn/api/collections-framework/organization/?no_cache=true )


    In the latest version of https://dev.java/learn/api/collections-framework/sorted-maps/ (archived as is at https://web.archive.org/web/20260710225705/https://dev.java/learn/api/collections-framework/sorted-maps/ )


    Separately,
    I previously reported the following which is still an issue as of 2026-07-10 US PT ( https://web.archive.org/web/20260711005831/https://dev.java/learn/api/collections-framework/collection-interface/?no_cache=true&nonce=1 ):

    In the [previously reported] version of https://dev.java/learn/api/collections-framework/collection-interface/ (archived as [it was] at https://web.archive.org/web/20260628211938/https://dev.java/learn/api/collections-framework/collection-interface/ ),

    • In the sentence

    This result stands for ArrayList, but as you will see in the following, it would not be the same for HashSet.

    it is promised that we will get a complementary example using HashSet , but no such example occurs within the page in the current version.
    [appears NOT fixed yet https://web.archive.org/web/20260701135507/https://dev.java/learn/api/collections-framework/collection-interface/ , maybe just replace "as you will see in the following" with "as you will see later in this tutorial series" or something ]

    I still don't see any meaningful change for this particular sentence as of https://web.archive.org/web/20260711005831/https://dev.java/learn/api/collections-framework/collection-interface/?no_cache=true&nonce=1 , as it now reads:

    This result stands for ArrayList, but as you can see in the following example, it is not the same for HashSet.

    You did add a sentence afterwards (but which is not obviously connected; "learn more about Set" in a separate section does not seem to refer to "in the following example" of the last sentence):

    You will learn more about Set in the section Extending Collection with Set, SortedSet and NavigableSet.

    when an example on HashSet does not occur on this page (it still seems to suggest there is a specific code example coming up demonstrating HashSet). Thus I still recommend (you can acknowledge "won't fix" if you like of course) that it say "as you will see later in this tutorial series" or tie it directly into your next sentence mentioning more Set content will be covered on a later section of the tutorial (different page), or add a HashSet example.


    Thanks so much!

  22. JosePaumard commented on Jul 11, 2026

    @JosePaumard
    Contributor

    Thank you again for your comments! A fix is on its way.
    As for your last comment: I just added an example with a HashSet.

  23. willy-b commented on Jul 13, 2026

    @willy-b
    Author

    Thanks! I see the following items in the latest version of this tutorial.


    In the latest version of https://dev.java/learn/api/collections-framework/sorted-maps/ (archived as is at https://web.archive.org/web/20260712070012/https://dev.java/learn/api/collections-framework/sorted-maps/ )


    In the latest version of https://dev.java/learn/api/collections-framework/lists/ (archived as is at https://web.archive.org/web/20260713000721/https://dev.java/learn/api/collections-framework/lists/ )

    • In the sentence

      IO.println("A sublist is a modifiable view on the orginal list");

      "orginal" should be "original" .


    In the latest version of https://dev.java/learn/api/collections-framework/maps/ (archived as is at https://web.archive.org/web/20260713002109/https://dev.java/learn/api/collections-framework/maps/ )

    • In the example code output for "Updating and Removing Existing Key / Value Pairs"

      Running the previous code prints the following.

      Ages: {David=35, Carol=28, Bob=30, Alice=25}
      Previous Alice age: 25
      Previous Alice age: 26
      Current ALice age: 26 [<-- this line has a typo "ALice" and DOES NOT occur in the actual program output]
      Previous Eve age: null
      Updated map: {David=35, Carol=28, Bob=30, Eve=22, Alice=26}
      After removing David: {Carol=28, Bob=30, Eve=22, Alice=26}
      

      Where the line Current ALice age: 26 is not produced by the code shown in the page currently and should be removed from the expected output.


    In the latest version of https://dev.java/learn/api/collections-framework/maps-and-lambdas/ (archived as is at https://web.archive.org/web/20260713003221/https://dev.java/learn/api/collections-framework/maps-and-lambdas/?nonce=1 )

    • In the sentence

      The Map.merge() method merges a given key / value pair to an exising key / binding.

      "exising" should be "existing".

    • In the sentence

      Whic gives the following.

      "Whic" should be "Which"


    Thanks so much!

  24. JosePaumard commented on Jul 13, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.

  25. willy-b commented on Jul 14, 2026

    @willy-b
    Author

    Hey, for the page https://dev.java/learn/api/collections-framework/collection-interface/ ,
    it looks like there's been a regression in one of the dev.java team's recent changes (note I do not get to review changes before they go out).

    As of http://web.archive.org/web/20260714105633/https://dev.java/learn/api/collections-framework/collection-interface/?nonce=1
    the code box has been changed from a plain code snippet to an interactive dev.java playground for the example including

    User rebecca = new User("Rebecca");
    

    compare to the OLDER version https://web.archive.org/web/20260628144825/https://dev.java/learn/api/collections-framework/collection-interface/ which did not have that code box configured as runnable,
    and so did NOT need User defined (did not need to be a self-contained example),
    but as of the latest (archived as is at http://web.archive.org/web/20260714105633/https://dev.java/learn/api/collections-framework/collection-interface/?nonce=1 )
    this example now has a run button implying it is a complete executable example, which it is not (it is missing a User class definition). Attempting to run that example fails with an error:

    Image

    To make the example complete, a User class definition could be added to the top:

    class User {
      String name;
      User(String name) {
        this.name = name;
      }
    }
    

    After which the expected output would show:

    Image

    Earlier the dev.java team had that example configured non-runnable (making it ok to not be standalone):

    Image

    Thanks!

  26. JosePaumard commented on Jul 14, 2026

    @JosePaumard
    Contributor

    Thank you again for your report! A fix is on its way.

  27. willy-b commented on Jul 16, 2026

    @willy-b
    Author

    Thanks so much! All items previously reported appear resolved, so I am closing this.

    Not a blocker but as a side note I will comment that there is an inconsistent use of periods at the end of sentences in the bullet points on https://dev.java/learn/api/collections-framework/maps/ (archived as is at https://web.archive.org/web/20260716081031/https://dev.java/learn/api/collections-framework/maps/ ), see e.g. the bullet points in the section "Storing Key/Value Pairs in a Map".

    And also at
    https://dev.java/learn/api/collections-framework/sets/ ( archived as is at https://web.archive.org/web/20260716082222/https://dev.java/learn/api/collections-framework/sets/?nonce=1 )
    where e.g. the sentence below appears twice, and the first time has no ending period but in the second does:

    If there is no such element then null is returned

    Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions