Statik bir haritayı nasıl başlatabilirim?


1131

MapJava'da statik bir durumu nasıl başlatırsınız ?

Birinci yöntem: statik başlatıcı İkinci
yöntem: örnek başlatıcı (anonim alt sınıf) veya başka bir yöntem?

Her birinin artıları ve eksileri nelerdir?

İki yöntemi gösteren bir örnek:

import java.util.HashMap;
import java.util.Map;

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<>(){
        {
            put(1, "one");
            put(2, "two");
        }
    };
}

2
Java 8'de bir haritayı başlatmak için: stackoverflow.com/a/37384773/1216775
akhil_mittal

2
Lütfen, asla çift ​​ayraç başlatmayı kullanmayın - bu bir hack ve bellek sızıntısı ve diğer sorunlara neden olmanın kolay bir yoludur.
dimo414

Java 9? Giriş sayısı <= 10 ise Map.ofbaşka kullanın Map.ofEntries, stackoverflow.com/a/37384773/1216775
akhil_mittal

Yanıtlar:


1106

Örnek başlatıcısı bu durumda sadece sözdizimsel şekerdir, değil mi? Sadece başlatmak için neden ekstra bir anonim sınıfa ihtiyacınız olduğunu anlamıyorum. Ve yaratılan sınıf nihai ise işe yaramaz.

Statik bir başlatıcı kullanarak da değişmez bir harita oluşturabilirsiniz:

public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

10
Bu yıllardır kullandığım deyim ve hiç kimsenin gözüne çarpmadı. Aynı şeyi değiştirilemez sabit Kümeler ve Listeler için de yaparım.
jasonmp85

3
Bir HashMap <String, String> ile bir String anahtarını nasıl işleyebilirim. Map nesnesi bir String anahtarına izin vermiyor, bu yüzden unmodifiableMap () kullanamıyorum. Ben bir HashMap döküm de yenilgi olurdu sanırım. Herhangi bir fikir?
Luke

30
@ Android'in böyle bir sınırlaması olduğundan şüpheliyim. Hiç mantıklı değil. Hızlı bir arama bu soruyu burada (ve diğer birçoklarında) buldu; bu , Android'de bir Harita nesnesi için bir String anahtarı kullanabileceğinizi ima ediyor gibi görünüyor.
mluisbrown

11
Bu yüzden başka hiç kimse araştırmayı rahatsız etmiyor, Android'de bir Harita nesnesi için bir String anahtarı kullanmanın herhangi bir sorunu olmadığını doğrulayabilirim.
Ürdün

11
Jordan: Şimdi eski bir konu ama @Luke farklı bir anahtar türü olan bir haritada bir anahtar olarak bir dize kullanmaya çalıştığından şüpheleniyorum, örneğin Map <Integer, String>.
Sefil Değişken

445

Statik, değişmez bir haritayı başlatmak için Guava yolunu seviyorum :

static final Map<Integer, String> MY_MAP = ImmutableMap.of(
    1, "one",
    2, "two"
);

Gördüğünüz gibi, çok özlü (çünkü uygun fabrika yöntemleri nedeniyle ImmutableMap).

Haritanın 5'ten fazla girişi olmasını istiyorsanız, artık kullanamazsınız ImmutableMap.of(). Bunun yerine, ImmutableMap.builder()şu satırları deneyin :

static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
    .put(1, "one")
    .put(2, "two")
    // ... 
    .put(15, "fifteen")
    .build();

Guava'nın değişmez toplama yardımcı programlarının yararları hakkında daha fazla bilgi için, bkz . Guava Kullanıcı Kılavuzu'nda Açıklanan Değişken Koleksiyonlar .

Guava'nın (bir alt kümesi) eskiden Google Koleksiyonları olarak adlandırılırdı . Bu kütüphaneyi henüz Java projenizde kullanmıyorsanız, denemenizi önemle tavsiye ederim! Guava, SO kullanıcılarının kabul ettiği gibi, Java için en popüler ve kullanışlı ücretsiz 3. taraf kütüphanelerinden biri haline geldi . (Eğer yeniyseniz, bu bağlantının arkasında bazı mükemmel öğrenme kaynakları vardır.)


