Bagaimana saya bisa menginisialisasi Peta statis?

1132

Bagaimana Anda menginisialisasi statis Mapdi Jawa?

Metode satu: penginisialisasi statis
Metode dua: penginisialisasi instance (subclass anonim) atau metode lain?

Apa pro dan kontra dari masing-masing?

Berikut adalah contoh yang menggambarkan dua metode:

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");
        }
    };
}
dogbane
sumber
2
Untuk menginisialisasi peta di Java 8: stackoverflow.com/a/37384773/1216775
akhil_mittal
2
Tolong, jangan pernah menggunakan inisialisasi penjepit ganda - ini adalah peretasan, dan cara mudah untuk membocorkan memori dan menyebabkan masalah lain.
dimo414
Java 9? Jika entri dihitung <= 10 gunakan yang Map.oflain Map.ofEntries, periksa stackoverflow.com/a/37384773/1216775
akhil_mittal

Jawaban:

1106

Inisialisasi instance hanya gula sintaksis dalam kasus ini, kan? Saya tidak mengerti mengapa Anda perlu kelas anonim tambahan hanya untuk menginisialisasi. Dan itu tidak akan berhasil jika kelas yang dibuat adalah final.

Anda dapat membuat peta abadi menggunakan penginisialisasi statis juga:

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);
    }
}
Variabel Menyedihkan
sumber
10
Ini adalah ungkapan yang telah saya gunakan selama bertahun-tahun dan saya tidak pernah melihat orang yang melihatnya. Saya melakukan hal yang sama untuk Sets dan Daftar konstan yang tidak dapat dimodifikasi juga.
jasonmp85
3
Bagaimana saya menangani HashMap <String, String> dengan kunci String. Objek Peta tidak memungkinkan saya memiliki kunci String sehingga saya tidak dapat menggunakan Map yang tidak dapat dimodifikasi (). Saya kira casting ke HashMap akan mengalahkan tujuan juga. Ada ide?
Luke
30
@ Lukas Saya sangat ragu bahwa Android memiliki batasan seperti itu. Tidak masuk akal sama sekali. Pencarian cepat menemukan pertanyaan ini di sini (dan banyak lainnya) yang tampaknya menyiratkan Anda dapat menggunakan kunci String untuk objek Peta di Android.
mluisbrown
11
Jadi tidak ada orang lain yang mengganggu untuk diselidiki, saya dapat mengonfirmasi tidak ada masalah dengan menggunakan kunci String untuk objek Peta di Android.
Jordan
11
Jordan: ini adalah topik lama sekarang tapi saya curiga @Luke mencoba menggunakan string sebagai kunci di peta yang memiliki tipe kunci yang berbeda, misalnya Map <Integer, String>.
Variabel yang
445

Saya suka cara Jambu menginisialisasi peta statis yang tidak dapat diubah:

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

Seperti yang Anda lihat, ini sangat ringkas (karena metode pabrik yang mudah digunakan ImmutableMap).

Jika Anda ingin peta memiliki lebih dari 5 entri, Anda tidak dapat lagi menggunakan ImmutableMap.of(). Alih-alih, coba ImmutableMap.builder()di bawah ini:

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

Untuk mempelajari lebih lanjut tentang manfaat dari utilitas pengumpulan yang tidak berubah oleh Guava, lihat Koleksi Tidak Berubah yang Dijelaskan dalam Panduan Pengguna Guava .

(Subset dari) Jambu biji dulu disebut Google Collections . Jika Anda belum menggunakan pustaka ini dalam proyek Java Anda, saya sangat merekomendasikan untuk mencobanya! Guava dengan cepat menjadi salah satu libs pihak ke-3 gratis yang paling populer dan bermanfaat untuk Java, seperti yang disetujui oleh sesama pengguna SO . (Jika Anda baru mengenalnya, ada beberapa sumber belajar yang sangat baik di balik tautan itu.)


Pembaruan (2015) : Adapun Java 8 , well, saya masih akan menggunakan pendekatan Guava karena jauh lebih bersih daripada yang lain. Jika Anda tidak ingin ketergantungan jambu biji, pertimbangkan metode init lama biasa . Peretasan dengan array dua dimensi dan Stream API sangat jelek jika Anda bertanya kepada saya, dan menjadi lebih buruk jika Anda perlu membuat Peta yang kunci dan nilainya bukan tipe yang sama (seperti Map<Integer, String>dalam pertanyaan).

