Bir HashMap'i doğrudan nasıl başlatabilirim (gerçek anlamda)?


1093

Bunun gibi bir Java HashMap başlatmanın bir yolu var mı ?:

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

Doğru sözdizimi ne olurdu? Bununla ilgili hiçbir şey bulamadım. Mümkün mü? Asla değişmez ve Harita oluştururken önceden bilinen bir harita bazı "nihai / statik" değerleri koymak için en kısa / en hızlı yolu arıyorum.



Yakından ilgili: stackoverflow.com/questions/507602/… (Her iki soru da statik, son değerlerle sabit bir haritanın başlatılmasıyla ilgilidir.)
Jonik



Yanıtlar:


1343

Tüm Sürümler

Sadece tek bir girişe ihtiyacınız varsa: Var Collections.singletonMap("key", "value").

Java Sürüm 9 veya üstü için:

Evet, şimdi mümkün. Java 9'da haritaların oluşturulmasını kolaylaştıran birkaç fabrika yöntemi eklendi:

// 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")
);

Her iki Yukarıdaki örnekte testve test2sadece Harita ifade farklı şekillerde ile aynı olacaktır. Map.ofİse yöntem, haritadaki on elemanlarına up için tanımlanan Map.ofEntriesyöntem böyle sınırı olacaktır.

Bu durumda ortaya çıkan haritanın değişmez bir harita olacağını unutmayın. Haritanın değiştirilebilir olmasını isterseniz, örneğin kopyasını kullanarak tekrar kopyalayabilirsiniz.mutableMap = new HashMap<>(Map.of("a", "b"));

(Ayrıca bkz.JEP 269 ve Javadoc )

Java Sürüm 8'e kadar:

Hayır, tüm öğeleri manuel olarak eklemeniz gerekecek. Sözdizimini biraz daha kısaltmak için anonim bir alt sınıfta bir başlatıcı kullanabilirsiniz:

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

Ancak, anonim alt sınıf bazı durumlarda istenmeyen davranışlara neden olabilir. Bu, örneğin şunları içerir:

  • Bellek tüketimini, disk alanı tüketimini ve başlatma süresini artıran ek bir sınıf oluşturur
  • Statik olmayan bir yöntem durumunda: Oluşturma yönteminin çağrıldığı nesneye bir referans tutar. Bu, oluşturulan harita nesnesine hala başvurulurken dış sınıfın nesnesine çöp toplanamayacağı ve dolayısıyla ek belleği engelleyemeyeceği anlamına gelir

Başlatma için bir işlev kullanmak, bir başlatıcıda bir harita oluşturmanıza da olanak tanır, ancak kötü yan etkilerden kaçınır:

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;
}

3
Bir işlevdeki öğeleri başlatmak istiyorsanız bu işe yaramaz ...
Michael

9
@Michael: Evet, eğer bir işlevi kullanmak istemiyorsanız, bir işlevi kullanamazsınız. Ama neden istiyorsun?
yankee

6
ve tek girişli bir Haritaya ihtiyaç duyduğunuz durumlar için Collections.singletonMap():)
skwisgaar

3
Artık kararlı Java 9 yayınlandı, Javadoc için bu bağlantıyı tercih ediyorum . Ve +1 çünkü daha az bağımlılık!
Franklin Yu

3
Java 9 nerede entrybelgelenir?
nobar

1029

Bu bir yol.

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

