Guaven Set + Funktion = Karte
1. Überblick
In diesem Tutorial werden wir eine der vielen nützlichen Funktionen inGuavacollect Paket veranschaulichen:how to apply a Function to a Guava Set and obtain a Map.
Wir werden zwei Ansätze diskutieren - das Erstellen einer unveränderlichen Karte und einer Live-Karte basierend auf den integrierten Guavenoperationen und anschließend die Implementierung einer benutzerdefinierten LiveMap-Implementierung.
2. Konfiguration
Zunächst fügen wir dieGuava-Bibliothek als Abhängigkeit inpom.xml: hinzu
com.google.guava
guava
19.0
Ein kurzer Hinweis: Sie können überprüfen, obnewer version herevorhanden sind.
3. Die ZuordnungFunction
Definieren wir zunächst die Funktion, die wir auf die Mengenelemente anwenden:
Function function = new Function() {
@Override
public String apply(Integer from) {
return Integer.toBinaryString(from.intValue());
}
};
Die Funktion konvertiert einfach den Wert einesInteger in seine binäreString-Darstellung.
4. GuavetoMap()
Guava bietet eine statische Dienstprogrammklasse fürMap-Instanzen. Unter anderem gibt es zwei Operationen, mit denen einSetin einMapumgewandelt werden kann, indem die definiertenFunctionder Guave angewendet werden.
Das folgende Snippet zeigt das Erstellen eines unveränderlichenMap:
Map immutableMap = Maps.toMap(set, function);
Die folgenden Tests bestätigen, dass der Satz ordnungsgemäß konvertiert wurde:
@Test
public void givenStringSetAndSimpleMap_whenMapsToElementLength_thenCorrect() {
Set set = new TreeSet(Arrays.asList(32, 64, 128));
Map immutableMap = Maps.toMap(set, function);
assertTrue(immutableMap.get(32).equals("100000")
&& immutableMap.get(64).equals("1000000")
&& immutableMap.get(128).equals("10000000"));
}
Das Problem mit der erstellten Zuordnung besteht darin, dass die abgeleitete Zuordnung nicht aktualisiert wird, wenn dem Quellensatz ein Element hinzugefügt wird.
4. GuaveasMap()
Wenn wir das vorherige Beispiel verwenden und eine Karte mit der MethodeMaps.asMaperstellen:
Map liveMap = Maps.asMap(set, function);
Wir erhaltena live map view - was bedeutet, dass die Änderungen am Ursprungssatz auch in der Karte angezeigt werden:
@Test
public void givenStringSet_whenMapsToElementLength_thenCorrect() {
Set set = new TreeSet(Arrays.asList(32, 64, 128));
Map liveMap = Maps.asMap(set, function);
assertTrue(liveMap.get(32).equals("100000")
&& liveMap.get(64).equals("1000000")
&& liveMap.get(128).equals("10000000"));
set.add(256);
assertTrue(liveMap.get(256).equals("100000000") && liveMap.size() == 4);
}
Beachten Sie, dass die Tests korrekt bestätigen, obwohl wir ein Element über eine Gruppe hinzugefügt und es in der Karte nachgeschlagen haben.
5. Erstellen von benutzerdefinierten LiveMap
Wenn wir von derMap-Ansicht vonSet sprechen, erweitern wir grundsätzlich die Fähigkeit vonSet unter Verwendung vonGuavaFunction.
In der Live-Ansicht vonMapollten Änderungen anSet dieMapEntrySet in Echtzeit aktualisieren. Wir werden unsere eigenen generischenMap erstellen undAbstractMap<K,V> wie folgt unterklassifizieren:
public class GuavaMapFromSet extends AbstractMap {
public GuavaMapFromSet(Set keys,
Function super K, ? extends V> function) {
}
}
Bemerkenswert ist, dass der Hauptvertrag aller Unterklassen vonAbstractMap darin besteht, die MethodeentrySet wie bisher zu implementieren. In den folgenden Unterabschnitten werden wir uns dann mit zwei kritischen Teilen des Codes befassen.
5.1. Einträge
Ein weiteres Attribut in unserenMap istentries, was unsereEntrySet: darstellt
private Set> entries;
Das Feldentries wird immer mit der EingabeSet ausconstructor: initialisiert
public GuavaMapFromSet(Set keys,Function super K, ? extends V> function) {
this.entries=keys;
}
Ein kurzer Hinweis hier - um eine Live-Ansicht aufrechtzuerhalten, verwenden wir die gleicheniterator in der EingabeSet für die nachfolgendenMapEntrySet.
Bei der Erfüllung des Vertrags vonAbstractMap<K,V> implementieren wir die MethodeentrySet, bei der wir dannentries zurückgeben:
@Override
public Set> entrySet() {
return this.entries;
}
5.2. Zwischenspeicher
DiesesMap speichert die durchapplying Function to Set: erhaltenen Werte
private WeakHashMap cache;
6. DieSet Iterator
Wir werden die EingabeSetiterator für die nachfolgendenMapEntrySet verwenden. Dazu verwenden wir eine angepassteEntrySet-Klasse sowie eine angepassteEntry-Klasse.
6.1. DieEntry Klasse
Lassen Sie uns zunächst sehen, wie ein einzelner Eintrag inMapaussehen wird:
private class SingleEntry implements Entry {
private K key;
public SingleEntry( K key) {
this.key = key;
}
@Override
public K getKey() {
return this.key;
}
@Override
public V getValue() {
V value = GuavaMapFromSet.this.cache.get(this.key);
if (value == null) {
value = GuavaMapFromSet.this.function.apply(this.key);
GuavaMapFromSet.this.cache.put(this.key, value);
}
return value;
}
@Override
public V setValue( V value) {
throw new UnsupportedOperationException();
}
}
In diesem Code ist es natürlich nicht zulässig,Set vonMap zu ändern. Ansichtas ein Aufruf vonsetValue löstUnsupportedOperationException. aus
Achten Sie genau aufgetValue - dies ist der Kern unsererlive view-Funktionalität. Wir überprüfencache in unserenMap auf das aktuellekey (Set Element).
Wenn wir den Schlüssel finden, geben wir ihn zurück, andernfallswe apply our function to the current key and obtain a value, und speichern ihn incache.
Auf diese Weise ist die Karte immer dann auf dem neuesten Stand, wennSet ein neues Element enthält, da die neuen Werte im laufenden Betrieb berechnet werden.
6.2. DieEntrySet
Wir werden nun dieEntrySet implementieren:
private class MyEntrySet extends AbstractSet> {
private Set keys;
public MyEntrySet(Set keys) {
this.keys = keys;
}
@Override
public Iterator> iterator() {
return new LiveViewIterator();
}
@Override
public int size() {
return this.keys.size();
}
}
Wir haben den Vertrag zur Verlängerung vonAbstractSet erfüllt, indem wir die Methodeniterator undsize überschrieben haben. Aber es gibt noch mehr.
Denken Sie daran, dass die Instanz diesesEntrySet dasentries in unsererMap-Ansicht bildet. Normalerweise gibtEntrySet einer Karte für jede Iteration einfach ein vollständigesEntry zurück.
In unserem Fall müssen wir jedochiterator aus der EingabeSet verwenden, um unsere Live-Ansicht. beizubehalten. Wir wissen, dass nur die ElementeSet zurückgegeben werden , also brauchen wir auch ein benutzerdefiniertesiterator.
6.3. DieIterator
Hier ist die Implementierung unsereriterator für die obigenEntrySet:
public class LiveViewIterator implements Iterator> {
private Iterator inner;
public LiveViewIterator () {
this.inner = MyEntrySet.this.keys.iterator();
}
@Override
public boolean hasNext() {
return this.inner.hasNext();
}
@Override
public Map.Entry next() {
K key = this.inner.next();
return new SingleEntry(key);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
LiveViewIterator müssen sich innerhalb der KlasseMyEntrySetbefinden. Auf diese Weise können wir dieSetiterator bei der Initialisierung gemeinsam nutzen.
Beim Durchlaufen der Einträge vonGuavaMapFromSet mititerator ruft ein Aufruf vonnext einfach den Schlüssel aus deniterator vonSet ab und erstellt einSingleEntry.
7. Alles zusammenfügen
Nachdem Sie das, was wir in diesem Tutorial behandelt haben, zusammengefügt haben, lassen Sie uns die VariableliveMap aus den vorherigen Beispielen ersetzen und durch unsere benutzerdefinierte Karte ersetzen:
@Test
public void givenIntSet_whenMapsToElementBinaryValue_thenCorrect() {
Set set = new TreeSet<>(Arrays.asList(32, 64, 128));
Map customMap = new GuavaMapFromSet(set, function);
assertTrue(customMap.get(32).equals("100000")
&& customMap.get(64).equals("1000000")
&& customMap.get(128).equals("10000000"));
}
Wenn Sie den Inhalt der EingabeSet ändern, werden Sie sehen, dass dieMap in Echtzeit aktualisiert werden:
@Test
public void givenStringSet_whenMapsToElementLength_thenCorrect() {
Set set = new TreeSet(Arrays.asList(32, 64, 128));
Map customMap = Maps.asMap(set, function);
assertTrue(customMap.get(32).equals("100000")
&& customMap.get(64).equals("1000000")
&& customMap.get(128).equals("10000000"));
set.add(256);
assertTrue(customMap.get(256).equals("100000000") && customMap.size() == 4);
}
8. Fazit
In diesem Tutorial haben wir uns die verschiedenen Möglichkeiten angesehen, wie manGuava Operationen undobtain a Map view from a Set by applying a Function nutzen kann.
Die vollständige Implementierung all dieser Beispiele und Codefragmentecan be found in my Guava github project - dies ist ein Eclipse-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.