Adapun masa depan Guava secara umum, sehubungan dengan Java 8, Louis Wasserman mengatakan ini kembali pada tahun 2014, dan [ pembaruan ] pada tahun 2016 diumumkan bahwa Guava 21 akan membutuhkan dan mendukung Java 8 dengan baik .


Pembaruan (2016) : Seperti yang ditunjukkan Tagir Valeev , Java 9 pada akhirnya akan membuat hal ini bersih dengan menggunakan JDK murni, dengan menambahkan metode pabrik yang praktis untuk koleksi:

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);
Jonik
sumber
21
Sepertinya sesama admin SO kami telah menghapus pertanyaan "Perpustakaan Java pihak ketiga yang paling berguna gratis" yang saya tautkan. :( Sialan mereka.
Jonik
2
Saya setuju, ini adalah cara terbaik untuk menginisialisasi peta yang konstan. Tidak hanya lebih mudah dibaca tetapi juga karena Collections.unmodifiableMap mengembalikan tampilan baca-saja dari peta yang mendasarinya (yang masih dapat dimodifikasi).
crunchdog
11
Sekarang saya dapat melihat pertanyaan yang dihapus (dengan 10k + rep), jadi inilah salinan 'Perpustakaan Java pihak ketiga gratis yang paling berguna' . Ini hanya halaman pertama, tetapi setidaknya Anda dapat menemukan sumber daya Guava yang disebutkan di atas.
Jonik
2
Saya benar-benar lebih suka pendekatan ini, meskipun itu bermanfaat untuk mengetahui bagaimana melakukannya tanpa ketergantungan tambahan.
Kunci pas
2
JEP 186 masih belum ditutup, jadi mungkin memperkenalkan fitur baru yang terkait dengan literal koleksi
cybersoft
182

Saya akan menggunakan:

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. itu menghindari kelas anonim, yang saya pribadi anggap sebagai gaya yang buruk, dan hindari
  2. itu membuat pembuatan peta lebih eksplisit
  3. itu membuat peta tidak dapat dimodifikasi
  4. karena MY_MAP adalah konstan, saya akan menamainya seperti konstan
Peter Štibraný
sumber
3
Dari opsi JDK murni (tanpa lib), saya paling suka ini, karena definisi peta jelas terkait dengan inisialisasi. Juga menyetujui penamaan yang konstan.
Jonik
Tidak pernah terpikir oleh saya bahwa Anda bisa melakukan ini.
romulusnr
181

Java 5 menyediakan sintaks yang lebih ringkas ini:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};
Chris Noe
sumber
46
Teknik itu disebut inisialisasi penjepit ganda: stackoverflow.com/questions/1372113/... Ini bukan sintaks Java 5 khusus, itu hanya sebuah trik dengan kelas anonim dengan instance initializer.
Jesper
13
Pertanyaan cepat mengenai inisialisasi brace ganda: Ketika melakukan ini, Eclipse mengeluarkan Peringatan tentang Serial ID yang hilang. Di satu sisi, saya tidak melihat mengapa ID Serial diperlukan dalam kasus khusus ini, tetapi di sisi lain, saya biasanya tidak suka peringatan yang ditekan. Apa pendapat Anda tentang ini?
nbarraille
8
@nbarraille Itu karena HashMap implements Serializable. Karena Anda benar-benar membuat subkelas HashMap menggunakan "trik" ini, Anda secara implisit membuat kelas Serializable. Dan untuk ini, Anda harus menyediakan serialUID.
noone
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 - Penggunaan yang disarankan adalah konteks statis. Kinerja mungkin lebih buruk, tetapi tidak begitu terlihat ketika berhadapan dengan sejumlah kecil peta statis yang ditentukan. HashMap.equalsdidefinisikan AbstractMapdan bekerja pada subkelas Peta apa pun , jadi itu bukan masalah di sini. Masalahnya, operator berlian mengganggu, tetapi seperti yang disebutkan sekarang telah diselesaikan.
Jules
95

Satu keuntungan dari metode kedua adalah Anda dapat membungkusnya dengan Collections.unmodifiableMap()untuk memastikan bahwa tidak ada yang akan memperbarui koleksi nanti:

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!
Programmer Hukum
sumber
3
Tidak bisakah Anda melakukan ini dengan mudah di metode pertama dengan memindahkan operator baru ke blok {} statis dan membungkusnya?
Patrick
2
Saya akan memindahkan panggilan konstruktor ke statis yang diinisialisasi. Ada lagi yang terlihat aneh.
Tom Hawtin - tackline
2
ada ide kinerja apa yang mungkin ada dari menggunakan kelas anonim yang bertentangan dengan kelas konkret?
Kip
62