Ancak, dikkatli olmanız ve yukarıdaki kodu anladığınızdan emin olmanız gerekir (HashMap'ten devralan yeni bir sınıf oluşturur). Bu nedenle, burada daha fazlasını okumalısınız: http://www.c2.com/cgi/wiki?DoubleBraceInitialization veya sadece Guava kullanın:

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

72
Çalışıyor ama çirkin ve kullanıcının yapmadan önce anlaması gereken görünmez yan etkileri var - örneğin, yerinde anonim bir sınıf oluşturmak.
jprete

96
Evet, bu yüzden dikkatli olmak için yazdım ve açıklamaya bir bağlantı verdim.
gregory561

6
Harika bağlantı. Bu linkteki GreencoddsTenthRuleOfProgramming referansı okunmaya değer.
michaelok

19
"olarak" yöntemi maksimum 5 çiftle sınırlı olduğundan "ImmutableMap.builder.put (" k1 "," v1 "). put (" k2 "," v2 "). build ()" ekleyebilir misiniz?
kommradHomer


342

3. taraf kütüphanelerini izin verirseniz, kullanabilirsiniz Guava 'nın ImmutableMap kısalığı değişmez benzeri ulaşmak için:

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

Bu, 5 anahtar / değer çiftine kadar çalışır , aksi takdirde oluşturucusunu kullanabilirsiniz :

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



26
Ayrıca, guava'nın ImmutableMap.builder.put ("k1", "v1"). Put ("k2", "v2"). Build ();
Xetius

17
ImmutableMap, null değerlerinde başarısız olacağı için HashMap ile aynı değildir, oysa HashMap eşlemede olmaz.
Gewthen

2
Sadece bu sorunla karşılaşabilecek başkalarına yardım etmek için. Bir Harita <String, String> yapmak için yapıcıyı şu şekilde yazmanız gerekir: Map <String, String> test = ImmutableMap. <String, String> builder (). Put ("k1", "v1"). put ("k2", "v2"). build ();
Thiago

Bu harika Jens!
gaurav

105

Bunu yapmanın doğrudan bir yolu yok - Java'nın Harita değişmezleri yok (henüz - Java 8 için önerildiklerini düşünüyorum).

Bazı insanlar böyle:

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

Bu, örnek başlatıcısı bu değerleri koyan HashMap'in anonim bir alt sınıfını oluşturur. (Bu arada, bir harita aynı değerin iki katını içeremez, ikinci koyunuz birincisinin üzerine yazacaktır. Sonraki örnekler için farklı değerler kullanacağım.)

Normal yol şu şekildedir (yerel bir değişken için):

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

Senin Eğer testharita bir örnek değişkendir, bir kurucu ya da örnek in başlatıcısı başlatma koyun:

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

Senin Eğer testharita sınıf değişkendir, statik in başlatıcısı başlatma koyun:

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

Haritanızın hiçbir zaman değişmemesini istiyorsanız, başlattıktan sonra haritanızı tamamlayın Collections.unmodifiableMap(...). Bunu statik bir başlatıcıda da yapabilirsiniz:

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);
}

(Şimdi testfinal yapıp yapamayacağınızdan emin değilim ... deneyin ve burada bildirin.)


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

Basit ve noktaya. Bence bu geniş bir yorum bölümü ile en iyi cevap olacaktır.
ooolala

15
Yine de dikkat edilmesi gereken bellek etkileri vardır. blog.jooq.org/2014/12/08/…
Amalgovinus

1
@Amalgovinus Temel olarak, yeni bir alt sınıf oluşturarak, tür argümanlarını HashMapbu alt sınıfa zor kodlarsınız . Bu yalnızca onları gerçekten sağladıysanız işe yarayabilir. (Yeni (boş) bir HashMap ile tür argümanları ilgili değildir.)
Paŭlo Ebermann

1
Temizliğini seviyorum, ancak gereksiz anonim sınıf yaratıyor ve burada açıklanan sorunları var: c2.com/cgi/wiki?DoubleBraceInitialization
udachny

1
@hello_its_me: Çünkü bunun stackoverflow.com/a/6802512/1386911 yanıtı ile aynı , sadece biçimlendirme farklı. Ve bu durumda, bu genişletilmiş biçimlendirmenin okunabilirlik için kompakt biçimin üstünde ek bir değeri yoktur.
Daniel Hári

44

Düz Java 7 sınıfları ve varargs: kullanan bir alternatif: HashMapBuilderbu yöntemle bir sınıf oluşturun :

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;
}

Bunun gibi bir yöntem kullanın:

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

Sizinkilerden esinlenerek bir cevap yazdım: stackoverflow.com/questions/507602/…
GeroldBroser,

