Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java for-each loop throw NullPointException

Tags:

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.

like image 1000
Linlin Avatar asked Apr 28 '13 03:04

Linlin


People also ask

Does for loop throws NullPointerException Java?

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?

Does forEach handle null Java?

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 .

How do I fix Java Lang NullPointerException in Java?

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.

Can we iterate empty list in Java?

Yes, it is equivalent. If the list is empty, the for-each cycle is not executed even once.


2 Answers

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.

  1. As you demonstrated the current behavior of the for(:)-loop is very easy to understand. The other behavior isn't

  2. It would be the only thing in the java universe behaving in this way.

  3. It wouldn't be equivalent to the simple for-loop so migrating between the two would actually not be equivalent

  4. 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.

  5. 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.

like image 138
Jens Schauder Avatar answered Oct 07 '22 04:10

Jens Schauder


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.

1: Enhanced For vs. Iterator

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.

2. Enhanced For vs. for-Loop

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.

3. Conclusion

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.

like image 32
Makoto Avatar answered Oct 07 '22 05:10

Makoto