關於Java 8 中 list 集合 size 大於 0 包含 null 元素導致空指針異常(NullPointException)的問題與解決方案



​ ​

1 摘要

java 中 list 是一種常用的集合類(接口),起子類 ArrayList 更是重要的使用廣泛; list 集合允許存在值爲 null 的元素,在操作 list 元素時,值爲 null 的對象可能會導致空指針異常(NullPointException)。下文將模擬 list 集合中 size 大於 0,同時包含 null 元素的情景,並針對該情況在 java 8 環境下給出一定的解決方案。

2 情景模擬

2.1 模擬包含 null 元素的 list 集合

        int length = 10;

        /**
         * list 初始化並賦值
         */
        List<Integer> integerList = new ArrayList<>(16);
        for (int i = 0; i < length; i++) {
            if (i == 5 || i == 8) {
                integerList.add(null);
            } else {
                integerList.add(i);
            }
        }

        /**
         * list size
         */
        System.out.println("list size: " + integerList.size());

        /**
         * 打印 list 的每一個元素
         */
        integerList.stream().forEach(integer -> {
            System.out.println(integer);
            // TODO to do something

        });

運行結果:

list size: 10
0
1
2
3
4
null
6
7
null
9

從結果可知,list 集合的長度爲 10,其中包含 2 個值爲 null 的元素

2.2 模擬出現空指針異常的場景

        try {
            integerList.stream().forEach(integer -> {
                System.out.println(integer.toString());
                // TODO to do something

            });
        } catch (Exception e) {
            e.printStackTrace();
        }

運行結果:

0
1
2
3
4
java.lang.NullPointerException
	at com.ljq.demo.object.ListDemoTest.lambda$listSize$1(ListDemoTest.java:50)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at com.ljq.demo.object.ListDemoTest.listSize(ListDemoTest.java:49)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

在 list 集合包含 null 元素時,如果再來操作 null 對象,則會拋出空指針異常(NullPointerException)

3 解決方案

3.1 當只操作原來的 list 時

如果只需要在原來的 list 中進行操作,後期不再使用該 list 時,可以使用的解決方法:

        integerList.stream().filter(Objects::nonNull).forEach(integer -> {
            System.out.println(integer.toString());
            // TODO to do something

        });

java 8 中使用 lambda 表達式進行過濾 null 元素

運行結果:

0
1
2
3
4
6
7
9

3.2 單次引用原來的 list 時

如果原來的 list 需要被引用單次時,可以使用該解決方案:

如將 list 轉化爲 json 字符串,同時過濾 null 元素

        try {
            String listJsonStr = new ObjectMapper().writeValueAsString(integerList.stream().filter(Objects::nonNull).peek(integer -> {
                System.out.println(integer.toString());
                // TODO to do something

            }).collect(Collectors.toList()));
            System.out.println("listJsonStr: " + listJsonStr);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

運行結果:

0
1
2
3
4
6
7
9
listJsonStr: [0,1,2,3,4,6,7,9]

3.3 多次引用,生成新的 list

當需要多次使用該 list,或使用 list 的不同屬性時,可以過濾 null 元素之後生成一個新的 list

在生成新的 list 之後,舊的 list 集合將會被棄用,可能會產生內存垃圾

        List<Integer> newIntegerList = integerList.stream().filter(Objects::nonNull).collect(Collectors.toList());
        System.out.println("newIntegerList size: " + newIntegerList.size());
        // TODO to do something

運行結果:

newIntegerList size: 8

4 完整測試代碼

package com.ljq.demo.object;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class ListDemoTest {

    @Test
    public void listSize() {

        int length = 10;

        /**
         * list 初始化並賦值
         */
        List<Integer> integerList = new ArrayList<>(16);
        for (int i = 0; i < length; i++) {
            if (i == 5 || i == 8) {
                integerList.add(null);
            } else {
                integerList.add(i);
            }
        }

        /**
         * list size
         */
        System.out.println("list size: " + integerList.size());

        /**
         * 打印 list 的每一個元素
         */
        integerList.stream().forEach(integer -> {
            System.out.println(integer);
            // TODO to do something

        });

        System.out.println("---------------- 分割線 ----------------------");

        /**
         * list 中的 null 元素可能導致空指針異常(NullPointerException)
         */
        try {
            integerList.stream().forEach(integer -> {
                System.out.println(integer.toString());
                // TODO to do something

            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("---------------- 分割線 ----------------------");

        /**
         * java 8 針對 list 集合 null 元素解決方案
         */
        /**
         * 1) 操作原來的 list
         */
        integerList.stream().filter(Objects::nonNull).forEach(integer -> {
            System.out.println(integer.toString());
            // TODO to do something

        });

        System.out.println("---------------- 分割線 ----------------------");

        /**
         * 2) 引用原來的 list
         */
        try {
            String listJsonStr = new ObjectMapper().writeValueAsString(integerList.stream().filter(Objects::nonNull).peek(integer -> {
                System.out.println(integer.toString());
                // TODO to do something

            }).collect(Collectors.toList()));
            System.out.println("listJsonStr: " + listJsonStr);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        System.out.println("---------------- 分割線 ----------------------");

        /**
         * 3) 生成一個新的 list(原來的 list 可能會產生內存垃圾)
         */
        List<Integer> newIntegerList = integerList.stream().filter(Objects::nonNull).collect(Collectors.toList());
        System.out.println("newIntegerList size: " + newIntegerList.size());
        // TODO to do something

    }
}

個人公衆號:404Code,分享半個互聯網人的技術與思考,感興趣的可以關注.
404Code

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章