Güncelleme (2015) : Java 8'e gelince , yine de Guava yaklaşımını kullanacağım çünkü her şeyden çok daha temiz. Guava bağımlılığını istemiyorsanız, eski bir init yöntemini düşünün . Bana sorarsanız , iki boyutlu dizi ve Akış API'sı ile kesmek oldukça çirkin ve anahtarları ve değerleri aynı türde olmayan bir Harita oluşturmanız gerekiyorsa ( Map<Integer, String>sorudaki gibi ) çirkinleşir .

Guava'nın geleceğine gelince, Java 8 ile ilgili olarak, Louis Wasserman bunu 2014'te söyledi ve 2016'da [ güncelleme ] Guava 21'in Java 8'i gerektireceği ve uygun şekilde destekleyeceği açıklandı .


Güncelleme (2016) : Tagir Valeev'in belirttiği gibi , Java 9 nihayet koleksiyonlar için kolaylık fabrika yöntemleri ekleyerek saf JDK'den başka bir şey kullanmadan bunu temizleyecektir :

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);

21
Görünüşe göre bizim SO yöneticileri saygı duyduğum saygıdeğer "En yararlı ücretsiz üçüncü taraf Java kütüphaneleri" sorusunu silmiş. :( Lanet olsun.
Jonik

2
Katılıyorum, bu sabit bir haritayı başlatmanın en güzel yolu. Yalnızca daha okunabilir değil, aynı zamanda Collections.unmodifiableMap , alttaki haritanın salt okunur bir görünümünü döndürdüğü için (yine de değiştirilebilir).
crunchdog

11
Şimdi silinen soruları görebiliyorum (10k + rep ile), bu yüzden 'En yararlı ücretsiz üçüncü taraf Java kitaplıkları'nın bir kopyası . Sadece ilk sayfa, ama en azından yukarıda belirtilen Guava kaynaklarını bulabilirsiniz .
Jonik

2
Ekstra bağımlılıklar olmadan nasıl yapılacağını bilmek faydalı olsa da, bu yaklaşımı gerçekten tercih ediyorum.
İngiliz anahtarı

2
JEP 186 hala kapalı değil, bu yüzden koleksiyon değişmezleriyle ilgili yeni özellikler
sunabilir

182

Kullanmak istiyorum:

public class Test {
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}
  1. kişisel olarak kötü bir stil olduğunu düşündüğüm anonim bir sınıftan kaçınır ve
  2. haritanın oluşturulmasını daha açık hale getirir
  3. haritayı değiştirilemez hale getirir
  4. MY_MAP sabit olduğundan, bunu sabit gibi adlandırırım

3
Saf JDK seçeneklerinden (libs yok), bunu en çok beğendim, çünkü harita tanımı başlatılmasına açıkça bağlı. Ayrıca sürekli adlandırma konusunda da anlaştı.
Jonik

Bunu yapabileceğin hiç aklıma gelmedi.
romulusnr

181

Java 5 bu daha kompakt sözdizimini sağlar:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};

46
Bu tekniğe çift ayraç başlatma adı verilir: stackoverflow.com/questions/1372113/… Özel bir Java 5 sözdizimi değil, yalnızca örnek başlatıcısı olan anonim bir sınıfla yapılan bir numara.
Jesper

13
Çift ayraç başlatma hakkında hızlı soru: Eclipse bunu yaparken eksik Seri Kimliği hakkında bir Uyarı verir. Bir yandan, bu özel durumda neden bir Seri Kimliğe ihtiyaç duyulacağını anlamıyorum, ancak diğer yandan, genellikle bastırıcı uyarıları sevmiyorum. Bu konudaki düşünceleriniz neler?
nbarraille

8
@nbarraille Çünkü HashMap implements Serializable . Bu "hileyi" kullanarak aslında HashMap'in bir alt sınıfını oluşturduğunuzdan, dolaylı olarak bir Serileştirilebilir sınıf oluşturursunuz. Ve bunun için bir serialUID sağlamalısınız.
kimse

5
Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes.- IntelliJ
Mark Jeronimus

3
@MarkJeronimus - önerilen kullanım olan statik bir bağlamı. Performans daha kötü olabilir, ancak muhtemelen çok az sayıda statik olarak tanımlanmış harita ile uğraşırken fark edilmeyebilir. HashMap.equals, Harita'nın herhangi bir alt sınıfında tanımlanır AbstractMapve bu sınıf üzerinde çalışır . Elmas operatörü şey sinir bozucu, ancak belirtildiği gibi şimdi çözüldü.
Jules

95

