For Loop with Lambda Expression in JAVA

2019-10-02 01:10发布

问题:

Why when I use following code I get IndexOutOfBoundsException

Code:

    List<Integer> ints = Stream.of(21,22,32,42,52).collect(Collectors.toList());
    System.out.print("the list: ");
    ints.forEach((i) -> {
        System.out.print(ints.get(i-1) + " ");
    });

My error stack:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 11, Size: 5
    at java.util.ArrayList.rangeCheck(ArrayList.java:638)
    at java.util.ArrayList.get(ArrayList.java:414)
    at Agent.lambda$main$1(Agent.java:33)
    at Agent$$Lambda$8/980546781.accept(Unknown Source)
    at java.util.ArrayList.forEach(ArrayList.java:1234)
    at Agent.main(Agent.java:32)
the list: Java Result: 1

but When I change my list to one digit numbers everything is fine

Code:

    List<Integer> ints = Stream.of(2,8,7,4,3).collect(Collectors.toList());
    System.out.print("the list: ");
    ints.forEach((i) -> {
        System.out.print(ints.get(i-1) + " ");
    });

output:

2 8 7 4 3 

回答1:

I think the reason is pretty clear.

ints.forEach((i) -> {
    System.out.print(ints.get(i-1) + " ");
});

Translates approximately to:

for (Integer i : ints) {
    System.out.println(ints.get(i - 1) + " ");
}

Which will cause IndexOutOfBoundsExceptions because i refers to the elements of each list, and each of those elements - 1 will give an index that is clearly out of bounds. For your first example, i will be 21, which gives an index of 21 - 1 == 20, which is out of bounds for the list you created.

Example:

List<Integer> ints = Stream.of(21,22,32,42,52).collect(Collectors.toList());

will end up so that

ints == [21, 22, 32, 42, 52]

So when you run this:

ints.forEach((i) -> {
    System.out.print(ints.get(i-1) + " ");
});

The computer takes the first element and tries to execute the body of the lambda:

Execute System.out.print(ints.get(i-1) + " ");:
    First element is 21
    21 - 1 == 20
    ints.get(20) --> IndexOutOfBoundsException

And for your second example:

List<Integer> ints = Stream.of(2,8,7,4,3).collect(Collectors.toList());

becomes

ints == [2, 8, 7, 4, 3]

So when you run this:

ints.forEach((i) -> {
    System.out.print(ints.get(i-1) + " ");
});

The computer goes through the elements and tries to execute the body of the lambda:

Execute System.out.print(ints.get(i-1) + " ");:
    First element is 2
    2 - 1 == 1
    ints.get(1) --> 8
    Print 8
Execute System.out.print(ints.get(i-1) + " ");:
    Second element is 8
    8 - 1 == 7
    ints.get(7) --> IndexOutOfBoundsException

So evidently the code in your second example is not what you actually have. I suspect that the code you actually have is:

List<Integer> ints = Stream.of(2,8,7,4,3).collect(Collectors.toList());
System.out.print("the list: ");
ints.forEach((i) -> {
    System.out.print(i + " ");
                     ^^^^^^^ <-- this is different
});

Which is entirely different than what you posted.



回答2:

Even simpler:

String s = Stream.of(21,22,32,42,52)
                 .collect(Collectors.joining(" "));


回答3:

forEach javadoc states

Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.

So the i in

(i) -> {System.out.print(ints.get(i-1) + " ");}

is each element in the List. If any of those elements minus 1 is bigger or equal to 5 (the size of the List), you try to get an element that is out of bounds.



回答4:

You are overdoing it. What you want is

List<Integer> ints = Stream.of(2,8,7,4,3).collect(Collectors.toList());
System.out.print("the list: ");
ints.forEach((i) -> {
    System.out.print(i + " ");
});

i in the forEach is not a loop counter, it's the items themselves. So i will take on the values 2,8,7,4,3 and for the second iteration ints.get (8-1) will be out of bounds.