我知道Set and Map接受null值,今天我刚刚发现LinkedList也接受像这样的null值
map.put(null, 1);
set.add(null);
linkedList.add(null)
是否有其他允许存储null值的集合?。只是为了在一个地方获得一个全面的列表,并为每个列表列出原因。
Set
和Map
是接口。它们有几个Java实现。
Map
的流行实现有:
HashMap
-接受一个null
密钥Hashtable
-不接受任何null
密钥TreeMap
-不接受任何null
密钥LinkedHashMap
-接受一个null
密钥
在上述任何实现中,任何数量的null
值都可以添加为值
Set
的流行实现有:
HashSet
-接受一个null
元素TreeSet
-不接受任何null
元素LinkedHashSet
-接受一个null
元素
List
的任何实现,如ArrayList
或LinkedList
,都可以接受null
s。
这个问题可以通过使用JDK中的占位符类Optional
来解决。
其原理是使用将携带值(也为null值(的Optional类将可为null的值封装在包装器实例中。
它可能很烦人,代码也有点重,但在许多情况下肯定是合适的。
以下是一个非零值的示例:
Optional<Integer> valuedInt = Optional.ofNullable(123);
assertTrue(valuedInt.isPresent());
assertEquals(Integer.valueOf(123), valuedInt.get());
这里有一个空值的例子:
Optional<Integer> nullInt = Optional.ofNullable(null);
assertTrue(nullInt.isEmpty());
try {
var val = nullInt.get();
} catch (Exception e) {
// Invocation of nullInt.get() throws an exception.
assertTrue(e instanceof NoSuchElementException);
}
// However, the method Optional.orElse(T) can be used as
// a getter that supplies the value wether it is valued or null.
assertEquals(Integer.valueOf(123), valuedInt.orElse(null));
assertEquals(null, nullInt.orElse(null));
我们可以初始化列表如下:
// Our hacked list
List<Optional<Integer>> integersOpt;
填充列表的第一种方法:
integersOpt = new ArrayList<>();
integersOpt.add(Optional.ofNullable(1));
integersOpt.add(Optional.ofNullable(null));
integersOpt.add(Optional.ofNullable(2));
integersOpt.add(Optional.ofNullable(null));
integersOpt.add(Optional.ofNullable(3));
填充列表的第二种方法(不可修改(:
integersOpt =
Arrays.asList(1, null, 2, null, 3).stream()
.map(x -> Optional.ofNullable(x))
.collect(Collectors.toList());
填充列表的第三种方式(可修改(:
integersOpt =
new ArrayList<>(
Arrays.asList(1, null, 2, null, 3).stream()
.map(x -> Optional.ofNullable(x))
.collect(Collectors.toList())
);
计数和打印值:
int countNulls = 0, countNonNulls = 0;
for(Optional<Integer> opt : integersOpt) {
Integer optVal = opt.orElse(null); // Our famous "getter" function.
if(optVal == null) {countNulls++;}
else {countNonNulls++;}
System.out.println("Original value = " + optVal);
}
assertEquals(2, countNulls);
assertEquals(3, countNonNulls);
integersOpt.toString()
将返回此值:
[Optional[1], Optional.empty, Optional[2], Optional.empty, Optional[3]]
时间证明,接受null
是一个设计错误。首先,这会使实现变得复杂,因为在某个时间点,您可能需要能够比较null
和null
;这有时需要特殊的处理(或者至少在代码中有特殊的分支(。
自java-9以来,不可变集合都拒绝null
,甚至正确地记录它。一个小例子(但还有更多(是Set:of
,它说:
@throws NullPointerException如果元素为空
不仅如此,他们还不允许null
检查,所以你可能会对此感到惊讶:
Set<Object> oldS = new HashSet<>();
oldS.add(new Object());
System.out.println(oldS.contains(null)); // false
Set<Object> newS = Set.of(new Object());
System.out.println(newS.contains(null)); // NPE
JDK中的每一个新集合或实现都禁止null
。