[JAVA] Karte <K, V1> in Karte <K, V2> konvertieren (Kartenwert konvertieren)

Überblick

Wie soll ich schreiben, wenn ich Map-Werte () sofort in eine andere Klasse konvertieren möchte? Lassen Sie uns anhand eines Beispiels erklären, das "Map <String, String>" in "Map <String, Integer>" konvertiert.

Implementierungsplan

Vorschlag 1: Zur Aussage verwenden

public Map<String, Integer> strToInt(final Map<String, String> map) {
    final Map<String, Integer> ret = new HashMap<>();
    for (final Map.Entry<String, String> entry : map.entrySet()) {
        ret.put(entry.getKey(), Integer.valueOf(entry.getValue()));
    }
    return ret;
}

Vorerst wurde es auf Java 7 geschrieben. Es gibt viel Code, der redundant und nicht wesentlich ist.

Option 2: Verwenden Sie die Java 8 Stream-API

public Map<String, Integer> strToInt(final Map<String, String> map) {
    return map.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey, e -> Integer.valueOf(e.getValue())));
}

Es ist kürzer als Plan 1, aber immer noch etwas lang.

Vorschlag 3: Guavas [Maps :: transformValues](https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/Maps.html#transformValues-java.util.Map- Verwenden Sie com.google.common.base.Function-)

public Map<String, Integer> strToInt(final Map<String, String> map) {
    return Maps.transformValues(map, new Function<String, Integer>() {
        @Override
        public Integer apply(String input) {
            return Integer.valueOf(input);
        }
    });
}

Das? Ist es länger als Plan 2? Nein, Sie können Lambda-Ausdruck verwenden, da die abstrakte Methode eine funktionale Schnittstelle ist.

public Map<String, Integer> strToInt(final Map<String, String> map) {
    return Maps.transformValues(map, v -> Integer.valueOf(v));
}

Sie können auch Siehe Methode verwenden, um es etwas kürzer zu machen.

public Map<String, Integer> strToInt(final Map<String, String> map) {
    return Maps.transformValues(map, Integer::valueOf);
}

Vorschlag 4: Guavas [Maps :: transformValues](https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/Maps.html#transformValues-java.util.Map- Eine andere Lösung mit com.google.common.base.Function-)

public Map<String, Integer> strToInt(final Map<String, String> map) {
    return ImmutableMap.copyOf(Maps.transformValues(map, Integer::valueOf));
}

Vorschlag 3 mit ImmutableMap :: copyOf Eingewickelt. Die Erklärung wird später beschrieben.

Prüfung

Testen wir nun, ob es richtig funktioniert.

Getestete Klasse

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

interface MapValuesTransformer {

    Map<String, Integer> strToInt(final Map<String, String> map);

    /**
     *Vorschlag 1:Zur Aussage verwenden
     */
    static class ForImpl implements MapValuesTransformer {
        @Override
        public Map<String, Integer> strToInt(final Map<String, String> map) {
            final Map<String, Integer> ret = new HashMap<>();
            for (final Map.Entry<String, String> entry : map.entrySet()) {
                ret.put(entry.getKey(), Integer.valueOf(entry.getValue()));
            }
            return ret;
        }
    }

    /**
     *Vorschlag 2:Verwenden Sie die Java 8 Stream-API
     */
    static class StreamImpl implements MapValuesTransformer {
        @Override
        public Map<String, Integer> strToInt(final Map<String, String> map) {
            return map.entrySet().stream()
                    .collect(Collectors.toMap(Map.Entry::getKey, e -> Integer.valueOf(e.getValue())));
        }
    }

    /**
     *Vorschlag 3:Guaven-Karten::Verwenden Sie transformValues
     */
    static class TranformValuesImpl implements MapValuesTransformer {
        @Override
        public Map<String, Integer> strToInt(final Map<String, String> map) {
            return Maps.transformValues(map, Integer::valueOf);
        }
    }

    /**
     *Vorschlag 4:Guaven-Karten::Eine andere Lösung mit transformValues
     */
    static class CopyOfTranformValuesImpl implements MapValuesTransformer {
        @Override
        public Map<String, Integer> strToInt(final Map<String, String> map) {
            return ImmutableMap.copyOf(Maps.transformValues(map, Integer::valueOf));
        }
    }

}

Testcode-Skelett

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class MapValuesTramsformerTest {

    private final MapValuesTransformer transformer;

    public MapValuesTramsformerTest(MapValuesTransformer transformer) {
        this.transformer = transformer;
    }

    @Parameters(name = "{0}")
    public static Iterable<MapValuesTransformer> testClasses() {
        return Arrays.asList(
                new MapValuesTransformer.ForImpl(),
                new MapValuesTransformer.StreamImpl(),
                new MapValuesTransformer.TranformValuesImpl(),
                new MapValuesTransformer.CopyOfTranformValuesImpl()
        );
    }

    //Schreiben Sie hier verschiedene Tests
}

Testen Sie, ob der Wert konvertiert wurde

@Test
public void Wird der Wert konvertiert?() {
    Map<String, String> source = new HashMap<>();
    source.put("one", "1");
    source.put("zero", "0");
    source.put("min", "-2147483648");

    Map<String, Integer> expected = new HashMap<>();
    expected.put("one", 1);
    expected.put("zero", 0);
    expected.put("min", Integer.MIN_VALUE);

    // [Vorschlag 1: ForImpl] Passed
    // [Vorschlag 2: StreamImpl] Passed
    // [Vorschlag 3: TranformValuesImpl] Passed
    // [Vorschlag 4: CopyOfTranformValuesImpl] Passed
    assertThat(transformer.strToInt(source), is(expected));
}

Ich habe es sicher bestanden.

Testen Sie, ob die Reihenfolge erhalten bleibt

@Test
public void Wird die Bestellung aufrechterhalten?() {
    Map<String, String> source = new LinkedHashMap<>();
    source.put("one", "1");
    source.put("zero", "0");
    source.put("min", "-2147483648");

    Map<String, Integer> transformed = transformer.strToInt(source);

    // [Vorschlag 1: ForImpl] Expected: is "[one, zero, min]" but: was "[zero, min, one]"
    // [Vorschlag 2: StreamImpl] Expected: is "[one, zero, min]" but: was "[zero, min, one]"
    // [Vorschlag 3: TranformValuesImpl] Passed
    // [Vorschlag 4: CopyOfTranformValuesImpl] Passed
    assertThat(transformed.keySet().toString(), is(source.keySet().toString()));
}

(Obwohl es ein grober Test ist, das Ergebnis zu testen) Plan 1 und Plan 2 sind fehlgeschlagen.

Bei der Implementierung von Plan 1 und Plan 2 wird HashMap geändert, sodass LinkedHashMap und TreeMap Wenn Sie docs / api / java / util / TreeMap.html übergeben, geht die Garantie der Bestellung verloren.

Unveränderlicher Test

@Test(expected = UnsupportedOperationException.class)
public void Unveränderlich Ist es unveränderlich?() {
    Map<String, String> source = new HashMap<>();
    source.put("one", "1");

    Map<String, Integer> expected = new HashMap<>();
    expected.put("one", 1);

    Map<String, Integer> transformed = transformer.strToInt(source);

    // [Vorschlag 1: ForImpl] Failed
    // [Vorschlag 2: StreamImpl] Failed
    // [Vorschlag 3: TranformValuesImpl] Passed
    // [Vorschlag 4: CopyOfTranformValuesImpl] Passed
    transformed.put("two", 2);
}

Im Fall von Plan 3 und Plan 4, wenn die Put-Methode der konvertierten Map aufgerufen wird, [UnsupportedOperationException](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/UnsupportedOperationException] .html) wird geworfen.

Die Vorschläge 1 und 2 waren veränderlich und die Vorschläge 3 und 4 waren unveränderlich.

Testen Sie, ob die Karte kopiert wurde

@Test
Wird die öffentliche leere Karte kopiert?() {
    Map<String, String> source = new HashMap<>();
    source.put("one", "1");

    Map<String, Integer> expected = new HashMap<>();
    expected.put("one", 1);

    Map<String, Integer> transformed = transformer.strToInt(source);
    source.put("two", "2");

    // [Vorschlag 1: ForImpl] Passed
    // [Vorschlag 2: StreamImpl] Passed
    // [Vorschlag 3: TransformValuesImpl]: Expected: is <{one=1}> but: was <{one=1, two=2}>
    // [Vorschlag 4: CopyOfTranformValuesImpl] Passed
    assertThat(transformed, is(expected));
}

Nur bei der Implementierung von Plan 3 wurden die nach der Konvertierung an der Conversion-Quellkarte vorgenommenen Änderungen in der Conversion-Zielkarte berücksichtigt.

Dies ist [JavaDoc für Maps :: transformValues](https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/Maps.html#transformValues-java.util.Map- Wie in com.google.common.base.Function-) beschrieben, gibt diese Methode "eine Ansicht der Karte zurück".

Wenn Sie daher eine Kopie wünschen (Sie möchten die Änderungen an der Conversion-Quellzuordnung im Conversion-Ziel nicht widerspiegeln), wählen Sie ImmutableMap :: copyOf, wie in Vorschlag 4 gezeigt. Sie müssen mit -jre / api / docs / com / google / common / collect / ImmutableMap.html # copyOf-java.util.Map-) umbrechen.

Zusammenfassung der Testergebnisse

Testperspektive Vorschlag 1 Vorschlag 2 Vorschlag 3 Vorschlag 4
Wird der Wert konvertiert? Yes Yes Yes Yes
Wird die Bestellung beibehalten? No No Yes Yes
Immutable(Unveränderlich)Oder No No Yes Yes
Wird die Karte kopiert? Yes Yes No Yes

Wenn Ihnen [Vorteile der Unveränderlichkeit] bekannt sind (https://www.ibm.com/developerworks/jp/java/library/j-jtp02183/index.html), ist die Implementierungsmethode von Plan 3 oder Plan 4 möglicherweise besser. ?? Es scheint, dass.

Referenz-URL

Recommended Posts

Karte <K, V1> in Karte <K, V2> konvertieren (Kartenwert konvertieren)
[Java] Konvertiere 1 in N Liste in Karte
[Core ML] So konvertieren Sie YOLO v3 in Core ML
[Java] Konvertieren Sie DB-Code mit enum in einen Codewert
Verwendung von Map
Verwendung der Karte
Gelernter Stream (Ich möchte List in Map <Integer, List> konvertieren)
Verwendung von Map
Für Java-Anfänger: Liste, Karte, Iterator / Array ... Wie konvertiere ich?
[Java] Verwendung von Map
9 Entspricht dem Rückgabewert
Ich möchte Zeichen konvertieren ...
Konvertieren Sie den Zeichenfolgentyp in den Zeitstempeltyp
In Ruby Leet-Zeichenfolge konvertieren
Konvertieren Sie ein serialisierbares Objekt in Byte []
Verwendung von Java Map
Konvertieren Sie von ○ Monaten in ○ Jahre ○ Monate
So konvertieren Sie Java Base
[Java] Konvertiert ArrayList in Array
[Android] Konvertieren Sie Map in JSON mit GSON mit Kotlin und Java
So erhöhen Sie den Wert von Map in einer Zeile in Java