İkinci yöntemin bir avantajı Collections.unmodifiableMap(), koleksiyonun daha sonra hiçbir şeyin güncellenmeyeceğini garanti etmek için onu sarmanızdır :

private static final Map<Integer, String> CONSTANT_MAP = 
    Collections.unmodifiableMap(new HashMap<Integer, String>() {{ 
        put(1, "one");
        put(2, "two");
    }});

 // later on...

 CONSTANT_MAP.put(3, "three"); // going to throw an exception!

3
Bunu ilk yöntemde yeni operatörü statik {} bloğuna taşıyarak ve sararak yapamaz mısınız?
Patrick

2
Zaten yapıcı çağrısını yine statik olarak başlatıldı. Başka bir şey tuhaf görünüyor.
Tom Hawtin - tackline

2
somut bir sınıfın aksine anonim bir sınıf kullanmaktan hangi performansın vurulabileceğine dair bir fikrin var mı?
Kip

62

İşte Java 8 tek satırlık statik harita başlatıcısı:

private static final Map<String, String> EXTENSION_TO_MIMETYPE =
    Arrays.stream(new String[][] {
        { "txt", "text/plain" }, 
        { "html", "text/html" }, 
        { "js", "application/javascript" },
        { "css", "text/css" },
        { "xml", "application/xml" },
        { "png", "image/png" }, 
        { "gif", "image/gif" }, 
        { "jpg", "image/jpeg" },
        { "jpeg", "image/jpeg" }, 
        { "svg", "image/svg+xml" },
    }).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));

Düzenleme: Map<Integer, String>soruda olduğu gibi başlatmak için aşağıdaki gibi bir şeye ihtiyacınız vardır:

static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
        {1, "one"},
        {2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));

Düzenleme (2): i_am_zero'nun new SimpleEntry<>(k, v)arama akışı kullanan daha iyi, karışık tipte bir sürümü var . Bu cevaba göz atın: https://stackoverflow.com/a/37384773/3950982


7
Soru ve diğer cevaplara eşdeğer bir sürüm ekleme özgürlüğünü aldım: anahtarları ve değerleri farklı türde bir Harita başlat (bu yüzden String[][]yapmayacak Object[][], gerekli). IMHO, bu yaklaşım çirkin (dökümlerle daha da fazla) ve hatırlanması zor; kendim kullanmazdım.
Jonik

57

Map.of Java 9 ve üstü sürümlerde

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

Ayrıntılar için JEP 269'a bakın. JDK 9 , Eylül 2017'de genel kullanılabilirliğe ulaştı .


7
Veya 10'dan fazla anahtar / değer çifti istiyorsanız, kullanabilirsinizMap.ofEntries
ZhekaKozlov


Ugh bu çok üzücü - sadece 10 girişi destekliyor gibi görünüyor, bundan sonra Girişleri kullanmanız gerekiyor. Topal.
Somaiah Kumbera

2
JDK'daki uygulama temizliği, çalıştığı ve sözleşmeyi yerine getirdiği sürece önemli olmamalıdır. Herhangi bir kara kutu gibi, uygulama detayları gerçekten ihtiyaç duyulursa gelecekte her zaman düzeltilebilir ...
vikingsteve

@mid Java'da bunu yapmanın tek tipik yolu budur.
Luke Hutchison

44

Java 9

Her girişi oluşturmak için Map.ofEntriesarayarak kullanabiliriz Map.entry( k , v ).

import static java.util.Map.entry;
private static final Map<Integer,String> map = Map.ofEntries(
        entry(1, "one"),
        entry(2, "two"),
        entry(3, "three"),
        entry(4, "four"),
        entry(5, "five"),
        entry(6, "six"),
        entry(7, "seven"),
        entry(8, "eight"),
        entry(9, "nine"),
        entry(10, "ten"));

Biz de kullanabilir Map.ofonun cevabını Tagir önerdiği gibi burada ama biz kullanarak 10'dan fazla giriş olamaz Map.of.

Java 8 (Düzgün Çözüm)

Bir harita girişi akışı oluşturabiliriz. Biz zaten iki uygulamaları var Entryiçinde java.util.AbstractMaphangi vardır SimpleEntry ve SimpleImmutableEntry . Bu örnek için, öncekinden şu şekilde faydalanabiliriz:

import java.util.AbstractMap.*;
private static final Map<Integer, String> myMap = Stream.of(
            new SimpleEntry<>(1, "one"),
            new SimpleEntry<>(2, "two"),
            new SimpleEntry<>(3, "three"),
            new SimpleEntry<>(4, "four"),
            new SimpleEntry<>(5, "five"),
            new SimpleEntry<>(6, "six"),
            new SimpleEntry<>(7, "seven"),
            new SimpleEntry<>(8, "eight"),
            new SimpleEntry<>(9, "nine"),
            new SimpleEntry<>(10, "ten"))
            .collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));

2
Yol new SimpleEntry<>()statikten çok daha az okunabilir put(): /
Danon

32

Eclipse Collections ile aşağıdakilerin tümü çalışacaktır:

import java.util.Map;

import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Maps;

public class StaticMapsTest
{
    private static final Map<Integer, String> MAP =
        Maps.mutable.with(1, "one", 2, "two");

    private static final MutableMap<Integer, String> MUTABLE_MAP =
       Maps.mutable.with(1, "one", 2, "two");


    private static final MutableMap<Integer, String> UNMODIFIABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").asUnmodifiable();


    private static final MutableMap<Integer, String> SYNCHRONIZED_MAP =
        Maps.mutable.with(1, "one", 2, "two").asSynchronized();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").toImmutable();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP2 =
        Maps.immutable.with(1, "one", 2, "two");
}

Eclipse Collections ile ilkel haritaları statik olarak başlatabilirsiniz.

import org.eclipse.collections.api.map.primitive.ImmutableIntObjectMap;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps;

public class StaticPrimitiveMapsTest
{
    private static final MutableIntObjectMap<String> MUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two");

    private static final MutableIntObjectMap<String> UNMODIFIABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asUnmodifiable();

    private static final MutableIntObjectMap<String> SYNCHRONIZED_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asSynchronized();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .toImmutable();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP2 =
            IntObjectMaps.immutable.<String>empty()
                    .newWithKeyValue(1, "one")
                    .newWithKeyValue(2, "two");
} 

Not: Eclipse Collections için bir komisyoncuyum


1
Eclipse Collections'ın Java için varsayılan koleksiyon kütüphanesi olmasını gerçekten isterdim. Guava + JCL'den çok daha fazla keyif alıyorum.
Kenny Cason

29

Bu durumda asla anonim bir alt sınıf oluşturmam. Haritayı değiştirilemez hale getirmek istiyorsanız statik başlatıcılar eşit derecede iyi çalışır:

private static final Map<Integer, String> MY_MAP;
static
{
    Map<Integer, String>tempMap = new HashMap<Integer, String>();
    tempMap.put(1, "one");
    tempMap.put(2, "two");
    MY_MAP = Collections.unmodifiableMap(tempMap);
}

1
Hangi durumda bir hashmap başlatmak için anonim bir alt sınıf kullanırsınız?
dogbane

6
Asla bir Koleksiyon başlatmayın.
eljenso

Statik bir başlatıcı kullanmanın neden anonim bir alt sınıf oluşturmaktan daha iyi bir seçim olduğunu açıklayabilir misiniz?
leba-lev

3
@rookie Diğer yanıtlarda statik başlangıcı destekleyen çeşitli nedenler vardır. Burada amaç ise başlatmak için, yani subclassing getirmek neden birkaç tuşa kurtarmak için belki hariç? (Tuşlara kaydetmek istiyorsanız, Java kesinlikle bir programlama dili olarak iyi bir seçim değildir.) Java'da programlama yaparken kullandığım bir başparmak kuralı: mümkün olduğunca az alt sınıf (ve hiçbir zaman makul bir şekilde önlenemezse).
eljenso

@eljenso - bunun için genellikle alt sınıf sözdizimini tercih etmemin nedeni , başlatma işlemini ait olduğu yere koymasıdır . İkinci en iyi seçenek, başlatılmış haritayı döndüren statik bir yöntemi çağırmaktır. Ama korkarım kodunuza bakacağım ve MY_MAP'ın nereden geldiğini öğrenmek için birkaç saniye harcamak zorunda kaldım ve o zaman boşa harcamak istemiyorum. Okunabilirlikteki herhangi bir gelişme bir bonus ve performans sonuçları minimal, bu yüzden benim için en iyi seçenek gibi görünüyor.
Jules

18

Google Koleksiyonlarına göz atmak ilginç olabilir , örneğin sayfalarında bulunan videolara . Haritaları ve kümeleri başlatmak ve değişmez koleksiyonlar sağlamak için çeşitli yollar sağlarlar.

