Hoe een HashMap direct initialiseren (letterlijk)?

Is er een manier om een ​​Java HashMap op deze manier te initialiseren?:

Map<String,String> test = 
    new HashMap<String, String>{"test":"test","test":"test"};

Wat zou de juiste syntaxis zijn? Ik heb hierover niets gevonden. Is dit mogelijk? Ik ben op zoek naar de kortste/snelste manier om enkele “definitieve/statische” waarden in een kaart te zetten die nooit veranderen en die van tevoren bekend zijn bij het maken van de kaart.


Antwoord 1, autoriteit 100%

Alle versies

Voor het geval je maar één invoer nodig hebt: er is Collections.singletonMap("key", "value").

Voor Java versie 9 of hoger:

Ja, dit is nu mogelijk. In Java 9 zijn een aantal fabrieksmethoden toegevoegd die het maken van kaarten vereenvoudigen:

// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
    "a", "b",
    "c", "d"
);
// this works for any number of elements:
import static java.util.Map.entry;    
Map<String, String> test2 = Map.ofEntries(
    entry("a", "b"),
    entry("c", "d")
);

In het bovenstaande voorbeeld zijn zowel testals test2hetzelfde, alleen met verschillende manieren om de kaart uit te drukken. De methode Map.ofis gedefinieerd voor maximaal tien elementen in de kaart, terwijl de methode Map.ofEntrieseen dergelijke limiet niet heeft.

Houd er rekening mee dat in dit geval de resulterende kaart een onveranderlijke kaart zal zijn. Als u wilt dat de kaart veranderlijk is, kunt u deze opnieuw kopiëren, b.v. met behulp van mutableMap = new HashMap<>(Map.of("a", "b"));

(Zie ook JEP 269en de Javadoc)

Voor maximaal Java versie 8:

Nee, u moet alle elementen handmatig toevoegen. U kunt een initializer in een anonieme subklasse gebruiken om de syntaxis een beetje korter te maken:

Map<String, String> myMap = new HashMap<String, String>() {{
        put("a", "b");
        put("c", "d");
    }};

In sommige gevallen kan de anonieme subklasse echter ongewenst gedrag introduceren. Dit omvat bijvoorbeeld:

  • Het genereert een extra klasse die het geheugenverbruik, het schijfruimteverbruik en de opstarttijd verhoogt
  • In het geval van een niet-statische methode: deze bevat een verwijzing naar het object waarop de aanmaakmethode werd aangeroepen. Dat betekent dat het object van de buitenste klasse niet kan worden verzameld terwijl er nog steeds naar het gemaakte kaartobject wordt verwezen, waardoor extra geheugen wordt geblokkeerd

Het gebruik van een functie voor initialisatie stelt u ook in staat om een ​​kaart in een initializer te genereren, maar vermijdt vervelende bijwerkingen:

Map<String, String> myMap = createMap();
private static Map<String, String> createMap() {
    Map<String,String> myMap = new HashMap<String,String>();
    myMap.put("a", "b");
    myMap.put("c", "d");
    return myMap;
}

Antwoord 2, autoriteit 65%

Dit is één manier.

Map<String, String> h = new HashMap<String, String>() {{
    put("a","b");
}};

Je moet echter voorzichtig zijn en ervoor zorgen dat je de bovenstaande code begrijpt (het creëert een nieuwe klasse die erft van HashMap). Lees daarom hier meer:
http://www.c2.com/cgi/wiki?DoubleBraceInitialization
, of gebruik gewoon Guava:

Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);

ImmutableMap.ofwerkt voor maximaal 5 ingangen. Gebruik anders de builder: bron .


Antwoord 3, autoriteit 21%

Als u bibliotheken van derden toestaat, kunt u Guava‘s ImmutableMapom letterlijke- zoals beknoptheid:

Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");

Dit werkt voor maximaal 5 sleutel/waarde-paren, anders kunt u de builder:

Map<String, String> test = ImmutableMap.<String, String>builder()
    .put("k1", "v1")
    .put("k2", "v2")
    ...
    .build();


Antwoord 4, autoriteit 6%

Er is geen directe manier om dit te doen – Java heeft geen letterlijke kaarten van de kaart (nog – ik denk dat ze zijn voorgesteld voor Java 8).