Berikut adalah penginisialisasi peta statis satu baris Java 8:

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

Sunting: untuk menginisialisasi a Map<Integer, String>seperti pada pertanyaan, Anda memerlukan sesuatu seperti ini:

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

Sunting (2): Ada versi yang lebih baik, tipe campuran yang mampu oleh i_am_zero yang menggunakan aliran new SimpleEntry<>(k, v)panggilan. Lihat jawaban itu: https://stackoverflow.com/a/37384773/3950982

Luke Hutchison
sumber
7
Saya mengambil kebebasan untuk menambahkan versi yang setara dengan pertanyaan dan jawaban lainnya: init Peta yang kunci dan nilainya berbeda (jadi String[][]tidak perlu, Object[][]diperlukan). IMHO, pendekatan ini jelek (apalagi dengan gips) dan sulit diingat; tidak akan menggunakannya sendiri.
Jonik
57

Map.of di Jawa 9+

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

Lihat JEP 269 untuk detailnya. JDK 9 mencapai ketersediaan umum pada bulan September 2017.

Tagir Valeev
sumber
7
Atau jika Anda menginginkan lebih dari 10 pasangan nilai kunci, Anda dapat menggunakanMap.ofEntries
ZhekaKozlov
8
Ini bersih dan semuanya, sampai Anda menyadari bagaimana penerapannya
pertengahan
Ugh itu sangat sedih - sepertinya hanya mendukung 10 entri, setelah itu Anda perlu menggunakan Entries. Kuno.
Somaiah Kumbera
2
Kebersihan implementasi di JDK seharusnya tidak masalah asalkan berfungsi dan memenuhi kontrak. Seperti kotak hitam mana pun, detail implementasi selalu dapat diperbaiki di masa depan jika benar-benar diperlukan ...
vikingsteve
@ id Itu satu-satunya cara yang aman untuk melakukan ini di Jawa.
Luke Hutchison
44

Jawa 9

Kita dapat menggunakan Map.ofEntries, memanggil Map.entry( k , v )untuk membuat setiap entri.

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

Kita juga dapat menggunakan Map.ofseperti yang disarankan oleh Tagir dalam jawabannya di sini tetapi kita tidak dapat menggunakan lebih dari 10 entriMap.of .

Java 8 (Solusi Rapi)

Kami dapat membuat Stream entri peta. Kita sudah memiliki dua implementasi dari Entrydalam java.util.AbstractMapyang SimpleEntry dan SimpleImmutableEntry . Untuk contoh ini kita dapat menggunakan yang pertama sebagai:

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));
akhil_mittal
sumber
2
The new SimpleEntry<>()cara adalah jauh lebih mudah dibaca daripada statis put(): /
Danon
32

Dengan Eclipse Collections , semua hal berikut ini akan berfungsi:

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

Anda juga dapat secara statis menginisialisasi peta primitif dengan Eclipse Collections.

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

Catatan: Saya pengendara untuk Eclipse Collections

Donald Raab
sumber
1
Saya sangat berharap Eclipse Collections adalah perpustakaan koleksi default untuk Java. Saya menikmatinya lebih dari Jambu + JCL.
Kenny Cason
29

