Let's suppose that we are writing a program to play a card game and are looking at writing a function to shuffle the deck. We might look first at something like this:
public class DeckOfCards { private Card[] cards; private java.util.Random rand = new java.util.Random(); ... public void addToTop (Card c) {...} public void addToBottom (Card c) {...} public Card drawFromTop() {...} public void shuffle() { for (int i = 1; i < cards.length; ++i) { int k = rand.nextInt(i+1); Card temp = cards[k]; cards[k] = cards[i]; cards[i] = temp; } } };
The shuffle function shown here will, indeed, arrange the cards into a random order. But looking at the other functions provided on the deck, we might wonder if the use of an array as storage is really appropriate. These functions seem to alter the number of cards left in the deck. That suggests that we need something more flexible. So we might look at something like:
public class DeckOfCards { private ArrayList<Card> cards; private java.util.Random rand = new java.util.Random(); public DeckOfCards (ArrayList<Card> initialSetOfCards) { cards = (ArrayList<Card>)initialSetOfCards.clone(); } public void addToTop (Card c) {cards.add(c);} public void addToBottom(Card c) {cards.add (0, c);} public Card drawFromTop() { return cards.remove(cards.size()-1); } public void shuffle() { for (int i = 1; i < cards.size(); ++i) { int k = rand.nextInt(i+1); Card temp = cards.get(k); cards.set(k, cards.get(i)); cards.set (i, temp); } } };
Now, the use of ArrayList might seem to be an obvious choice here. The shuffle function relies on random access (literally) to locations in the list, and that would be far, far slower in a linked list than in an array-based structure.
That's fine for games like poker where we always draw from the top of the deck and even for games like hearts or spades where we draw from the top of one deck and discard to the top of another (initially empty) deck. But what about a game like "war" where each player has a deck and draws cards from the top while adding cards to the bottom. Adding to the bottom will be terribly inefficient with an array-based structure. Over an entire game, that's likely to outweigh the one-time cost of an inefficient shuffle.
So we might actually be better off with
public class DeckOfCards { private List<Card> cards; private java.util.Random rand = new java.util.Random(); public DeckOfCards (List<Card> initialSetOfCards) { cards = (List<Card>)initialSetOfCards.clone(); } public void addToTop (Card c) {cards.add(c);} public void addToBottom(Card c) {cards.add (0, c);} public Card drawFromTop() { return cards.remove(cards.size()-1); } public void shuffle() { for (int i = 1; i < cards.size(); ++i) { int k = rand.nextInt(i+1); Card temp = cards.get(k); cards.set(k, cards.get(i)); cards.set (i, temp); } } };
which would allow the specific applications to select the underlying implementation of the list:
public class Poker { DeckOfCards deck; public Poker () { ArrayList<Card> startingDeck = ...; deck = new DeckOfCards (startingDeck); } ... } public class War { DeckOfCards deck; public Poker () { LinkedList<Card> startingDeck = ...; deck = new DeckOfCards (startingDeck); } ... }
So what gets stored in the DeckOfCards could be either kind of list, because both ArrayList and LinkedList implement the List interface.
In the Forum: