Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid list copy on reverse iteration #12297

Merged
merged 3 commits into from
Sep 24, 2024
Merged

Conversation

gregw
Copy link
Contributor

@gregw gregw commented Sep 23, 2024

The changes in #12289 introduced several list copy and reverses operations on every request.
This PR replaces that reversed list with a ListIterator used with previous

…l concurrency. (#12290)"

This reverts/modified parts of commit 115ee1c.
…l concurrency. (#12290)"

This reverts/modified parts of commit 115ee1c.
@gregw gregw requested review from sbordet and lorban and removed request for sbordet September 23, 2024 01:30
@gregw
Copy link
Contributor Author

gregw commented Sep 23, 2024

@sbordet @lorban an alternative approach would be to have the following method:

    /**
     * <p>Returns an {@link Iterator} that efficiently iterates the passed list in reverse.</p>
     * <p>The method is safe for concurrent modifications iff the list iterator itself is safe.</p>
     * @param list the list
     * @param <T> the element type
     * @return An {@link Iterator} over the list elements in reverse order
     * at the snapshot of the list represented by this call.
     */
    public static <T> Iterator<T> reversed(List<T> list)
    {
        ListIterator<T> i;
        try
        {
            int size = list.size();
            switch (size)
            {
                case 0:
                    return Collections.emptyIterator();
                case 1:
                    return list.iterator();
                default:
                    i = list.listIterator(size);
            }
        }
        catch (IndexOutOfBoundsException e)
        {
            // list was concurrently modified, so do this the hard way
            i = list.listIterator();
            while (i.hasNext())
                i.next();
        }

        ListIterator<T> iterator = i;
        return new Iterator<T>()
        {
            @Override
            public boolean hasNext()
            {
                return iterator.hasPrevious();
            }

            @Override
            public T next()
            {
                return iterator.previous();
            }
        };
    }

sbordet
sbordet previously approved these changes Sep 23, 2024
Copy link
Contributor

@sbordet sbordet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just javadocs niggles.

Copy link
Contributor

@lorban lorban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of modifying the iterations, we could keep TypeUtil.reverse() and implement it this way:

    public static <T> Iterable<T> reverse(List<T> list)
    {
        return () -> new Iterator<>()
        {
            private final ListIterator<T> listIterator = listIteratorAtEnd(list);

            @Override
            public boolean hasNext()
            {
                return listIterator.hasPrevious();
            }

            @Override
            public T next()
            {
                return listIterator.previous();
            }

            @Override
            public void remove()
            {
                listIterator.remove();
            }
        };
    }

@lorban lorban linked an issue Sep 23, 2024 that may be closed by this pull request
@sbordet
Copy link
Contributor

sbordet commented Sep 23, 2024

@lorban but that's another allocation.

@lorban
Copy link
Contributor

lorban commented Sep 23, 2024

@sbordet possibly, but I sometimes feel we're a bit too obsessed with allocations so I'm tempted to spend some time looking at the JIT's efficiency at generating stack allocating code to deny/confirm if our fears are justified.

In all case, I think exposing an iterator that delegates to ListIterator's previous() and hasPrevious() like @gregw suggested in his alternative would still look better IMHO.

janbartel
janbartel previously approved these changes Sep 23, 2024
@joakime
Copy link
Contributor

joakime commented Sep 23, 2024

Please fix the title of this PR, so it makes sense on a changelog.

@gregw
Copy link
Contributor Author

gregw commented Sep 23, 2024

Instead of modifying the iterations, we could keep TypeUtil.reverse() and implement it this way:

The resulting list would have an list with an iterator that does not match the get(int) behaviour. If that list ever got out into the wild, then chaos would reign.

However, I'm not totally opposed to my alternative, even though that is an extra allocation.... The resulting code is a little neater.... but then it pretty simple code anyway, and seldom touched, and clear what it does, so in this case the extra allocation doesn't get us much.

@gregw gregw changed the title Fix/12296/reverse iteration Avoid list copy on reverse iteration Sep 23, 2024
updates from review

Co-authored-by: Simone Bordet <[email protected]>
Copy link
Contributor

@lorban lorban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this could be improved, but I agree that this code is simple enough that no improvement would bring very much value; so LGTM the way it is.

@gregw gregw merged commit 18b9782 into jetty-12.0.x Sep 24, 2024
12 checks passed
@gregw gregw deleted the fix/12296/reverseIteration branch September 24, 2024 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Revert inefficient changes made in #12289
5 participants