Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions src/java.base/share/classes/java/util/List.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@
*
* The {@code List} interface places additional stipulations, beyond those
* specified in the {@code Collection} interface, on the contracts of the
* {@code iterator}, {@code add}, {@code remove}, {@code equals}, and
* {@code hashCode} methods. Declarations for other inherited methods are
* also included here for convenience.<p>
* {@code iterator}, {@code add}, {@code remove}, {@code removeAtIndex},
* {@code equals}, and {@code hashCode} methods. Declarations for other
* inherited methods are also included here for convenience.<p>
*
* The {@code List} interface provides four methods for positional (indexed)
* The {@code List} interface provides five methods for positional (indexed)
* access to list elements. Lists (like Java arrays) are zero based. Note
* that these operations may execute in time proportional to the index value
* for some implementations (the {@code LinkedList} class, for
Expand Down Expand Up @@ -623,6 +623,10 @@ default void sort(Comparator<? super E> c) {
* operation). Shifts any subsequent elements to the left (subtracts one
* from their indices). Returns the element that was removed from the
* list.
* <p>
* When removing by index, prefer {@linkplain #removeAtIndex(int)} over
* this method, especially for {@code List<Integer>}, to avoid confusion
* with {@linkplain #remove(Object)}.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
Expand All @@ -633,6 +637,30 @@ default void sort(Comparator<? super E> c) {
*/
E remove(int index);

/**
* Removes the element at the specified position in this list (optional
* operation). Shifts any subsequent elements to the left (subtracts one
* from their indices). Returns the element that was removed from the
* list.
* <p>
* This method is preferred over {@linkplain #remove(int)} when removing
* by index, especially for {@code List<Integer>}, to avoid confusion
* with {@linkplain #remove(Object)}.
*
* @implSpec
* The default implementation invokes {@link #remove(int) remove(index)}.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws UnsupportedOperationException if the {@code remove} operation
* is not supported by this list
* @throws IndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
* @since 27
*/
default E removeAtIndex(int index) {
return remove(index);
}

// Search Operations

Expand Down
44 changes: 43 additions & 1 deletion test/jdk/java/util/List/ListDefaults.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -21,6 +21,7 @@
* questions.
*/

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -275,6 +276,47 @@ private void removeFirst(final List<Integer> original, final List<Integer> list,
CollectionAsserts.assertContents(original.subList(offset.getAndIncrement(), original.size()), list);
}

@Test
public void testRemoveAtIndex() {
final class RemoveTrackingList extends AbstractList<Integer> {
boolean removeCalled;
final List<Integer> delegate = new ArrayList<>(List.of(10, 20, 30));

@Override public Integer get(int index) { return delegate.get(index); }
@Override public int size() { return delegate.size(); }
@Override public Integer remove(int index) {
removeCalled = true;
return delegate.remove(index);
}
}

var trackingList = new RemoveTrackingList();
assertEquals(trackingList.removeAtIndex(1), Integer.valueOf(20));
assertTrue(trackingList.removeCalled);
assertEquals(trackingList, List.of(10, 30));

// The following two sub-tests show how to avoid picking up the wrong
// overload.

List<Integer> byIndex = new ArrayList<>(List.of(1, 2, 1));
assertEquals(byIndex.removeAtIndex(1), Integer.valueOf(2));
assertEquals(byIndex, List.of(1, 1));

List<Integer> byValue = new ArrayList<>(List.of(1, 2, 1));
assertTrue(byValue.remove(Integer.valueOf(1)));
assertEquals(byValue, List.of(2, 1));

try {
byIndex.removeAtIndex(byIndex.size());
fail("expected IndexOutOfBoundsException not thrown");
} catch (IndexOutOfBoundsException _) {}

try {
List.of(1, 2, 3).removeAtIndex(1);
fail("expected UnsupportedOperationException not thrown");
} catch (UnsupportedOperationException _) {}
}

@Test
public void testReplaceAll() {
final int scale = 3;
Expand Down