Sommige mensen vinden dit leuk:

Map<String,String> test = new HashMap<String, String>(){{
       put("test","test"); put("test","test");}};

Dit creëert een anonieme subklasse van HashMap, waarvan de instantie-initialisatie deze waarden plaatst. (Trouwens, een kaart kan niet twee keer dezelfde waarde bevatten, je tweede put zal de eerste overschrijven. Ik zal andere waarden gebruiken voor de volgende voorbeelden.)

De normale manier zou zijn (voor een lokale variabele):

Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");

Als uw test-kaart een instantievariabele is, plaatst u de initialisatie in een constructor- of instantie-initialisator:

Map<String,String> test = new HashMap<String, String>();
{
    test.put("test","test");
    test.put("test1","test2");
}

Als uw test-kaart een klassevariabele is, plaatst u de initialisatie in een statische initialisatie:

static Map<String,String> test = new HashMap<String, String>();
static {
    test.put("test","test");
    test.put("test1","test2");
}

Als je wilt dat je kaart nooit verandert, moet je je kaart na de initialisatie inpakken met Collections.unmodifiableMap(...). U kunt dit ook in een statische initializer doen:

static Map<String,String> test;
{
    Map<String,String> temp = new HashMap<String, String>();
    temp.put("test","test");
    temp.put("test1","test2");
    test = Collections.unmodifiableMap(temp);
}

(Ik weet niet zeker of je de testnu definitief kunt maken … probeer het uit en rapporteer hier.)


Antwoord 5, autoriteit 4%

Map<String,String> test = new HashMap<String, String>()
{
    {
        put(key1, value1);
        put(key2, value2);
    }
};

Antwoord 6, autoriteit 3%

Een alternatief, met gewone Java 7-klassen en varargs: maak een klasse HashMapBuildermet deze methode:

public static HashMap<String, String> build(String... data){
    HashMap<String, String> result = new HashMap<String, String>();
    if(data.length % 2 != 0) 
        throw new IllegalArgumentException("Odd number of arguments");      
    String key = null;
    Integer step = -1;
    for(String value : data){
        step++;
        switch(step % 2){
        case 0: 
            if(value == null)
                throw new IllegalArgumentException("Null key value"); 
            key = value;
            continue;
        case 1:             
            result.put(key, value);
            break;
        }
    }
    return result;
}

Gebruik de methode als volgt:

HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");

Antwoord 7

JAVA 8

In gewone Java 8 heb je ook de mogelijkheid om Streams/Collectorste gebruiken om het werk te doen.

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", "value2"),
         new SimpleEntry<>("key3", "value3"))
        .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue));

Dit heeft het voordeel dat er geen anonieme klas wordt gemaakt.

Merk op dat de importen zijn:

import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;

Natuurlijk, zoals opgemerkt in andere antwoorden, heb je vanaf Java 9 eenvoudigere manieren om hetzelfde te doen.


Antwoord 8

tl;dr

Gebruik Map.of…-methoden in Java 9 en hoger.

Map< String , String > animalSounds =
    Map.of(
        "dog"  , "bark" ,   // key , value
        "cat"  , "meow" ,   // key , value
        "bird" , "chirp"    // key , value
    )
;

Map.of

Java 9 heeft een reeks Map.ofstatische methoden om precies te doen wat u wilt: Maak een onveranderlijke Mapmet behulp van letterlijke syntaxis.

De kaart (een verzameling items) is onveranderlijk, dus je kunt geen items toevoegen of verwijderen na het instantiëren. Ook zijn de sleutel en de waarde van elk item onveranderlijk en kunnen niet worden gewijzigd. Zie de Javadocvoor andere regels , zoals geen NULL’s toegestaan, geen dubbele sleutels toegestaan, en de iteratievolgorde van toewijzingen is willekeurig.

Laten we eens kijken naar deze methoden, waarbij we enkele voorbeeldgegevens gebruiken voor een kaart van de dag van de week voor een persoon van wie we verwachten dat die op die dag zal werken.

Person alice = new Person( "Alice" );
Person bob = new Person( "Bob" );
Person carol = new Person( "Carol" );

Map.of()