1
Apache Utils ile daha önce bahsedilmeyen ancak okunabilen, önceki Java sürümleri kullanılarak okunabilen başka bir çözüm: MapUtils.putAll (yeni HashMap <String, String> (), new Object [] {"My key", "my value", ...
Rolintocour

4

tl; Dr.

Map.of…Java 9 ve sonraki sürümlerinde yöntemleri kullanın .

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

Map.of

Java 9 bir dizi ilave Map.ofstatik yöntemlerle istediğiniz sadece ne yapmalı: örneğini değişmez bir Mapkullanarak değişmez sözdizimi .

Harita (bir girdi koleksiyonu) değiştirilemez, bu nedenle somutlaştırdıktan sonra giriş ekleyemez veya kaldıramazsınız. Ayrıca, her girişin anahtarı ve değeri değiştirilemez, değiştirilemez. NULL değerine izin verilmeyen, yinelenen anahtarlara izin verilmeyen ve eşlemlerin yineleme sırası gibi diğer kurallar için Javadoc'a bakın .

O gün çalışacağını düşündüğümüz bir kişiye haftanın günü haritası için bazı örnek verileri kullanarak bu yöntemlere bakalım.

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

Map.of()

Map.ofboş oluşturur Map. Değiştirilemez, bu nedenle girdi ekleyemezsiniz. Aşağıda, girişsiz boş bir harita örneği verilmiştir.

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

dailyWorkerEmpty.toString (): {}

Map.of( … )

Map.of( k , v , k , v , …)1 ila 10 anahtar / değer çifti alan birkaç yöntemdir. İşte iki girişin bir örneği.

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

weekendWorker.toString (): {PAZAR = Kişi {name = 'Bob'}, CUMARTESİ = Kişi {name = 'Alice'}}

Map.ofEntries( … )

Map.ofEntries( Map.Entry , … )Map.Entryarabirimi uygulayan herhangi bir sayıda nesneyi alır . Java bu arabirimi uygulayan iki sınıfı bir araya getirir, biri değiştirilebilir, diğeri değişmez: AbstractMap.SimpleEntry, AbstractMap.SimpleImmutableEntry. Ancak somut bir sınıf belirtmemize gerek yok. Sadece Map.entry( k , v )yöntemi çağırmamız , anahtarımızı ve değerimizi geçirmemiz gerekiyor ve bazı sınıf uygulama Map.Entryarayüzünün bir nesnesini geri alıyoruz .

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 = Kişi {name = 'Bob'}, SALI = Kişi {name = 'Bob'}, PERŞEMBE = Kişi {name = 'Carol'}, CUMA = Kişi {name = 'Carol'} , PAZARTESİ = Kişi {name = 'Alice'}}

Map.copyOf

Java 10 yöntemi ekledi Map.copyOf. Mevcut bir haritayı geçin, haritanın değişmez bir kopyasını geri alın.

notlar

Bildirim haritaların yineleyici sırası üretilen üzeri Map.ofolan değil garantili. Girişlerin keyfi bir sırası var. Belgeler siparişin değişebileceği konusunda uyardığı için, görülen sıraya göre kod yazmayın.

Bunların hepsi unutmayın Map.of…yöntemlerin bir dönüş Mapait belirtilmeyen bir sınıf . Temel beton sınıfı, Java'nın bir sürümünden diğerine bile değişebilir. Bu anonimlik, Java'nın verilerinize en uygun olanı olan çeşitli uygulamalar arasından seçim yapmasını sağlar. Örneğin, anahtarlarınız bir numaradan geliyorsa , Java EnumMapkapakların altında bir kullanabilirsiniz .


1

Muhtemelen kendi Map.ofyönteminizi (yalnızca Java 9 ve üstü sürümlerde mevcuttur) 2 kolay yolla kolayca yapabilirsiniz

Belirli miktarda parametre ile yapın

Misal

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

Bir Liste kullanarak yap

Bunu, belirli bir parametre seti için birçok yöntem yapmak yerine bir liste kullanarak da yapabilirsiniz.

Misal

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)));
    }};
}

Not Bunu her kullandığınızda anonim bir sınıf haline getirdiğinden, bunu her şey için kullanmanız önerilmez.


1

JAVA 8

Düz java 8'de de Streams/Collectorsişi yapmak için kullanma olanağına sahipsiniz .

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

Bunun Anonim bir sınıf yaratmama avantajı vardır.

İthalatın aşağıdaki gibi olduğuna dikkat edin:

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

Tabii ki, diğer cevaplarda belirtildiği gibi, java 9'dan itibaren aynı şeyi yapmanın daha basit yollarına sahipsiniz.


0

Ne yazık ki, anahtarların ve değerlerin türü aynı değilse varargs kullanmak Object..., tür güvenliğini tamamen kullanmanız ve kaybetmeniz gerektiği için çok makul değildir . Her zaman örneğin bir a oluşturmak istiyorsanız Map<String, String>, elbette a toMap(String... args)mümkün olabilir, ancak anahtarları ve değerleri karıştırmak kolay olacağı için çok hoş olmaz ve tek sayıda argüman geçersiz olur.

Gibi zincirlenebilir bir yöntemi olan HashMap alt sınıfı oluşturabilirsiniz

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

ve onu gibi kullan new ChainableMap<String, Object>().set("a", 1).set("b", "foo")

Başka bir yaklaşım, ortak oluşturucu modelini kullanmaktır:

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;
  }
}

ve onu gibi kullan new MapBuilder<String, Object>().put("a", 1).put("b", "foo").build();

Ancak, şimdi ve sonra kullandığım çözüm varargs ve Pairsınıf kullanır :

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");

Bir ayrıntı Pair.create()oldukça ince beni rahatsız biraz, ama bu işler. Statik ithalatı önemsemiyorsanız elbette bir yardımcı oluşturabilirsiniz:

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");

( PairBiri kullanmak yerine hayal edebiliyordu Map.Entry, ancak bir arayüz olduğu için bir uygulama sınıfı ve / veya bir yardımcı fabrika yöntemi gerektiriyor. Ayrıca değişmez değil ve bu görev için yararlı olmayan başka mantık içeriyor.)


0

Java 8'de Streams'i kullanabilirsiniz (bu, Set'in örneğidir):

@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-initialization


0

Yalnızca bir anahtar / değer çifti yerleştirmeniz gerekiyorsa Collections.singletonMap (anahtar, değer);


1
biçimlendirme kodu yazı çok daha okunabilir hale getirir
Renato
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.