使用Map.of()导致的空指针问题

开发中遇到的Map的get空指针问题

0、前置

什么是Map.of()?
	/**
     * Returns an unmodifiable map containing zero mappings.
     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
     *
     * @param <K> the {@code Map}'s key type
     * @param <V> the {@code Map}'s value type
     * @return an empty {@code Map}
     *
     * @since 9
     */
    @SuppressWarnings("unchecked")
    static <K, V> Map<K, V> of() {
        return (Map<K,V>) ImmutableCollections.EMPTY_MAP;
    }

Map.of()Java 9 中引入的一种方法,它允许开发人员创建最多包含 10 个键值对的不可变映射。
它提供了一种方便简洁的 Map 创建方式,无需编写大量代码即可轻松创建小型Map。Map.of()是对以前使用类的构造函数创建小Map的方法的改进HashMap,这种方法可能很麻烦且冗长。

什么是HashMap?
	/**
     * Constructs an empty {@code HashMap} with the specified initial
     * capacity and the default load factor (0.75).
     *
     * @param  initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * Constructs an empty {@code HashMap} with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

new HashMap<>() 是Java中类提供的构造函数HashMap,允许开发者新建一个HashMap. 它用于创建可变映射,这意味着可以通过添加、删除或更新键值对来修改映射。
它是在 Java 中创建Map的常用方法,尤其是在处理较大的数据集时。

1、Map获取value的方法

map.get(key)map.getOrDefault(key, defaultValue)

两个Map都可以使用 get 或者 getOrDefault 方法获取map中的key对应的value

BUT !!!
我们现在做一个测试

public static void main(String[] args) {
    Map<Object, Object> hashMap = new HashMap<>();
    Map<Object, Object> mapOf = Map.of();

    Object mapObject = hashMap.get("");
    Object mapOfObject = mapOf.get("");

    System.out.println(map);
    System.out.println(mapOfObject);
}

输出结果:
在这里插入图片描述

这样输出的结果是正确的,因为两个map都是空的所以返回的结果是null

然而,是这样的吗?不妨我们想一想,get中的key可能是不同的类型的,然后我们把key换一下试试!

@Data
static class TestA {
    private Long id;
    private String name;
}

public static void main(String[] args) {
    Map<Object, Object> hashMap = new HashMap<>();
    Map<Object, Object> mapOf = Map.of();

    TestA testA = new TestA();
    Object mapTestA = hashMap.get(testA.getId());
    Object mapOfTestA = mapOf.get(testA.getId());

    System.out.println(mapTestA);
    System.out.println(mapOfTestA);
}

输出结果却是这样的:
在这里插入图片描述
为什么呢,都是get为什么会导致空指针呢?

2、原因分析

HashMap重写父类get的方法

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(key)) == null ? null : e.value;
}

而 Map.of()重写的get方法是

@Override
@SuppressWarnings("unchecked")
public V get(Object o) {
    if (size == 0) {
        Objects.requireNonNull(o);
        return null;
    }
    int i = probe(o);
    if (i >= 0) {
        return (V)table[i+1];
    } else {
        return null;
    }
}

我们在回归回来看一下,当时我们用对象的属性字段去获取Map中的value的时候,对象中的属性值可能是个null

最后通过map去get的时候就会出现Map.of()使用get方法的时候就会走到 Objects.requireNonNull(o); 这一行,最终导致空指针异常!

3、总结

Map.of()在 Java 中使用有几个好处:

Map.of()提供了一种在 Java 中创建Map的简洁方便的方法。这使得代码更具可读性和更易于维护。

Map.of()创建不可变映射,这意味着映射一旦创建,就无法修改。这为Map中存储的数据提供了一定程度的安全性和保障性。

Map.of()为映射的键和值提供类型安全,这有助于防止使用 new 时可能发生的类型相关错误的HashMap<>()。

4、写在最后

报错主要是因为Map.of()不允许键或值为null

Map.of() 内部实现会检査传入的所有键和值,如果发现任何一个键或值是null,它会立即抛出 NullPointerException

这是因为不可变映射的设计原则之一就是保证映射的完整性和安全性,避免潜在的 null 值引发的问题。

5、最后的最后

在组装一些零散的map数据的时候Map.of还是很好用的,最后祝大家UpUpUp,每天多学一点,好记性不如烂笔头嘛

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用:lambda表达式中list.stream().map(o -> Long.valueOf(o.getId())).collect(Collectors.toList());报出了NPE,正常情况下list不为不会发生NPE,最多o.getId()中id为Long.valueOf(null)为抛出NumberFormatException,但是最终却是因为id为抛出了NPE。 。 引用:/** * 此时为指针异常NPE,而不是NumberFormatException */ public class LamError { @Test public void test1() { A a1 = new A(); a1.setId(null); a1.setName(null); A a2 = new A(); a2.setId(2L); a2.setName("1"); A a3 = new A(); a3.setId(2L); a3.setName(null); List<A> list = new ArrayList<>(); list.add(a1); // id是null name不是Null list.add(a2); // 都不为 list.add(a3); // name是null list.stream().map(o -> Long.valueOf(o.getId())).collect(Collectors.toList()); // list.stream().map(p -> Long.valueOf(p.getName())).collect(Collectors.toList()); } } class A { private Long id; private String name; } 。 引用:list.stream().filter(o.getId() != null).map(o -> Long.valueOf(o.getId())).collect(Collectors.toList());`。 Lambda表达式中出现NPE的原因是因为在o.getId()中,id为导致了Long.valueOf(null)抛出了NullPointerException。这是一个异常情况,正常情况下,当list不为时,不会发生NPE。最多会抛出NumberFormatException,因为Long.valueOf()方法无法将null转换为Long类型。 在引用中的示例代码中,当创建A对象时,a1的id被设置为null,a2和a3的id都被设置为非null的值。然后将这些A对象添加到了List中。在使用Lambda表达式进行操作时,当遇到a1的id为null时,就会抛出NPE异常。 解决这个问题的一种方法是在Lambda表达式中添加一个过滤器(filter),用于排除id为null的情况。可以使用filter(o.getId() != null)来过滤掉id为的对象,然后再进行map操作。 例如,可以使用以下代码来避免指针异常: list.stream().filter(o -> o.getId() != null).map(o -> Long.valueOf(o.getId())).collect(Collectors.toList());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值