Saya tidak akan pernah membuat subclass anonim dalam situasi ini. Inisialisasi statis berfungsi sama baiknya, jika Anda ingin membuat peta tidak dapat dimodifikasi, misalnya:

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);
}
Eljenso
sumber
1
Dalam situasi apa Anda menggunakan subclass anonim untuk menginisialisasi peta hash?
dogbane
6
Jangan pernah menginisialisasi Koleksi.
eljenso
Bisakah Anda menjelaskan mengapa menggunakan penginisialisasi statis adalah pilihan yang lebih baik daripada membuat subkelas anonim?
leba-lev
3
@rookie Ada beberapa alasan yang diberikan dalam jawaban lain yang mendukung init statis. Tujuannya di sini adalah untuk menginisialisasi, jadi mengapa membawa subclassing, kecuali mungkin untuk menyimpan beberapa penekanan tombol? (Jika Anda ingin menghemat penekanan tombol, Java jelas bukan pilihan yang baik sebagai bahasa pemrograman.) Salah satu aturan praktis yang saya gunakan ketika pemrograman di Jawa adalah: subkelas sesedikit mungkin (dan tidak pernah ketika itu dapat dihindari).
eljenso
@eljenso - alasan saya umumnya lebih menyukai sintaks subclass untuk ini adalah karena ia menempatkan inisialisasi inline, di mana tempatnya . Pilihan kedua terbaik adalah memanggil metode statis yang mengembalikan peta yang diinisialisasi. Tapi saya khawatir saya akan melihat kode Anda dan harus menghabiskan beberapa detik untuk mencari tahu dari mana MY_MAP berasal, dan saat itulah saya tidak ingin membuang-buang waktu. Setiap peningkatan keterbacaan adalah bonus, dan konsekuensi kinerjanya minimal, jadi sepertinya ini pilihan terbaik bagi saya.
Jules
18

Mungkin menarik untuk memeriksa Google Collections , misalnya video yang mereka miliki di halaman mereka. Mereka menyediakan berbagai cara untuk menginisialisasi peta dan set, dan juga menyediakan koleksi yang tidak berubah.

Pembaruan: Perpustakaan ini sekarang bernama Guava .

Kaarel
sumber
17

Saya suka kelas anonim, karena mudah ditangani:

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

Jika kita mendeklarasikan lebih dari satu konstanta maka kode itu akan ditulis dalam blok statis dan itu sulit dipertahankan di masa depan. Jadi lebih baik menggunakan kelas anonim.

public class Test {

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

Dan disarankan untuk menggunakan peta yang tidak dapat dimodifikasi untuk konstanta selain itu tidak dapat diperlakukan sebagai konstanta.

Leninkumar Koppoju
sumber
10

Saya sangat menyarankan gaya "inisialisasi penjepit ganda" atas gaya blok statis.

Seseorang mungkin berkomentar bahwa mereka tidak suka kelas anonim, overhead, kinerja, dll.

Tapi yang lebih saya pertimbangkan adalah pembacaan kode dan rawatan. Dalam sudut pandang ini, saya berdiri penyangga ganda adalah gaya kode yang lebih baik daripada metode statis.

  1. Elemen-elemen tersebut bersarang dan sejajar.
  2. Ini lebih OO, bukan prosedural.
  3. dampak kinerja sangat kecil dan dapat diabaikan.
  4. Dukungan IDE garis besar (daripada banyak blok statis {} anonim)
  5. Anda menyimpan beberapa baris komentar untuk memberi mereka hubungan.
  6. Mencegah kemungkinan kebocoran elemen / instance dari objek yang tidak diinisialisasi dari pengecualian dan pengoptimal bytecode.
  7. Tidak perlu khawatir dengan urutan eksekusi blok statis.

Selain itu, jika Anda mengetahui GC dari kelas anonim, Anda selalu dapat mengonversinya menjadi HashMap normal dengan menggunakan new HashMap(Map map).

Anda dapat melakukan ini sampai Anda menghadapi masalah lain. Jika ya, Anda harus menggunakan melengkapi gaya pengkodean lain (mis. Tidak ada kelas statis, pabrik) untuk itu.

Dennis C
sumber
8

Seperti biasa apache-commons memiliki metode MapUtils.putAll (Map, Object []) yang tepat :

Misalnya, untuk membuat peta warna:

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });
agad
sumber
Saya menyertakan Apache Commons di semua build jadi, dengan tidak adanya metode Arrays.asMap( ... )di Java, saya pikir ini adalah solusi terbaik. Menciptakan kembali roda biasanya konyol. Kelemahan sangat kecil adalah bahwa dengan obat generik akan memerlukan konversi yang tidak dicentang.
mike rodent
@mikerodent versi 4.1 adalah generik: statis publik <K, V> Peta <K, V> putAll (Peta akhir <K, V> peta, array objek [] akhir)
agad
Tx ... ya, saya menggunakan 4.1 tetapi saya masih harus SuppressWarnings( unchecked )di Eclipse dengan garis sepertiMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
mike rodent
@mikerodent bukan karena Object [] [] ? Lihat yang terbaru tidak dipakai - Saya tidak punya peringatan di Eclipse.
agad
Sungguh aneh ... bahkan ketika saya pergi String[][]saya mendapatkan "peringatan"! Dan tentu saja itu hanya bekerja jika Anda Kdan Vkelas yang sama. Saya kira Anda belum (secara dimengerti) mengatur "konversi yang tidak dicentang" menjadi "Abaikan" di pengaturan Eclipse Anda?
mike rodent
7

