Die Map-API hat seit Java 8 einige clevere Methoden, die mehrere Operationen zusammenfassen, wobei die Funktionsweise folgendem Bauplan entspricht: ist ein assoziierter Wert zu einem Schlüssel (nicht) vorhanden, dann tue dies, sonst das.
interface java.util.Map<K,V>
§ default V putIfAbsent(K key, V value)
Testet zuerst, ob es zu dem gegeben Schlüssel key einen assoziierten Wert existiert und wenn ja, gibt es keine Änderung an der Datenstruktur, nur der alte assoziierte Wert wird zurückgegeben. Existiert kein assoziierter Wert, speichert die Datenstruktur zum Schlüssel den value. Die Rückgabe von putIfAbsent(…) ist null, falls es vorher keinen alten assoziierten Wert gab, andernfalls die Referenz vom alten Objekt (was auch null ein kann, wenn die Map auch null-Werte erlaubt), was jetzt durch den neuen Wert überschreiben wurde. Falls null als Wert in der Map erlaubt ist – wie etwa in HashMap – so gilt eine Besonderheit: ist ein existierender Schlüssel mit null assoziiert, dann würde putIfAbsent(…) den Wert null mit etwas anderem überschreiben.
§ default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
Vergleichbar mit putIfAbsent(…), nur nutzt diese Methode eine Berechnungsmethode statt einen festen Wert. Ein wichtiger Punkt ist, dass wenn die Berechnungsfunktion null zurückgibt, nichts an dem Assoziativspeicher verändert wird und der alte Wert bleibt. Der Rückgabewert ist immer entweder der letzte assoziierter Wert oder der neue Eintrag, es sein denn, die mappingFunction lieferte null. Die Methode lässt sich damit perfekt in einer Methodenkaskadierung verwenden der Art map.computeIfAbsent(…).methodeVonV(…).
§ default V computeIfPresent(K key, BiFunction<? super K, ? super V,? extends V> remappingFunction)
Überschreibt den assoziierten Wert mit einem von der Funktion berechneten neuen Wert, wenn das Schlüssel/Werte-Paar existiert. Dann liefert die Methode denen neuen Wert zurück. Zwei Sonderfälle sind zu unterscheiden. Falls es zu dem Schlüssel key keinen Wert gibt macht computeIfPresent(…) nichts und die Rückgabe ist null. Gibt es einen assoziierten Wert, doch die auf den Wert angewendete Funktion liefert null, wird das Schlüssel/Werte-Paar gelöscht und die Rückgabe ist ebenfalls null.
Beispiel. Java bietet von Haus aus keine Datenstruktur, die wie ein Assoziativspeicher arbeitetet, aber einen Schlüssel mit einer Sammlung von Werten assoziieren kann. Doch so eine Klasse ist schnell geschrieben:
class Multimap<K, V> {
private final Map<K, Collection<V>> map = new HashMap<>();
public Collection<V> get( K key ) {
return map.getOrDefault( key, Collections.<V> emptyList() );
}
public void put( K key, V value ) {
map.computeIfAbsent( key, k -> new ArrayList<>() )
.add( value );
}
}
Ein kleines Beispiel:
Multimap<Integer, String> mm = new Multimap<>();
System.out.println( mm.get( 1 ) ); // []
mm.put( 1, „eins“ );
System.out.println( mm.get( 1 ) ); // [eins]
mm.put( 1, „one“ );
System.out.println( mm.get( 1 ) ); // [eins, one]
interface java.util.Map<K,V>
§ default V merge(K key, V value, BiFunction<? super V, ? super V,? extends V> remappingFunction)
Setzt einen neuen Eintrag in die Map oder verschmilzt einen existierenden Eintrag mit der angegebenen Funktion. Von der Semantik her ist das die komplexeste Methode. Der erste Fall einfach, denn wenn es zum Schlüssel kein Wert gibt; dann wird das Schlüssel-/Werte Paar in die Map gesetzt und merge(…) liefert als Rückgabe den value. Gibt es schon einen assoziierten Wert, wird die Funktion mit dem alten Wert und value auf aufgerufen (eine BiFunction hat zwei Parameter) und der alte assoziierte Wert mit diesem neuen Wert überschrieben und die Rückgabe von merge(…) liefert diesen neuen Wert. Jetzt gibt es noch zwei Sonderfälle, und die hängen damit zusammen wenn das Argument value gleich null oder die Funktion null liefert. In beiden Fällen wird das Schlüssel/Wert-Paar gelöscht und die Rückgabe von merge(…) ist null.
Beispiel: Zu einer ID soll ein Punkt assoziiert werden. Neue hinzugefügte Punkte zu dieser ID sollen die Koordinate des ursprünglichen Punktes verschieben.
Map<Integer, Point> map = new HashMap<>();
BiFunction<? super Point, ? super Point, ? extends Point> remappingFunc =
(oldVal, val) -> { val.translate( oldVal.x, oldVal.y ); return val; };
map.merge( 1, new Point( 12, 3 ), remappingFunc );
System.out.println( map.get( 1 ) ); // java.awt.Point[x=12,y=3]
map.merge( 1, new Point( -2, 2 ), remappingFunc );
System.out.println( map.get( 1 ) ); // java.awt.Point[x=10,y=5]
map.merge( 1, new Point( 0, 5 ), remappingFunct );
System.out.println( map.get( 1 ) ); // java.awt.Point[x=10,y=10]