The following java segment will result a NullPointException, since the variable list is null, which is pass to the for-each loop.
List<> arr = null; for (Object o : arr) { System.out.println("ln "+o); }
I think for (Object o : arr){ }
is a equivalent to
for (int i = 0; i < arr.length; i++) { }
and/or
for (Iterator<type> iter = arr.iterator(); iter.hasNext(); ){ type var = iter.next(); }
In either cases arr is null will cause arr.length or arr.iterator() throws a NullPointException
I'm just curious the reason why for (Object o : arr){ }
is NOT translate to
if (arr!=null){ for (int i = 0; i < arr.length; i++) { } } and if (arr!=null){ for (Iterator<type> iter = arr.iterator(); iter.hasNext(); ){ type var = iter.next(); } }
Include arr!=null expression could reduce code nesting.
The following java segment will result a NullPointException, since the variable list is null, which is pass to the for-each loop. Include arr!= null expression could reduce code nesting. If you know that arr is not null already, would you like it to go through an extra useless null check?
1.2 In Java 8, we can use forEach to loop a Map and print out its entries. 1.3 For the Map 's key or value containing null , the forEach will print null . P.S The normal way to loop a Map will print the same above output. 1.4 If we do not want to print the null key, add a simple null checking inside the forEach .
In Java, the java. lang. NullPointerException is thrown when a reference variable is accessed (or de-referenced) and is not pointing to any object. This error can be resolved by using a try-catch block or an if-else condition to check if a reference variable is null before dereferencing it.
Yes, it is equivalent. If the list is empty, the for-each cycle is not executed even once.
I see the following reasons, although I have no idea if anybody thought about this, when it was implemented, and what the actual reasons were.
As you demonstrated the current behavior of the for(:)-loop is very easy to understand. The other behavior isn't
It would be the only thing in the java universe behaving in this way.
It wouldn't be equivalent to the simple for-loop so migrating between the two would actually not be equivalent
Using null is a bad habit anyway, so NPEs are a nice way of telling the developer "you F***ed up, clean up your mess" with the proposed behavior the problem would just be hidden.
What if you want to do anything else with the array before or after the loop ... now you would have the null check twice in your code.
To answer your first question: no, these three loops are not equivalent. Second, there is no null check to be found in these loops; there isn't any sense in trying to iterate over that which does not exist.
Assume that we have the following class:
import java.util.Arrays; import java.util.Iterator; import java.util.List; public class EnhancedFor { private List<Integer> dummyList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); private List<Integer> nullList = null; public void enhancedForDummyList() { for(Integer i : dummyList) { System.out.println(i); } } public void iteratorDummyList() { for(Iterator<Integer> iterator = dummyList.iterator(); iterator.hasNext();) { System.out.println(iterator.next()); } } public void normalLoopDummyList() { for(int i = 0; i < dummyList.size(); i++) { System.out.println(dummyList.get(i)); } } }
We're going to decompose it to its bytecode and see if there's any difference between these loops.
Here's the bytecode for the enhanced for loop.
public enhancedForDummyList()V L0 LINENUMBER 12 L0 ALOAD 0 GETFIELD EnhancedFor.dummyList : Ljava/util/List; INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator; ASTORE 1 L1 FRAME APPEND [java/util/Iterator] ALOAD 1 INVOKEINTERFACE java/util/Iterator.hasNext ()Z IFEQ L2 ALOAD 1 INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object; CHECKCAST java/lang/Integer ASTORE 2 L3 LINENUMBER 13 L3 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 2 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L4 LINENUMBER 14 L4 GOTO L1 L2 LINENUMBER 15 L2 FRAME CHOP 1 RETURN L5 LOCALVARIABLE i Ljava/lang/Integer; L3 L4 2 LOCALVARIABLE i$ Ljava/util/Iterator; L1 L2 1 LOCALVARIABLE this LEnhancedFor; L0 L5 0 MAXSTACK = 2 MAXLOCALS = 3
Below this is the bytecode for the iterator.
public iteratorDummyList()V L0 LINENUMBER 24 L0 ALOAD 0 GETFIELD EnhancedFor.dummyList : Ljava/util/List; INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator; ASTORE 1 L1 FRAME APPEND [java/util/Iterator] ALOAD 1 INVOKEINTERFACE java/util/Iterator.hasNext ()Z IFEQ L2 L3 LINENUMBER 25 L3 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object; INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V GOTO L1 L2 LINENUMBER 27 L2 FRAME CHOP 1 RETURN L4 LOCALVARIABLE iterator Ljava/util/Iterator; L1 L2 1 // signature Ljava/util/Iterator<Ljava/lang/Integer;>; // declaration: java.util.Iterator<java.lang.Integer> LOCALVARIABLE this LEnhancedFor; L0 L4 0 MAXSTACK = 2 MAXLOCALS = 2
Ultimately, it does look like they're doing very similar things. They're using the same interface. There is a variation in that the enhanced for loop is using two variables for the current value (i
) and cursor to the rest of the list (i$
), whereas the iterator only needs the cursor to invoke .next()
.
Similar, but not quite the same.
Let's add in the bytecode for the for loop.
public normalLoopDummyList()V L0 LINENUMBER 24 L0 ICONST_0 ISTORE 1 L1 FRAME APPEND [I] ILOAD 1 ALOAD 0 GETFIELD EnhancedFor.dummyList : Ljava/util/List; INVOKEINTERFACE java/util/List.size ()I IF_ICMPGE L2 L3 LINENUMBER 25 L3 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 0 GETFIELD EnhancedFor.dummyList : Ljava/util/List; ILOAD 1 INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L4 LINENUMBER 24 L4 IINC 1 1 GOTO L1 L2 LINENUMBER 27 L2 FRAME CHOP 1 RETURN L5 LOCALVARIABLE i I L1 L2 1 LOCALVARIABLE this LEnhancedFor; L0 L5 0 MAXSTACK = 3 MAXLOCALS = 2
It's doing something different. It's not using the Iterator
interface at all. Instead, we're making calls to get()
, which is only specified by the List
, not the Iterator
.
There's a valid reason as to why the list we're dereferencing is assumed not null - we're invoking methods specified by the interface. If those methods weren't implemented that'd be different: throw an UnsupportedOperationException
. If the object we're trying to invoke the contract on didn't exist - that just doesn't make sense.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With