Map.ofmaakt een lege Mapaan. Onwijzigbaar, dus u kunt geen items toevoegen. Hier is een voorbeeld van zo’n kaart, leeg zonder invoer.

Map < DayOfWeek, Person > dailyWorkerEmpty = Map.of();

dailyWorkerEmpty.toString(): {}

Map.of( … )

Map.of( k , v , k , v , …)zijn verschillende methoden waarvoor 1 tot 10 sleutel-waardeparen nodig zijn. Hier is een voorbeeld van twee items.

Map < DayOfWeek, Person > weekendWorker = 
        Map.of( 
            DayOfWeek.SATURDAY , alice ,     // key , value
            DayOfWeek.SUNDAY , bob           // key , value
        )
;

weekendWorker.toString(): {SUNDAY=Persoon{ name=’Bob’ }, SATURDAY=Persoon{ name=’Alice’ }}

Map.ofEntries( … )

Map.ofEntries( Map.Entry , … )neemt een willekeurig aantal objecten die de Map.Entry-interface. Java bundelt twee klassen die die interface implementeren, de ene veranderlijk, de andere onveranderlijk: AbstractMap.SimpleEntry, AbstractMap.SimpleImmutableEntry. Maar we hoeven geen concrete klasse te specificeren. We hoeven alleen maar Map.entry( k , v )methode, geef onze sleutel en onze waarde door, en we krijgen een object terug van een klasse die Map.Entry-interface.

Map < DayOfWeek, Person > weekdayWorker = Map.ofEntries(
        Map.entry( DayOfWeek.MONDAY , alice ) ,            // Call to `Map.entry` method returns an object implementing `Map.Entry`. 
        Map.entry( DayOfWeek.TUESDAY , bob ) ,
        Map.entry( DayOfWeek.WEDNESDAY , bob ) ,
        Map.entry( DayOfWeek.THURSDAY , carol ) ,
        Map.entry( DayOfWeek.FRIDAY , carol )
);

weekdayWorker.toString(): {WEDNESDAY=Persoon{ name=’Bob’ }, TUESDAY=Persoon{ name=’Bob’ }, THURSDAY=Persoon{ name=’Carol’ }, FRIDAY=Persoon{ name=’ Carol’ }, MONDAY=Persoon{ name=’Alice’ }}

Map.copyOf

Java 10 heeft de methode Map.copyOf. Geef een bestaande kaart door, krijg een onveranderlijke kopie van die kaart terug.

Opmerkingen

Merk op dat de iteratorvolgorde van kaarten geproduceerd via Map.ofnietgegarandeerd is. De vermeldingen hebben een willekeurige volgorde. Schrijf geen code op basis van de geziene bestelling, aangezien de documentatie waarschuwt dat de bestelling kan worden gewijzigd.

Merk op dat al deze Map.of…-methoden een Mapvan een niet-gespecificeerde klasse. De onderliggende concrete klasse kan zelfs verschillen van de ene versie van Java tot de andere. Deze anonimiteit stelt Java in staat om uit verschillende implementaties te kiezen, wat het beste bij uw specifieke gegevens past. Als uw sleutels bijvoorbeeld afkomstig zijn van een enum, Java kan een EnumMaponder de dekens.


Antwoord 9

We kunnen AbstractMap Class gebruiken met SimpleEntry waarmee een onveranderlijke kaart kan worden gemaakt

