~/home of geeks

Null-Lookups in Sets

· 251 Wörter · 2 Minute(n) Lesedauer

Black hole in space

Prüfen von Werten in Sets wird problematisch, wenn man mit null-Werten arbeitet.

Leider wird das Verhalten von Set#contains() mit null-Werten vom Set-Interface nicht reglementiert. Das hat zur Folge, dass ein set.contains(null) je nach Implementierung, entweder false/true zurückgibt oder eine NullPointerException wirft.

Das ist insbesondere dann unpraktisch, wenn man Parameter ungeprüft gegen ein Set prüfen möchte.

Angenommen eine Wertemenge wird durch ein Set bestimmt und soll gegen eine Eingabe geprüft werden:

class MyClass {
    private static final ImmutableSet<String> allowedValues = ImmutableSet.of("a", "b");

    public boolean isValid(String value) {
        return allowedValues.contains(value);
    }
}

Um implementierungsspezifische Fehler zu vermeiden, sollte eine explizite Prüfung auf null erfolgen:

class MyClass {
    private static final Set<String> allowedValues = Set.of("a", "b");

    public boolean isValid(String value) {
        return value == null ? false : allowedValues.contains(value);
    }
}

Das ist leider etwas unleserlicher und umständlicher. Daher sollte man seine Set-Implementierung im Blick haben und sorgfältig aussuchen.

Die gängisten Set-Implementierungen sind folgende.

import com.google.common.collect.ImmutableSet;
import org.apache.commons.collections4.set.UnmodifiableSet;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

class SetContainsNullTest {
    public static void main(String[] args) {
        containsNull(Set.of("a", "b"), "Set.of()");

        containsNull(ImmutableSet.of("a", "b"), "ImmutableSet.of()");

        containsNull(new HashSet<>(Set.of("a", "b")), "new HashSet<>()");

        containsNull(new LinkedHashSet<>(Set.of("a", "b")), "new LinkedHashSet<>()");

        containsNull(UnmodifiableSet.unmodifiableSet(Set.of("a", "b")), "UnmodifiableSet.unmodifiableSet()");
    }

    static void containsNull(Set<String> set, String info) {
        try {
            System.out.println(info+" contains null: " + set.contains(null));
        } catch (NullPointerException e) {
            System.out.println(info+" "+e);
        }
    }
}

Sie liefern folgende Ausgaben.

Set.of() java.lang.NullPointerException: Cannot invoke "Object.equals(Object)" because "o" is null
ImmutableSet.of() contains null: false
new HashSet<>() contains null: false
new LinkedHashSet<>() contains null: false
UnmodifiableSet.unmodifiableSet() java.lang.NullPointerException: Cannot invoke "Object.equals(Object)" because "o" is null