Güncelleme: Bu kütüphane artık Guava olarak adlandırılıyor .


17

Anonim sınıfı seviyorum, çünkü onunla başa çıkmak kolay:

public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
    {
        put(1, "some value");
                    //rest of code here
    }
});

12
public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

Birden fazla sabit beyan edersek, bu kod statik blokta yazılır ve gelecekte korunması zordur. Bu yüzden anonim sınıf kullanmak daha iyidir.

public class Test {

    public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
        {
            put(1, "one");
            put(2, "two");
        }
    });
}

Ve sabit olarak ele alınamayacağı diğer sabitler için değiştirilemez harita kullanılması önerilir.


10

Ben statik blok tarzı üzerinde "çift ayraç başlatma" tarzı şiddetle tavsiye edebilir.

Birisi anonim sınıf, ek yük, performans vb. Hoşlanmadığını yorumlayabilir.

Ama ben daha çok kod okunabilirliği ve sürdürülebilirliği olduğunu düşünüyorum. Bu bakış açısıyla, ben bir çift ayraç durağan bir yöntem yerine daha iyi bir kod tarzı duruyorum.

  1. Öğeler iç içe ve satır içi.
  2. Daha fazla OO, prosedürel değil.
  3. performans etkisi gerçekten küçüktür ve göz ardı edilebilir.
  4. Daha iyi IDE anahat desteği (çok sayıda anonim statik {} blok yerine)
  5. İlişkilerini getirmek için birkaç satır yorum kaydettiniz.
  6. Başlatılmamış nesnenin olası öğe sızıntısı / örnek kurşununun istisna ve bayt kodu optimize edicisini önleyin.
  7. Statik blok yürütme sırası hakkında endişelenmenize gerek yok.

Buna ek olarak, anonim sınıfın GC'sini her zaman kullanarak normal bir HashMap'e dönüştürebilirsiniz. new HashMap(Map map) .

Başka bir sorunla karşılaşıncaya kadar bunu yapabilirsiniz. Bunu yaparsanız, bunun için başka bir kodlama stili (örn. Statik, fabrika sınıfı yok) kullanmanız gerekir.


8

Her zamanki gibi apache-commons MapUtils.putAll (Map, Object []) yöntemine sahiptir. :

Örneğin, bir renk haritası oluşturmak için:

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });

Apache Commons'ı tüm yapılara dahil ediyorum, bu yüzden Arrays.asMap( ... )düz Java'da bir yöntemin talihsiz yokluğunda, bunun en iyi çözüm olduğunu düşünüyorum. Tekerleğin yeniden icat edilmesi genellikle saçmadır. Çok hafif bir dezavantajı, jeneriklerle kontrolsüz bir dönüşüme ihtiyaç duymasıdır.
mike kemirgen

@mikerodent 4.1 sürümü genel: genel statik <K, V> Harita <K, V> putAll (son Harita <K, V> harita, son Nesne [] dizisi)
agad

Tx ... evet, 4.1 kullanıyorum ama hala SuppressWarnings( unchecked )Eclipse'de böyle bir çizgiyle zorundayımMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
mike rodent

@mikerodent Object [] [] yüzünden değil mi? Güncellenmiş bozulmalara bakın - Eclipse'de hiçbir uyarım yok.
agad

Ne kadar garip ... Gittiğimde bile String[][]"uyarı" yı alıyorum! Ve tabii ki bu sadece sizin Kve Vaynı sınıftaysanız çalışır. Eclipse kurulumunda "kontrol edilmeyen dönüşümü" "Yoksay" olarak ayarlamadığınız anlaşılıyor mu?
mike kemirgen

7

İşte Guava'nın kullanmak istemediğimde (veya kullanamadığımda) ImmutableMap.of()veya bir değişime ihtiyacım olduğunda favorim Map:

public static <A> Map<String, A> asMap(Object... keysAndValues) {
    return new LinkedHashMap<String, A>() {{
        for (int i = 0; i < keysAndValues.length - 1; i++) {
            put(keysAndValues[i].toString(), (A) keysAndValues[++i]);
        }
    }};
}

Çok kompakttır ve kaçak değerleri yoksayar (yani değeri olmayan bir son anahtar).

Kullanımı:

Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));

7

