Guavasatz + Funktion = Karte

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 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 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.