Inilah favorit saya ketika saya tidak ingin (atau tidak bisa) menggunakan Guava ImmutableMap.of(), atau jika saya perlu bisa berubah 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]);
        }
    }};
}

Ini sangat kompak, dan mengabaikan nilai-nilai liar (yaitu kunci terakhir tanpa nilai).

Pemakaian:

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

Jika Anda ingin peta yang tidak dapat dimodifikasi, akhirnya java 9 menambahkan metode pabrik keren ofke Mapantarmuka. Metode serupa ditambahkan ke Set, List juga.

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

Bharanidharan K
sumber
6

Saya lebih suka menggunakan penginisialisasi statis untuk menghindari menghasilkan kelas anonim (yang tidak memiliki tujuan lebih lanjut), jadi saya akan daftar tips inisialisasi dengan penginisialisasi statis. Semua solusi / tip yang tercantum adalah tipe-aman.

Catatan: Pertanyaannya tidak mengatakan apa pun tentang membuat peta tidak dapat dimodifikasi, jadi saya akan mengabaikannya, tetapi tahu bahwa itu dapat dengan mudah dilakukan Collections.unmodifiableMap(map).

Kiat pertama

Kiat pertama adalah Anda dapat membuat referensi lokal ke peta dan Anda memberinya nama PENDEK:

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

Tip kedua

Kiat kedua adalah Anda bisa membuat metode pembantu untuk menambahkan entri; Anda juga dapat membuat metode penolong ini menjadi publik jika Anda ingin:

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

Metode helper di sini tidak dapat digunakan kembali karena hanya dapat menambahkan elemen myMap2. Untuk membuatnya dapat digunakan kembali, kita bisa menjadikan peta itu sendiri sebagai parameter dari metode helper, tetapi kemudian kode inisialisasi tidak akan lebih pendek.

Tip ketiga

Kiat ke-3 adalah Anda dapat membuat kelas pembantu seperti pembangun yang dapat digunakan kembali dengan fungsionalitas populating. Ini benar-benar kelas pembantu sederhana 10-baris yang aman untuk jenis:

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
    }
}
icza
sumber
5

Kelas anonim yang Anda buat berfungsi dengan baik. Namun Anda harus menyadari bahwa ini adalah kelas dalam dan karenanya, itu akan berisi referensi ke instance kelas sekitarnya. Jadi Anda akan menemukan bahwa Anda tidak dapat melakukan hal-hal tertentu dengannya (menggunakan XStream untuk satu). Anda akan mendapatkan beberapa kesalahan yang sangat aneh.

Karena itu, selama Anda sadar maka pendekatan ini baik-baik saja. Saya menggunakannya sebagian besar waktu untuk menginisialisasi semua jenis koleksi secara ringkas.

EDIT: Tunjukkan dengan benar di komentar bahwa ini adalah kelas statis. Jelas saya tidak cukup membaca ini. Namun komentar saya lakukan masih berlaku untuk kelas batin anonim.

Brian Agnew
sumber
3
Dalam kasus khusus ini statis, jadi tidak ada contoh luar.
Tom Hawtin - tackline
Bisa dibilang XStream tidak boleh mencoba untuk membuat serial hal-hal seperti ini (ini statis. Mengapa Anda perlu membuat serialisasi variabel statis?)
jasonmp85
5

Jika Anda menginginkan sesuatu yang singkat dan relatif aman, Anda bisa mengubah pemeriksaan tipe kompilasi menjadi run-time:

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

Implementasi ini harus menangkap kesalahan:

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);
        }
    }
}
Philip Guin
sumber
4

Dengan Java 8 saya datang untuk menggunakan pola berikut:

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

Ini bukan yang paling singkat dan agak bundaran, tapi

  • tidak memerlukan apa pun di luar java.util
  • itu aman dan mudah mengakomodasi berbagai jenis kunci dan nilai.
zrvan
sumber
jika diperlukan, orang dapat menggunakan toMaptanda tangan termasuk pemasok peta untuk menentukan jenis peta.
zrvan
4