Eğer unmodifiable haritayı istiyorsanız, nihayet java 9 serin fabrika yöntemini eklendi ofiçin Mapbir arayüz. Benzer yöntem Küme, Liste'ye de eklenir.

Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");


6

Ben statik bir başlatıcısı ile başlangıç ​​ipuçlarını listeler (anonim sınıfları oluşturmak için önlemek için statik bir başlatıcısı kullanmayı tercih, bu yüzden ben statik bir başlatıcısı ile başlangıç ​​ipuçları listeleyeceğim. Listelenen tüm çözümler / ipuçları tipte güvenlidir.

Not: Soru, haritayı değiştirilemez hale getirmeyle ilgili bir şey söylemiyor, bu yüzden bunu dışarıda bırakacağım, ancak bunun kolayca yapılabileceğini biliyorum Collections.unmodifiableMap(map).

İlk ipucu

İlk ipucu, haritaya yerel bir referans yapabilmeniz ve buna KISA bir ad vermenizdir:

private static final Map<Integer, String> myMap = new HashMap<>();
static {
    final Map<Integer, String> m = myMap; // Use short name!
    m.put(1, "one"); // Here referencing the local variable which is also faster!
    m.put(2, "two");
    m.put(3, "three");
}

İkinci ipucu

İkinci ipucu, giriş eklemek için bir yardımcı yöntem oluşturabilmenizdir; ayrıca şunları yapmak istiyorsanız bu yardımcı yöntemi herkese açık yapabilirsiniz:

private static final Map<Integer, String> myMap2 = new HashMap<>();
static {
    p(1, "one"); // Calling the helper method.
    p(2, "two");
    p(3, "three");
}

private static void p(Integer k, String v) {
    myMap2.put(k, v);
}

Buradaki yardımcı yöntem yeniden kullanılamaz, çünkü yalnızca öğelere öğe ekleyebilir myMap2. Yeniden kullanılabilir hale getirmek için haritanın kendisini yardımcı yöntemin bir parametresi yapabiliriz, ancak başlatma kodu daha kısa olmazdı.

Üçüncü ipucu

Üçüncü ipucu, doldurma işleviyle yeniden kullanılabilir oluşturucu benzeri bir yardımcı sınıf oluşturabilmenizdir. Bu gerçekten tip-güvenli, 10 satırlı basit bir yardımcı sınıftır:

public class Test {
    private static final Map<Integer, String> myMap3 = new HashMap<>();
    static {
        new B<>(myMap3)   // Instantiating the helper class with our map
            .p(1, "one")
            .p(2, "two")
            .p(3, "three");
    }
}

class B<K, V> {
    private final Map<K, V> m;

    public B(Map<K, V> m) {
        this.m = m;
    }

    public B<K, V> p(K k, V v) {
        m.put(k, v);
        return this; // Return this for chaining
    }
}

5

Oluşturduğunuz anonim sınıf iyi çalışıyor. Bununla birlikte, bunun bir sınıf olduğunu ve bu nedenle, çevresindeki sınıf örneğine bir referans içereceğini bilmelisiniz . Böylece onunla belirli şeyler yapamayacağınızı göreceksiniz ( XStream kullanarak için ). Çok garip hatalar alırsınız.

Bunu söyledikten sonra, farkında olduğunuz sürece bu yaklaşım iyidir. Çoğu zaman her türlü koleksiyonu özlü bir şekilde başlatmak için kullanıyorum.

EDIT: Yorumlar statik bir sınıf olduğunu doğru işaret etti. Açıkçası bunu yeterince yakından okumadım. Ancak açıklamalarım yok hala anonim iç sınıflar için geçerlidir.


3
Bu özel durumda statiktir, bu nedenle dış örnek yoktur.
Tom Hawtin - tackline

Muhtemelen XStream böyle serialize şeyler çalışıyor olmamalıdır ( 's statik Neden statik bir değişken seri hale getirmek gerekir.?)
jasonmp85

5

Kısa ve nispeten güvenli bir şey istiyorsanız, derleme zamanı tür denetimini çalışma zamanına kaydırabilirsiniz:

static final Map<String, Integer> map = MapUtils.unmodifiableMap(
    String.class, Integer.class,
    "cat",  4,
    "dog",  2,
    "frog", 17
);

Bu uygulama tüm hataları yakalamalıdır:

import java.util.HashMap;

public abstract class MapUtils
{
    private MapUtils() { }

    public static <K, V> HashMap<K, V> unmodifiableMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        return Collections.<K, V>unmodifiableMap(makeMap(
            keyClazz,
            valClazz,
            keyValues));
    }

    public static <K, V> HashMap<K, V> makeMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        if (keyValues.length % 2 != 0)
        {
            throw new IllegalArgumentException(
                    "'keyValues' was formatted incorrectly!  "
                  + "(Expected an even length, but found '" + keyValues.length + "')");
        }

        HashMap<K, V> result = new HashMap<K, V>(keyValues.length / 2);

        for (int i = 0; i < keyValues.length;)
        {
            K key = cast(keyClazz, keyValues[i], i);
            ++i;
            V val = cast(valClazz, keyValues[i], i);
            ++i;
            result.put(key, val);
        }

        return result;
    }

    private static <T> T cast(Class<? extends T> clazz, Object object, int i)
    {
        try
        {
            return clazz.cast(object);
        }
        catch (ClassCastException e)
        {
            String objectName = (i % 2 == 0) ? "Key" : "Value";
            String format = "%s at index %d ('%s') wasn't assignable to type '%s'";
            throw new IllegalArgumentException(String.format(format, objectName, i, object.toString(), clazz.getSimpleName()), e);
        }
    }
}

4

Java 8 ile aşağıdaki kalıbı kullanmaya geldim:

private static final Map<String, Integer> MAP = Stream.of(
    new AbstractMap.SimpleImmutableEntry<>("key1", 1),
    new AbstractMap.SimpleImmutableEntry<>("key2", 2)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

En kısa ve dolaylı değil, ama

  • dışında bir şey gerektirmez java.util
  • tipik bir özelliktir ve anahtar ve değer için farklı türleri kolayca barındırır.

gerekiyorsa, toMapharitanın türünü belirtmek için harita sağlayıcısı da dahil olmak üzere imza kullanılabilir .
zrvan


4

Sen kullanabilir StickyMapve MapEntrygelen Cactoos :

private static final Map<String, String> MAP = new StickyMap<>(
  new MapEntry<>("name", "Jeffrey"),
  new MapEntry<>("age", "35")
);

4

İkinci yaklaşımınızın (Double Brace başlatması) bir anti-desen olduğu düşünülür , bu yüzden ilk yaklaşımı tercih ederim.

Statik bir Harita başlatmanın başka bir kolay yolu, bu yardımcı program işlevini kullanmaktır:

public static <K, V> Map<K, V> mapOf(Object... keyValues) {
    Map<K, V> map = new HashMap<>(keyValues.length / 2);

    for (int index = 0; index < keyValues.length / 2; index++) {
        map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
    }

    return map;
}

Map<Integer, String> map1 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");

Not: içinde Map.ofJava 9 kullanabilirsiniz


3

Statik başlatıcı sözdizimini sevmiyorum ve anonim alt sınıflara ikna olmadım. Genel olarak, Statik başlatıcıları kullanmanın tüm eksilerini ve önceki cevaplarda belirtilen anonim alt sınıfları kullanmanın tüm eksilerini kabul ediyorum. Diğer yandan bu yazılarda sunulan profesyoneller benim için yeterli değil. Statik başlatma yöntemini kullanmayı tercih ederim:

public class MyClass {
    private static final Map<Integer, String> myMap = prepareMap();

    private static Map<Integer, String> prepareMap() {
        Map<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "one");
        hashMap.put(2, "two");

        return hashMap;
    }
}

3

Herhangi bir cevapta kullandığım (ve beğenmek için büyüdüğüm) yaklaşımı görmedim, işte burada:

Statik başlatıcıları kullanmayı sevmiyorum çünkü bunlar tıknaz ve anonim sınıfları sevmiyorum çünkü her örnek için yeni bir sınıf oluşturuyor.

bunun yerine, şuna benzeyen başlatmayı tercih ederim:

map(
    entry("keyA", "val1"),
    entry("keyB", "val2"),
    entry("keyC", "val3")
);

ne yazık ki, bu yöntemler standart Java kitaplığının bir parçası değildir, bu nedenle aşağıdaki yöntemleri tanımlayan bir yardımcı program kitaplığı oluşturmanız (veya kullanmanız) gerekir:

 public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
 public static <K,V> Map.Entry<K,V> entry(K key, V val)

(yöntemin adını önek eklemek zorunda kalmamak için 'import static' kullanabilirsiniz)

