Java 集合和 null 值:容忍和限制
1. 概述
在本文中,我們將探討不同 Java 集合類型null
值的容忍度和限制。 Java 集合以不同的方式處理null
值,需要仔細注意。
雖然ArrayList
和HashMap
允許null
值,但存取它們會帶來NullPointerException.
相比之下, TreeMap
完全禁止null
鍵。了解這些差異對於避免執行時間錯誤至關重要,尤其是在使用 Streams 或處理集合時。正確的空值檢查可確保程式碼穩健且無錯誤。
2. List
中的ull
值
List
可以包含null
元素。我們可以新增和檢索null
值而不會出現錯誤。但是,在檢索後始終檢查null
以避免NullPointerException.
首先,我們定義一個可重複使用的 lambda 表達式來計算集合中的null
值:
Function<Collection<?>, Long> countNulls
= collection -> collection.stream().filter(Objects::isNull).count();
Function<Collection<?>, Long>
介面定義一個 lambda 表達式,該表達式採用Collection<?>
並傳回null
值的計數。 stream()
方法將集合轉換為流, filter(Objects::isNull)
只保留null
元素, count()
會傳回這些null
元素的數量。
現在,讓我們測試如何處理新增到清單中的null
值:
@Test
void givenList_whenNullValueAdded_doesNotFail() {
// adding nulls to a list
Integer[] numberArray = { null, 0, 1, null, 2, 3, null };
List<Integer> numbers = Arrays.asList(numberArray);
assertEquals(3, countNulls.apply(numbers));
// accessing nulls from a list
Integer number = numbers.get(0);
assertNull(number);
// dereferencing nulls from a list
assertThrows(NullPointerException.class, () -> number.toString());
}
此測試驗證向List
新增null
值不會導致錯誤。然後,測試驗證我們的函數是否準確地計算了null
元素的數量。接下來,測試斷言存取清單中的null
元素將傳回null.
最後,它確保嘗試取消引用null
(透過呼叫其方法)會拋出NullPointerException.
3. Set
中的ull
值
Set
由唯一的元素組成,這意味著每個元素只能出現一次。此外, Set
一次最多可以包含一個null
值。此特性可確保no duplicates
並有助於維護資料的完整性。
並非所有類型的集合都允許null
值。讓我們來探討一下背後的原因。
一個HashSet
可以保存一個null
值。在HashMap
的支援下,基於哈希的查找可以處理單一null
元素。但是,如果在自訂邏輯(例如雜湊)中使用null
,我們需要謹慎處理可能引發NullPointerException
的操作。
TreeSet
不能保存null
值,因為它依賴Comparable
或自訂Comparator
。任何新增null
嘗試都會導致NullPointerException
,因為自然排序無法比較null
。
讓我們繼續檢查Set
null
值的管理。首先,讓我們檢查一個HashSet
:
@Test
void givenHashSet_whenNullValueAdded_doesNotFail() {
// adding nulls to a HashSet
Integer[] numberArray = { null, 0, 1, null, 2, 3, null };
Set<Integer> numbers = new HashSet<>(Arrays.asList(numberArray));
assertEquals(1, countNulls.apply(numbers));
// accessing null from a set
assertTrue(numbers.contains(null));
// dereferencing nulls from a set
assertThrows(NullPointerException.class, () -> numbers.forEach(Object::toString()));
}
在這裡,我們確認在HashSet
加入null
值不會導致錯誤。然後,我們驗證重複的null
條目是否被忽略。然後,我們檢查該集合是否正確包含一個null
,並且嘗試在集合迭代中取消引用null
會引發NullPointerException.
現在,是時候檢查TreeSet
如何處理null
值了:
@Test
void givenTreeSet_whenNullValueAdded_mightFail() {
// adding nulls to a TreeSet
Integer[] numberArray = { null, 0, 1, null, 2, 3, null };
assertThrows(NullPointerException.class, () -> new TreeSet<>(Arrays.asList(numberArray)));
}
此測試示範了在TreeSet
新增null
值會引發NullPointerException.
請記住, TreeSet
要求元素可比較,排序時不能比較null
。
4. Map
中的ull
值
Java 中的HashMap
是一種允許儲存鍵值對的資料結構,可容納一個null
鍵和多個null
值。這意味著我們可以插入單一null
鍵,同時我們可以將多個null
值與其他鍵相關聯。當添加null
鍵時,專門將其儲存在為null
鍵保留的指定雜湊桶中。
當使用get()
方法檢索與null
鍵關聯的值時, HashMap
會優雅地處理這種情況,而不會引發任何異常,從而使開發人員可以輕鬆地使用null
鍵。
但是,在程式碼中取消引用這些null
鍵和值時要小心,因為嘗試對null
參考進行操作可能會導致NullPointerException
。因此,應該實施適當的檢查,以確保我們有效地管理null
值,以防止運行時錯誤:
@Test
void givenHashMap_whenNullKeyValueAdded_doesNotFail() {
// adding nulls to key-value pairs
Integer[] numberArray = { null, 0, 1, null, 2, 3, null };
Map<Integer, Integer> numbers = new HashMap<>();
Arrays.stream(numberArray)
.forEach(integer -> numbers.put(integer, integer));
assertEquals(1, countNulls.apply(numbers.keySet()));
assertEquals(1, countNulls.apply(numbers.values()));
// accessing nulls from a map
assertTrue(numbers.containsKey(null));
assertTrue(numbers.containsValue(null));
assertNull(numbers.get(null));
// dereferencing nulls from a map
assertThrows(NullPointerException.class, () -> numbers.get(null)
.toString());
}
在上面的測試中,我們驗證了在HashMap
加入null
鍵和值不會導致錯誤,這證明了HashMap's
將null
作為鍵和值處理的能力。我們也檢查是否存在null
鍵和值的行為,確認null,
並斷言取消引用null
會觸發NullPointerException.
現在,是時候檢查TreeMap
如何處理null
值了:
@Test
void givenTreeMap_whenNullKeyAdded_fails() {
// adding nulls to key-value pairs
Map<Integer, Integer> numbers = new TreeMap<>();
// adding null key and null value
assertThrows(NullPointerException.class, () -> numbers.put(null, null));
// adding null key and non-null value
assertThrows(NullPointerException.class, () -> numbers.put(null, 1));
// adding non-null key and null value
assertDoesNotThrow(() -> numbers.put(1, null));
// adding non-null key and non-null value
assertDoesNotThrow(() -> numbers.put(1, 1));
}
在這裡,我們示範了TreeMap
在嘗試新增null
鍵時拋出NullPointerException
,無論值是否為null.
但是,我們可以看到,如果鍵為非 null ,則它允許null
值.
5. 結論
在本文中,我們探討了 Java 集合如何處理null
值,從ArrayList
和HashSet
的靈活性到TreeMap's
嚴格限制。了解這些行為有助於防止錯誤,確保更安全的資料處理和更可靠的應用程式。
與往常一樣,所有程式碼範例都可以在 GitHub 上取得。