Jika Anda hanya perlu menambahkan satu nilai ke peta, Anda dapat menggunakan Collections.singletonMap :

Map<K, V> map = Collections.singletonMap(key, value)
Stromata
sumber
4

Anda dapat menggunakan StickyMapdan MapEntrydari Cactoos :

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

Pendekatan kedua Anda (inisialisasi Double Brace) dianggap sebagai pola anti , jadi saya akan menggunakan pendekatan pertama.

Cara mudah lain untuk menginisialisasi Peta statis adalah dengan menggunakan fungsi utilitas ini:

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

Catatan: Java 9Anda dapat menggunakan Map.of

R. Oosterholt
sumber
3

Saya tidak suka sintaks initializer Static dan saya tidak yakin dengan subclass anonim. Secara umum, saya setuju dengan semua kontra menggunakan inisialisasi Static dan semua kontra menggunakan subclass anonim yang disebutkan dalam jawaban sebelumnya. Di sisi lain - pro yang disajikan dalam posting ini tidak cukup untuk saya. Saya lebih suka menggunakan metode inisialisasi statis:

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;
    }
}
Stanisław Borowy
sumber
3

Saya belum melihat pendekatan yang saya gunakan (dan semakin menyukai) yang diposting dalam jawaban apa pun, jadi ini dia:

Saya tidak suka menggunakan inisialisasi statis karena mereka kikuk, dan saya tidak suka kelas anonim karena membuat kelas baru untuk setiap contoh.

sebagai gantinya, saya lebih suka inisialisasi yang terlihat seperti ini:

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

sayangnya, metode ini bukan bagian dari pustaka Java standar, jadi Anda perlu membuat (atau menggunakan) pustaka utilitas yang mendefinisikan metode berikut:

 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)

(Anda dapat menggunakan 'impor statis' untuk menghindari keharusan awalan nama metode)

Saya merasa bermanfaat untuk menyediakan metode statis yang serupa untuk koleksi lainnya (daftar, set, setel Urutkan, Urutkan Peta, dll.)

Ini tidak sebagus inisialisasi objek json, tapi ini adalah langkah ke arah itu, sejauh menyangkut keterbacaan.

wah
sumber
3

Karena Java tidak mendukung peta literal, instance peta harus selalu dibuat secara eksplisit dan dihuni.

Untungnya, dimungkinkan untuk memperkirakan perilaku literal peta di Jawa menggunakan metode pabrik .

Sebagai contoh:

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

Keluaran:

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

Ini jauh lebih nyaman daripada membuat dan mengisi elemen peta pada suatu waktu.

nazar_art
sumber
2

JEP 269 menyediakan beberapa metode pabrik praktis untuk Collections API. Metode pabrik ini tidak dalam versi Java saat ini, yaitu 8, tetapi direncanakan untuk rilis Java 9.

Karena Mapada dua metode pabrik: ofdan ofEntries. Menggunakan of, Anda dapat melewati pasangan kunci / nilai bolak-balik. Misalnya, untuk membuat Mapsejenis {age: 27, major: cs}:

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

Saat ini ada sepuluh versi kelebihan beban of, sehingga Anda dapat membuat peta yang berisi sepuluh pasangan kunci / nilai. Jika Anda tidak menyukai batasan ini atau kunci / nilai yang berganti-ganti, Anda dapat menggunakan ofEntries:

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

Keduanya ofdan ofEntriesakan mengembalikan yang tidak berubah Map, sehingga Anda tidak dapat mengubah elemen mereka setelah konstruksi. Anda dapat mencoba fitur-fitur ini menggunakan JDK 9 Early Access .

Ali Dehghani
sumber
2

Yah ... saya suka enum;)

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 ) );
}
jglatre
sumber
2

Saya sudah membaca jawabannya dan saya memutuskan untuk menulis pembuat peta saya sendiri. Jangan ragu untuk menyalin dan menempel.

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: Akhir-akhir ini, saya terus menemukan metode statis publik ofcukup sering dan saya agak menyukainya. Saya menambahkannya ke dalam kode dan menjadikan konstruktor sebagai pribadi, sehingga beralih ke pola metode pabrik statis.

EDIT2: Bahkan lebih baru-baru ini, saya tidak lagi suka metode statis yang disebut of, karena terlihat sangat buruk ketika menggunakan impor statis. Saya mengganti namanya menjadi mapOf, membuatnya lebih cocok untuk impor statis.

Vlasec
sumber