简体   繁体   中英

Why does this code throw an ArrayIndexOutOfBoundsException?

Here's the code:

public class Deck {

    private Card[] cards;

    public Deck() {
        cards = new Card[52];
        String[] ranks = {"ace","two","three","four","five","six","seven","eight","nine","ten","jack","queen","king"};
        String[] suits = {"hearts","diamonds","clubs","spades"};
        for(int i = 0; i < suits.length; i++) {
            for(int n = 0; n < ranks.length; n++) {
                cards[cards.length] = new Card(ranks[i],suits[n]);
            }
        }
    }
}

As you can see, this loops though the two given arrays and generates a card for every combination. There are 13 ranks x 4 suits = 52 cards. I expected that on the 52nd iteration, cards.length would be 51, but the compiler says

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 52
    at com.cards.Deck.<init>(Deck.java:14)

Why is that?

cards[cards.length]

Because you're trying to use an index that doesn't exist. cards.length is 52, your array is 0 - 51.

I suspect you're trying to insert each card into that array, which means you need another counter ;)

int cardIndex = 0;
for(int i = 0; i < suits.length; i++) {
    for(int n = 0; n < ranks.length; n++, cardIndex++) {
        cards[cardIndex] = new Card(ranks[n],suits[i]);
    }
}

EDIT: What I didn't catch is what others have mentioned - you also have the counters for ranks/suits switched in the Card constructor - fixed that too.

The issue is that cards.length is not the total number of elements used in the array; it's the total number of elements in the array, regardless of what you've stored in the array so far. Consequently, as soon as you execute the inner loop, this will try accessing the 52nd element of the array, causing the exception you've seen.

To fix this, consider instead storing a counter that will keep track of the next free index, or use some simple math to derive the position that the card should go in from its suit and value. For example, since on each iteration of the outer loop you will write ranks.length elements to the array, on iteration ( i , n ) you will write to array index i * ranks.length + n . Using this, you could rewrite the inner loop as

    // Careful... still buggy!
    for(int i = 0; i < suits.length; i++) {
        for(int n = 0; n < ranks.length; n++) {
            cards[i * ranks.length + n] = new Card(ranks[i],suits[n]);
        }
    }

Additionally, note that your access into the arrays is wrong. Right now, you're writing

new Card(ranks[i],suits[n]);

However, i ranges over suits, not values. The proper code would be

new Card(ranks[n],suits[i]);

Which gives this final implementation:

    for(int i = 0; i < suits.length; i++) {
        for(int n = 0; n < ranks.length; n++) {
            cards[i * ranks.length + n] = new Card(ranks[n],suits[i]);
        }
    }

More generally, though, don't use the .length field of an array to track how many used elements there are. You'll need to store that separately. Alternatively, consider using an ArrayList , which wraps an array and tracks this for you.

Hope this helps!

The index variables for ranks ranks and suits are exchanged. (Don't damage your head with your palm.)

Consider it with renamed variables:

for(int SUIT = 0; SUIT < suits.length; SUIT++) {
    for(int RANK = 0; RANK < ranks.length; RANK++) {
        cards[cards.length] = new Card(ranks[SUIT],suits[RANK]);
    }
}

It isn't always best to use i and n . (I would have used s and r .)

Also, consider:

Card[] cards = new Card[X];
cards[X] // will never be "in bounds", indices from [0, X-1]

Happy coding.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM