为什么我的ArrayList会抛出IndexOutOfBoundsException异常?

3
我正在编写这个方法来创建一副洗过的纸牌:

public static ArrayList<String> shuffle() {
    String cards[] = {"Two of Spades","Three of Spades","Four of Spades","Five of Spades", "Six of Spades","Seven of Spades", "Eight of Spades", "Nine of Spades", "Ten of Spades", "Jack of Spades", "Queen of Spades", "King of Spades", "Ace of Spades", 
                      "Two of Hearts","Three of Hearts","Four of Hearts","Five of Hearts", "Six of Hearts","Seven of Hearts", "Eight of Hearts", "Nine of Hearts", "Ten of Hearts", "Jack of Hearts", "Queen of Hearts", "King of Hearts", "Ace of Hearts",
                      "Two of Clubs","Three of Clubs","Four of Clubs","Five of Clubs", "Six of Clubs","Seven of Clubs", "Eight of Clubs", "Nine of Clubs", "Ten of Clubs", "Jack of Clubs", "Queen of Clubs", "King of Clubs", "Ace of Clubs", 
                      "Two of Diamonds","Three of Diamonds","Four of Diamonds","Five of Diamonds", "Six of Diamonds","Seven of Diamonds", "Eight of Diamonds", "Nine of Diamonds", "Ten of Diamonds", "Jack of Diamonds", "Queen of Diamonds", "King of Diamonds", "Ace of Diamonds" };
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    for (int i = 0; i < 52; i++) {
        numbers.add(i);
    }
    ArrayList<String> deck = new ArrayList<String>();
    for (int i = 0; i < 52; i++) {
        Random randomizer = new Random();
        int index = randomizer.nextInt(numbers.size());
        int number = numbers.get(index);
        deck.add(numbers.get(number), cards[i]);
        numbers.remove(index);
    }
    return deck;
}

我遇到的问题是在deck.add时,出现了IndexOutOfBoundsException异常。我原以为ArrayList会自动调整大小以适应添加元素,为什么会发生这种情况,怎么解决呢?以下是异常信息:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 23, Size: 0
    at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:661)
    at java.util.ArrayList.add(ArrayList.java:473)
    at game.mainGame.shuffle(mainGame.java:182)
    at game.mainGame.main(mainGame.java:32)

你需要弄清楚索引错误是由deck.addnumbers.get(number)还是cards[i]引起的。它一定是其中之一,但我们很难神奇地看出哪一个。 - JamesENL
哦,抱歉,我以为我提到了 - 就是在 deck.add 的地方,那里抛出了异常。 - smograth
这是另一个很好的"不要重复造轮子"的例子。正如Ted Hopp在他的回答中已经提到的那样,使用Collections.shuffle()会为你节省很多麻烦 ;) - QBrute
4个回答

5

这个异常可能有两个原因。一个是你在这两行代码中对 numbers 列表进行了重复索引:

int number = numbers.get(index);
deck.add(numbers.get(number), cards[i]);

问题在于,一旦您从“numbers”中删除一个数字,那么剩余数字中有一个比最大的合法索引要大的可能性很高。 (实际上,这是有保证的,除非您恰好按严格降序生成索引。)另一个原因在于 @default locale 所述:您无法添加到 deck 中的任意位置;如果您尝试将元素添加到大于列表当前长度的位置,则两个参数的 add()调用将生成此异常。

与其自己实现shuffle,我建议您只需使用Collections.shuffle(List)来随机化 numbers 列表(或更好的是deck本身)。

以下是我的 shuffle()版本:

private static final List<String> cards = Arrays.asList(
    "Two of Spades","Three of Spades","Four of Spades","Five of Spades", "Six of Spades","Seven of Spades", "Eight of Spades", "Nine of Spades", "Ten of Spades", "Jack of Spades", "Queen of Spades", "King of Spades", "Ace of Spades", 
    "Two of Hearts","Three of Hearts","Four of Hearts","Five of Hearts", "Six of Hearts","Seven of Hearts", "Eight of Hearts", "Nine of Hearts", "Ten of Hearts", "Jack of Hearts", "Queen of Hearts", "King of Hearts", "Ace of Hearts",
    "Two of Clubs","Three of Clubs","Four of Clubs","Five of Clubs", "Six of Clubs","Seven of Clubs", "Eight of Clubs", "Nine of Clubs", "Ten of Clubs", "Jack of Clubs", "Queen of Clubs", "King of Clubs", "Ace of Clubs", 
    "Two of Diamonds","Three of Diamonds","Four of Diamonds","Five of Diamonds", "Six of Diamonds","Seven of Diamonds", "Eight of Diamonds", "Nine of Diamonds", "Ten of Diamonds", "Jack of Diamonds", "Queen of Diamonds", "King of Diamonds", "Ace of Diamonds"
);

public static ArrayList<String> shuffle() {
    final ArrayList<String> deck = new ArrayList<String>(cards);
    Collections.shuffle(deck);
    return deck;
}

2
另一个问题是Random对象在每次循环迭代时都会重新创建,因此随机化效果不是很好。 - Tagir Valeev

2

方法 ArrayList.add(int index, E element) 将元素 element 放在指定的位置 index 上。 如果索引超出范围(索引 < 0 || 索引 > size()),该方法会抛出 IndexOutOfBoundsException 异常。

deck.add(numbers.get(number), cards[i]);

由于您的数组列表deck在开始时为空(size()=0),因此您只能在索引0上添加元素,对于任何其他索引,都会抛出异常。

您可能希望生成随机卡片而不是随机位置。请查看之前的答案以获取有关洗牌的替代建议。


1
异常更可能是由于这个原因而不是我在答案中提供的第一个原因(并不是说我的原因是错误的,只是可能性小得多)。 - Ted Hopp
@TedHopp 谢谢,你的回答绝对正确并包含有用的建议。我只是想强调另一个可能导致异常的原因。 - default locale

1

0

将for循环修改为以下形式:

    for (int i = 0; i < 52; i++) {
        Random randomizer = new Random();
        int index = randomizer.nextInt(numbers.size());
        int number = numbers.get(index);
        deck.add(cards[number]);
        numbers.remove(index);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接