List is an ordered
Collection (sometimes called a sequence). Lists may contain duplicate elements.
In addition to the operations inherited from Collection,
the List interface includes operations for the following:
Positional access — manipulates elements based on their numerical
position in the list
Search — searches for a specified object in the list and returns its numerical position
Iteration — extends Iterator semantics to
take advantage of the list's sequential nature
Range-view — performs arbitrary range operations on the list.
The List interface follows.
public interface List<E> extends Collection<E> {
// Positional access
E get(int index);
E set(int index, E element); //optional
boolean add(E element); //optional
void add(int index, E element); //optional
E remove(int index); //optional
boolean addAll(int index,
Collection<? extends E> c); //optional
// Search
int indexOf(Object o);
int lastIndexOf(Object o);
// Iteration
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
// Range-view
List<E> subList(int from, int to);
}
List
implementations.
ArrayList, which is usually the better-performing implementation, and
LinkedList which offers better performance under certain circumstances.
Also, Vector has been retrofitted to implement
List.
If you've usedVector, you're already familiar with the general basics ofList. (Of course,Listis an interface, whileVectoris a concrete implementation.)Listfixes several minor API deficiencies inVector. Commonly usedVectoroperations, such aselementAtandsetElementAt, have been given much shorter names. When you consider that these two operations are theListanalog of square brackets for arrays, it becomes apparent that shorter names are highly desirable. Consider the following assignment statement.Thea[i] = a[j].times(a[k]);Vectorequivalent is:Thev.setElementAt(v.elementAt(j).times(v.elementAt(k)), i);Listequivalent is:You may already have noticed that thev.set(i, v.get(j).times(v.get(k)));setmethod, which replaces theVectormethodsetElementAt, reverses the order of the arguments so that they match the corresponding array operation. Consider the following assignment statement.Thegift[5] = "golden rings";Vectorequivalent is:Thegift.setElementAt("golden rings", 5);Listequivalent is:For consistency's sake, the methodgift.set(5, "golden rings");add(int, E), which replacesinsertElementAt(Object, int), also reverses the order of the arguments.The three range operations in
Vector(indexOf,lastIndexOf, andsetSize) have been replaced by a single range-view operation (subList), which is far more powerful and consistent.
The operations inherited fromCollectionall do about what you'd expect them to do, assuming you're already familiar with them. If you're not familiar with them fromCollection, now would be a good time to read The Collection Interface section. Theremoveoperation always removes the first occurrence of the specified element from the list. TheaddandaddAlloperations always append the new element(s) to the end of the list. Thus, the following idiom concatenates one list to another.Here's a nondestructive form of this idiom, which produces a thirdlist1.addAll(list2);Listconsisting of the second list appended to the first.Note that the idiom, in its nondestructive form, takes advantage ofList<Type> list3 = new ArrayList<Type>(list1); list3.addAll(list2);ArrayList's standard conversion constructor.Like the
Setinterface,Liststrengthens the requirements on theequalsandhashCodemethods so that twoListobjects can be compared for logical equality without regard to their implementation classes. TwoListobjects are equal if they contain the same elements in the same order.
The basicpositional accessoperations (get,set,addandremove) behave just like their longer-named counterparts inVector(elementAt,setElementAt,insertElementAt, andremoveElementAt) with one noteworthy exception: Thesetandremoveoperations return the old value that is being overwritten or removed; theVectorcounterparts (setElementAtandremoveElementAt) return nothing (void). ThesearchoperationsindexOfandlastIndexOfbehave exactly like the identically named operations inVector.The
addAlloperation inserts all the elements of the specifiedCollectionstarting at the specified position. The elements are inserted in the order they are returned by the specifiedCollection's iterator. This call is the positional access analog ofCollection'saddAlloperation.Here's a little method to swap two indexed values in a
List. It should look familiar from Programming 101.Of course, there's one big difference. This is a polymorphic algorithm: It swaps two elements in anypublic static <E> void swap(List<E> a, int i, int j) { E tmp = a.get(i); a.set(i, a.get(j)); a.set(j, tmp); }List, regardless of its implementation type. Here's another polymorphic algorithm that uses the precedingswapmethod.This algorithm, which is included in the Java platform'spublic static void shuffle(List<?> list, Random rnd) { for (int i = list.size(); i > 1; i--) swap(list, i - 1, rnd.nextInt(i)); }Collectionsclass, randomly permutes the specified list using the specified source of randomness. It's a bit subtle: It runs up the list from the bottom, repeatedly swapping a randomly selected element into the current position. Unlike most naive attempts at shuffling, it's fair (all permutations occur with equal likelihood, assuming an unbiased source of randomness) and fast (requiring exactlylist.size()-1swaps). The following program uses this algorithm to print the words in its argument list in random order.In fact, this program can be made even shorter and faster. Theimport java.util.*; public class Shuffle { public static void main(String[] args) { List<String> list = new ArrayList<String>(); for (String a : args) list.add(a); Collections.shuffle(list, new Random()); System.out.println(list); } }Arraysclass has a static factory method calledasList, which allows an array to be viewed as aList. This method does not copy the array. Changes in theListwrite through to the array and vice versa. The resulting List is not a general-purposeListimplementation, because it doesn't implement the (optional)addandremoveoperations: Arrays are not resizable. Taking advantage ofArrays.asListand calling the library version ofshuffle, which uses a default source of randomness, you get the followingtiny programwhose behavior is identical to the previous program.import java.util.*; public class Shuffle { public static void main(String[] args) { List<String> list = Arrays.asList(args); Collections.shuffle(list); System.out.println(list); } }
As you'd expect, theIteratorreturned byList'siteratoroperation returns the elements of the list in proper sequence.Listalso provides a richer iterator, called aListIterator, which allows you to traverse the list in either direction, modify the list during iteration, and obtain the current position of the iterator. TheListIteratorinterface follows.The three methods thatpublic interface ListIterator<E> extends Iterator<E> { boolean hasNext(); E next(); boolean hasPrevious(); E previous(); int nextIndex(); int previousIndex(); void remove(); //optional void set(E e); //optional void add(E e); //optional }ListIteratorinherits fromIterator(hasNext,next, andremove) do exactly the same thing in both interfaces. ThehasPreviousand thepreviousoperations are exact analogues ofhasNextandnext. The former operations refer to the element before the (implicit) cursor, whereas the latter refer to the element after the cursor. Thepreviousoperation moves the cursor backward, whereasnextmoves it forward.Here's the standard idiom for iterating backward through a list.
Note the argument tofor (ListIterator<Type> it = list.listIterator(list.size()); it.hasPrevious(); ) { Type t = it.previous(); ... }listIteratorin the preceding idiom. TheListinterface has two forms of thelistIteratormethod. The form with no arguments returns aListIteratorpositioned at the beginning of the list; the form with anintargument returns aListIteratorpositioned at the specified index. The index refers to the element that would be returned by an initial call tonext. An initial call topreviouswould return the element whose index wasindex-1. In a list of lengthn, there aren+1valid values forindex, from0ton, inclusive.Intuitively speaking, the cursor is always between two elements — the one that would be returned by a call to
previousand the one that would be returned by a call tonext. Then+1validindexvalues correspond to then+1gaps between elements, from the gap before the first element to the gap after the last one. The following figure shows the five possible cursor positions in a list containing four elements.Calls to
The five possible cursor positions.
nextandpreviouscan be intermixed, but you have to be a bit careful. The first call topreviousreturns the same element as the last call tonext. Similarly, the first call tonextafter a sequence of calls topreviousreturns the same element as the last call toprevious.It should come as no surprise that the
nextIndexmethod returns the index of the element that would be returned by a subsequent call tonext, andpreviousIndexreturns the index of the element that would be returned by a subsequent call toprevious. These calls are typically used either to report the position where something was found or to record the position of theListIteratorso that anotherListIteratorwith identical position can be created.It should also come as no surprise that the number returned by
nextIndexis always one greater than the number returned bypreviousIndex. This implies the behavior of the two boundary cases: (1) a call topreviousIndexwhen the cursor is before the initial element returns-1and (2) a call tonextIndexwhen the cursor is after the final element returnslist.size(). To make all this concrete, the following is a possible implementation ofList.indexOf.Note that thepublic int indexOf(E e) { for (ListIterator<E> it = listIterator(); it.hasNext(); ) if (e == null ? it.next() == null : e.equals(it.next())) return it.previousIndex(); return -1; // Element not found }indexOfmethod returnsit.previousIndex()even though it is traversing the list in the forward direction. The reason is thatit.nextIndex()would return the index of the element we are about to examine, and we want to return the index of the element we just examined.The
Iteratorinterface provides theremoveoperation to remove the last element returned bynextfrom theCollection. ForListIterator, this operation removes the last element returned bynextorprevious. TheListIteratorinterface provides two additional operations to modify the list —setandadd. Thesetmethod overwrites the last element returned bynextorpreviouswith the specified element. The following polymorphic algorithm usessetto replace all occurrences of one specified value with another.The only bit of trickiness in this example is the equality test betweenpublic static <E> void replace(List<E> list, E val, E newVal) { for (ListIterator<E> it = list.listIterator(); it.hasNext(); ) if (val == null ? it.next() == null : val.equals(it.next())) it.set(newVal); }valandit.next. You need to special-case avalvalue ofnullto prevent aNullPointerException.The
addmethod inserts a new element into the list immediately before the current cursor position. This method is illustrated in the following polymorphic algorithm to replace all occurrences of a specified value with the sequence of values contained in the specified list.public static <E> void replace(List<E> list, E val, List<? extends E> newVals) { for (ListIterator<E> it = list.listIterator(); it.hasNext(); ){ if (val == null ? it.next() == null : val.equals(it.next())) { it.remove(); for (E e : newVals) it.add(e); } } }
Therange-viewoperation,subList(int fromIndex, int toIndex), returns aListview of the portion of this list whose indices range fromfromIndex, inclusive, totoIndex, exclusive. This half-open range mirrors the typicalforloop.As the term view implies, the returnedfor (int i = fromIndex; i < toIndex; i++) { ... }Listis backed up by theListon whichsubListwas called, so changes in the former are reflected in the latter.This method eliminates the need for explicit range operations (of the sort that commonly exist for arrays). Any operation that expects a
Listcan be used as a range operation by passing asubListview instead of a wholeList. For example, the following idiom removes a range of elements from aList.Similar idioms can be constructed to search for an element in a range.list.subList(fromIndex, toIndex).clear();Note that the preceding idioms return the index of the found element in theint i = list.subList(fromIndex, toIndex).indexOf(o); int j = list.subList(fromIndex, toIndex).lastIndexOf(o);subList, not the index in the backingList.Any polymorphic algorithm that operates on a
List, such as thereplaceandshuffleexamples, works with theListreturned bysubList.Here's a polymorphic algorithm whose implementation uses
subListto deal a hand from a deck. That is, it returns a newList(the "hand") containing the specified number of elements taken from the end of the specifiedList(the "deck"). The elements returned in the hand are removed from the deck.Note that this algorithm removes the hand from the end of the deck. For many commonpublic static <E> List<E> dealHand(List<E> deck, int n) { int deckSize = deck.size(); List<E> handView = deck.subList(deckSize - n, deckSize); List<E> hand = new ArrayList<E>(handView); handView.clear(); return hand; }Listimplementations, such asArrayList, the performance of removing elements from the end of the list is substantially better than that of removing elements from the beginning.The following is
a programthat uses thedealHandmethod in combination withCollections.shuffleto generate hands from a normal 52-card deck. The program takes two command-line arguments: (1) the number of hands to deal and (2) the number of cards in each hand.Running the program produces output like the following.import java.util.*; public class Deal { public static void main(String[] args) { if (args.length < 2) { System.out.println("Usage: Deal hands cards"); return; } int numHands = Integer.parseInt(args[0]); int cardsPerHand = Integer.parseInt(args[1]); // Make a normal 52-card deck. String[] suit = new String[] { "spades", "hearts", "diamonds", "clubs" }; String[] rank = new String[] { "ace","2","3","4","5","6","7","8", "9","10","jack","queen","king" }; List<String> deck = new ArrayList<String>(); for (int i = 0; i < suit.length; i++) for (int j = 0; j < rank.length; j++) deck.add(rank[j] + " of " + suit[i]); // Shuffle the deck. Collections.shuffle(deck); if (numHands * cardsPerHand > deck.size()) { System.out.println("Not enough cards."); return; } for (int i=0; i < numHands; i++) System.out.println(dealHand(deck, cardsPerHand)); } public static <E> List<E> dealHand(List<E> deck, int n) { int deckSize = deck.size(); List<E> handView = deck.subList(deckSize - n, deckSize); List<E> hand = new ArrayList<E>(handView); handView.clear(); return hand; } }% java Deal 4 5 [8 of hearts, jack of spades, 3 of spades, 4 of spades, king of diamonds] [4 of diamonds, ace of clubs, 6 of clubs, jack of hearts, queen of hearts] [7 of spades, 5 of spades, 2 of diamonds, queen of diamonds, 9 of clubs] [8 of spades, 6 of diamonds, ace of spades, 3 of hearts, ace of hearts]Although the
subListoperation is extremely powerful, some care must be exercised when using it. The semantics of theListreturned bysubListbecome undefined if elements are added to or removed from the backingListin any way other than via the returnedList. Thus, it's highly recommended that you use theListreturned bysubListonly as a transient object — to perform one or a sequence of range operations on the backingList. The longer you use thesubListinstance, the greater the probability that you'll compromise it by modifying the backingListdirectly or through anothersubListobject. Note that it is legal to modify a sublist of a sublist and to continue using the original sublist (though not concurrently).
Most polymorphic algorithms in theCollectionsclass apply specifically toList. Having all these algorithms at your disposal makes it very easy to manipulate lists. Here's a summary of these algorithms, which are described in more detail in the Algorithms section.
sort— sorts aListusing a merge sort algorithm, which provides a fast, stable sort. (A stable sort is one that does not reorder equal elements.)shuffle— randomly permutes the elements in aList.reverse— reverses the order of the elements in aList.rotate— rotates all the elements in aListby a specified distance.swap— swaps the elements at specified positions in aList.replaceAll— replaces all occurrences of one specified value with another.fill— overwrites every element in aListwith the specified value.copy— copies the sourceListinto the destinationList.binarySearch— searches for an element in an orderedListusing the binary search algorithm.indexOfSubList— returns the index of the first sublist of oneListthat is equal to another.lastIndexOfSubList— returns the index of the last sublist of oneListthat is equal to another.