The range-view operation, subList(int fromIndex, int toIndex)
, returns a List
view of the portion of this list whose indices range from fromIndex
, inclusive, to toIndex
, exclusive. This half-open range mirrors the typical for
-loop:
for (int i=fromIndex; i<toIndex; i++) {
...
}
As the term view implies, the returned List
is backed by the List
on which subList
was called, so changes in the former List
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 List
can be used as a range operation by passing a subList
view instead of a whole List
. For example, the following idiom removes a range of elements from a list:
list.subList(fromIndex, toIndex).clear();
Similar idioms may be constructed to search for an element in a range:
int i = list.subList(fromIndex, toIndex).indexOf(o);
int j = list.subList(fromIndex, toIndex).lastIndexOf(o);
Note that the above idioms return the index of the found element in the subList
, not the index in the backing List
.
Any polymorphic algorithm that operates on a List
(like the replace
and shuffle
examples, above) works with the List
returned by subList
.
Here's a polymorphic algorithm whose implementation uses subList
to deal a hand from a deck. That is to say, it returns a new List
(the "hand") containing the specified number of elements taken from the end of the specified List
(the "deck"). The elements returned in the hand are removed from the deck.
public static List dealHand(List deck, int n) {
int deckSize = deck.size();
List handView = deck.subList(deckSize-n, deckSize);
List hand = new ArrayList(handView);
handView.clear();
return hand;
}
The literal-minded might say that this program deals from the bottom of the deck, but I prefer to think that the computer is holding the deck upside down. At any rate, for many common List
implementations, like ArrayList
, the performance of removing elements from the end of the list is substantially better than that of removing elements from the beginning.
Here's a program using the dealHand
method in combination with Collections.shuffle
to generate hands from a normal 52-card deck. The program takes two command line arguments: the number of hands to deal and the number of cards in each hand.
import java.util.*;
class Deal {
public static void main(String args[]) {
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 deck = new ArrayList();
for (int i=0; i<suit.length; i++)
for (int j=0; j<rank.length; j++)
deck.add(rank[j] + " of " + suit[i]);
Collections.shuffle(deck);
for (int i=0; i<numHands; i++)
System.out.println(dealHand(deck, cardsPerHand));
}
}
% 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]
While the
subList
operation is extremely powerful, some care must be exercised when using it. The semantics of the List
returned by subList
become undefined if elements are added to or removed from the backing List
in any way other than via the returned List
. Thus, it's highly recommended that you use the List
returned by subList
only as a transient object, to perform one or a sequence of range operations on the backing List
. The longer you use the subList
object, the greater the probability that you'll compromise it by modifying the backing List
directly (or through another subList
object).
No comments:
Post a Comment