Map<String, String> map5 = Stream.of(
    new AbstractMap.SimpleEntry<>("Sakshi","java"),
    new AbstractMap.SimpleEntry<>("fine","python")
    ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    System.out.println(map5.get("Sakshi"));
    map5.put("Shiva", "Javascript");
    System.out.println(map5.get("Shiva"));// here we can add multiple entries.

Antwoord 10

U kunt mogelijk uw eigen Map.of(die alleen beschikbaar is in Java 9 en hoger) gemakkelijk op 2 eenvoudige manieren maken

Maak het met een vast aantal parameters

Voorbeeld

public <K,V> Map<K,V> mapOf(K k1, V v1, K k2, V v2 /* perhaps more parameters */) {
    return new HashMap<K, V>() {{
      put(k1, v1);
      put(k2,  v2);
      // etc...
    }};
}

Maak het met behulp van een lijst

Je kunt dit ook maken met behulp van een lijst, in plaats van veel methoden te maken voor een bepaalde set parameters.

Voorbeeld

public <K, V> Map<K, V> mapOf(List<K> keys, List<V> values) {
   if(keys.size() != values.size()) {
        throw new IndexOutOfBoundsException("amount of keys and values is not equal");
    }
    return new HashMap<K, V>() {{
        IntStream.range(0, keys.size()).forEach(index -> put(keys.get(index), values.get(index)));
    }};
}

Opmerking
Het wordt niet aanbevolen om dit voor alles te gebruiken, omdat dit elke keer dat je dit gebruikt een anonieme les maakt.


Antwoord 11

Als u slechts één sleutel-waardepaar hoeft te plaatsen, kunt u Collections.singletonMap(key, value);

gebruiken


Antwoord 12

Met Java 8 of minder

U kunt een statisch blok gebruiken om een ​​kaart met enkele waarden te initialiseren. Voorbeeld :

public static Map<String,String> test = new HashMap<String, String>
static {
    test.put("test","test");
    test.put("test1","test");
}

Met Java 9 of meer

U kunt de methode Map.of() gebruiken om een ​​kaart te initialiseren met enkele waarden tijdens het declareren. Voorbeeld :

public static Map<String,String> test = Map.of("test","test","test1","test");

Antwoord 13

Eenvoudige manier om dit te doen:

public static Map<String, String> mapWithValues(String...values) {
    Map<String, String> map = new HashMap<String, String>();
    for (int x = 0; x < values.length; x = x+2) {
        map.put(values[x], values[x+1]);
    }
    return map;
}

Antwoord 14

Als het een instantievariabele is, dan is een instantie-initialisatieblok zeker de juiste keuze, vooral als u Map.of()niet kunt gebruiken omdat u een ander type kaart nodig heeft.

Maar als u zich dartel voelt, kunt u een Java 8 Suppliergebruiken (niet aanbevolen).

private final Map<String,Runnable> games = ((Supplier<Map<String,Runnable>>)() -> {
  Map<String,Runnable> map = new LinkedHashMap<>();
  map.put("solarus",this::playSolarus);
  map.put("lichess",this::playLichess);
  return map;
}).get();

Of maak je eigen functionele interface (lijkt mij prima):

@FunctionalInterface
public interface MapMaker<M> {
  static <M extends Map<K,V>,K,V> M make(M map,MapMaker<M> maker) {
    maker.build(map);
    return map;
  }
  void build(M map);
}
// Can use LinkedHashMap!
private final Map<String,Runnable> games = MapMaker.make(
    new LinkedHashMap<>(),(map) -> {
      map.put("solarus",this::playSolarus);
      map.put("lichess",this::playLichess);
    });

Antwoord 15

Helaas is het niet erg redelijk om varargs te gebruiken als het type sleutels en waarden niet hetzelfde zijn, omdat je Object...zou moeten gebruiken en de typeveiligheid volledig zou verliezen. Als u altijd b.v. een Map<String, String>, natuurlijk een toMap(String... args)zou wel mogelijk zijn, maar niet erg mooi omdat het gemakkelijk door elkaar te halen zou zijn sleutels en waarden, en een oneven aantal argumenten zou ongeldig zijn.

Je zou een subklasse van HashMap kunnen maken met een ketenbare methode zoals

public class ChainableMap<K, V> extends HashMap<K, V> {
  public ChainableMap<K, V> set(K k, V v) {
    put(k, v);
    return this;
  }
}

en gebruik het als new ChainableMap<String, Object>().set("a", 1).set("b", "foo")

Een andere benadering is om het algemene bouwpatroon te gebruiken:

public class MapBuilder<K, V> {
  private Map<K, V> mMap = new HashMap<>();
  public MapBuilder<K, V> put(K k, V v) {
    mMap.put(k, v);
    return this;
  }
  public Map<K, V> build() {
    return mMap;
  }
}

en gebruik het als new MapBuilder<String, Object>().put("a", 1).put("b", "foo").build();

De oplossing die ik af en toe heb gebruikt, maakt echter gebruik van varargs en de klasse Pair:

public class Maps {
  public static <K, V> Map<K, V> of(Pair<K, V>... pairs) {
    Map<K, V> = new HashMap<>();
    for (Pair<K, V> pair : pairs) {
      map.put(pair.first, pair.second);
    }
    return map;
  }
}

Map<String, Object> map = Maps.of(Pair.create("a", 1), Pair.create("b", "foo");

De breedsprakigheid van Pair.create()stoort me een beetje, maar dit werkt prima. Als je statische imports niet erg vindt, kun je natuurlijk een helper maken:

public <K, V> Pair<K, V> p(K k, V v) {
  return Pair.create(k, v);
}

Map<String, Object> map = Maps.of(p("a", 1), p("b", "foo");

(In plaats van Pairzou men zich kunnen voorstellen om Map.Entryte gebruiken, maar aangezien het een interface is, vereist het een implementatieklasse en/of een helper-fabrieksmethode. Het is ook niet onveranderlijk, en bevat andere logica die niet nuttig is voor deze taak.)


Antwoord 16

U kunt Streams gebruiken in Java 8 (dit is een voorbeeld van Set):

@Test
public void whenInitializeUnmodifiableSetWithDoubleBrace_containsElements() {
    Set<String> countries = Stream.of("India", "USSR", "USA")
      .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));
    assertTrue(countries.contains("India"));
}

Ref: https://www.baeldung.com/java-double-brace -initialisatie


Antwoord 17

U kunt een methode maken om de kaart te initialiseren zoals in dit voorbeeld hieronder:

Map<String, Integer> initializeMap()
{
  Map<String, Integer> ret = new HashMap<>();
  //populate ret
  ...
  return ret;
}
//call
Map<String, Integer> map = initializeMap();

Antwoord 18

We kunnen hashmap op de volgende manieren initialiseren:

  1. HashMap met Constructor

    Kaart<String, String> hashMap = nieuwe HashMap<String, String>();

    hashMap.put("hcl", "amit");
     hashMap.put("tcs","ravi");
    

Hashmap heeft vier verschillende typen constructors, dus we kunnen deze initialiseren volgens onze
vereiste. Gebruikt nu HashMap(int initialCapacity) constructor

   Map<String, String> hashMap = new HashMap<String, String>(3);
    hashMap.put("virat", "cricket");
    hashMap.put("amit","football");

Referentie: Hoe HashMap te maken

  1. Singleton HashMaps met verzamelingen

     Map<String, String> immutableMap = Collections.singletonMap("rohit", 
           "cricket");
    
  2. HashMaps leegmaken met verzamelingen

     Map<String, String> emptyMap = Collections.emptyMap();
    
  3. Anonieme subklasse om HashMap te maken

      Map<String, String> map = new HashMap<String, String>() {{
         put("hcl", "amit");
         put("tcs","ravi");
         put("wipro","anmol");
        }};
    

Antwoord 19

Op basis van het antwoord van @johnny-willer kun je geen kaart met null-waarden krijgen op Java 8 omdat Collectors.toMapafhankelijk is van Map.merge. Deze methode verwacht geen null-waarden, dus het genereert een NullPointerExceptionzoals beschreven in dit bugrapport: https://bugs.openjdk.java.net/browse/JDK-8148463

Een alternatieve manier om een ​​kaart met null-waarden te krijgen met behulp van een builder-syntaxis op Java 8 is het schrijven van een inline collector:

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", (String) null),
         new SimpleEntry<>("key3", "value3"))
        .collect(HashMap::new,
                (map, entry) -> map.put(entry.getKey(),
                                        entry.getValue()),
                HashMap::putAll);

Deze implementatie vervangt ook een waarde als de sleutel meerdere keren voorkomt. De standaard Collectors.toMap, zonder een aangepaste samenvoegfunctie zoals (prev, next) -> next, zal in plaats daarvan een IllegalStatExceptiongenereren.


Antwoord 20

De volgende code kan het lukken in Java 8:

Map<String, Integer> map = Stream.of(new Object[][] { 
     { "data1", 1 }, 
     { "data2", 2 }, 
 }).collect(Collectors.toMap(data -> (String) data[0], data -> (Integer) data[1]));

tegoeden:

Other episodes