Diğer koleksiyonlar (liste, set, sortSet, sortMap, vb.) İçin benzer statik yöntemler sağlamak için yararlı buldum

Json nesnesi başlatma kadar güzel değil, ancak okunabilirlik söz konusu olduğunda bu yönde bir adım.


3

Java, harita değişmezlerini desteklemediğinden, harita örnekleri her zaman açıkça somutlaştırılmalı ve doldurulmalıdır.

Neyse ki, Java'daki harita değişmezlerinin davranışını fabrika yöntemleriyle yaklaşık olarak tahmin etmek mümkündür .

Örneğin:

public class LiteralMapFactory {

    // Creates a map from a list of entries
    @SafeVarargs
    public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
        LinkedHashMap<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
    // Creates a map entry
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static void main(String[] args) {
        System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
    }
}

Çıktı:

{a = 1, b = 2, c = 3}

Bir kerede bir öğe oluşturmak ve doldurmaktan çok daha uygundur.


2

JEP 269 Koleksiyonlar API'sı için bazı kullanışlı fabrika yöntemleri sağlar. Bu fabrika yöntemleri 8 olan geçerli Java sürümünde değildir, ancak Java 9 sürümü için planlanmıştır.

Çünkü Mapiki fabrika yöntemi vardır: ofve ofEntries. Düğmesini kullanarak of, alternatif anahtar / değer çiftlerini iletebilirsiniz. Örneğin, bir Mapbenzer oluşturmak için {age: 27, major: cs}:

Map<String, Object> info = Map.of("age", 27, "major", "cs");

Şu anda için aşırı yüklenmiş on sürüm var of, bu nedenle on anahtar / değer çifti içeren bir harita oluşturabilirsiniz. Bu sınırlamayı veya alternatif anahtar / değerleri beğenmezseniz, şunları kullanabilirsiniz ofEntries:

Map<String, Object> info = Map.ofEntries(
                Map.entry("age", 27),
                Map.entry("major", "cs")
);

Her ikisi de ofve ofEntriesdeğişmez bir geri dönecek Map, böylece inşaattan sonra elemanlarını değiştiremezsiniz. JDK 9 Erken Erişim'i kullanarak bu özellikleri deneyebilirsiniz .


2

Şey ... Numaraları severim;)

enum MyEnum {
    ONE   (1, "one"),
    TWO   (2, "two"),
    THREE (3, "three");

    int value;
    String name;

    MyEnum(int value, String name) {
        this.value = value;
        this.name = name;
    }

    static final Map<Integer, String> MAP = Stream.of( values() )
            .collect( Collectors.toMap( e -> e.value, e -> e.name ) );
}

2

Cevapları okudum ve kendi harita oluşturucumu yazmaya karar verdim. Kopyalayıp yapıştırmaktan ve eğlenmekten çekinmeyin.

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * A tool for easy creation of a map. Code example:<br/>
 * {@code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
 * @param <K> key type (inferred by constructor)
 * @param <V> value type (inferred by constructor)
 * @author Vlasec (for http://stackoverflow.com/a/30345279/1977151)
 */
public class MapBuilder <K, V> {
    private Map<K, V> map = new HashMap<>();

    /** Constructor that also enters the first entry. */
    private MapBuilder(K key, V value) {
        and(key, value);
    }

    /** Factory method that creates the builder and enters the first entry. */
    public static <A, B> MapBuilder<A, B> mapOf(A key, B value) {
        return new MapBuilder<>(key, value);
    }

    /** Puts the key-value pair to the map and returns itself for method chaining */
    public MapBuilder<K, V> and(K key, V value) {
        map.put(key, value);
        return this;
    }

    /**
     * If no reference to builder is kept and both the key and value types are immutable,
     * the resulting map is immutable.
     * @return contents of MapBuilder as an unmodifiable map.
     */
    public Map<K, V> build() {
        return Collections.unmodifiableMap(map);
    }
}

EDIT: Son zamanlarda, genel statik yöntem bulmaya devam ediyorum of sık sık ediyorum ve bunu beğendim. Kodu ekledim ve yapıcıyı özel yaptım, böylece statik fabrika yöntemi desenine geçtim.

EDIT2: Daha da yakın zamanda, ofstatik içe aktarma kullanırken oldukça kötü göründüğü için artık denilen statik yöntemi sevmiyorum . Bunun mapOfyerine yeniden adlandırdım , statik ithalata daha uygun hale getirdim .

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.