pembangun untuk HashMap

109

Guava memberi kami metode pabrik yang bagus untuk jenis Java, seperti Maps.newHashMap().

Tetapi apakah ada juga pembuat untuk java Maps?

HashMap<String,Integer> m = Maps.BuildHashMap.
    put("a",1).
    put("b",2).
    build();
Elazar Leibovich
sumber

Jawaban:

20

Karena Mapantarmuka Java 9 berisi:

  • Map.of(k1,v1, k2,v2, ..)
  • Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ..).

Batasan dari metode pabrik tersebut adalah:

  • tidak dapat menyimpan nulls sebagai kunci dan / atau nilai (jika Anda perlu menyimpan null, lihat jawaban lain)
  • menghasilkan peta yang tidak dapat diubah

Jika kita membutuhkan peta yang dapat berubah (seperti HashMap) kita dapat menggunakan copy-konstruktornya dan membiarkannya menyalin konten peta yang dibuat melaluiMap.of(..)

Map<Integer, String> map = new HashMap<>( Map.of(1,"a", 2,"b", 3,"c") );
Pshemo
sumber
2
Perhatikan bahwa metode Java 9 tidak mengizinkan nullnilai, yang bisa menjadi masalah bergantung pada kasus penggunaan.
Per Lundberg
@Tokopedia IMO Map.of(k1,v1, k2,v2, ...)dapat digunakan dengan aman saat kita tidak memiliki banyak nilai. Untuk jumlah nilai yang lebih besar Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)memberi kami kode yang lebih mudah dibaca yang lebih sedikit rawan kesalahan (kecuali saya salah paham).
Pshemo
Anda mengerti dengan baik. Yang pertama benar-benar menjijikkan bagi saya; Saya menolak untuk menggunakannya!
Josh
164

Tidak ada hal seperti itu untuk HashMaps, tetapi Anda dapat membuat ImmutableMap dengan pembuat:

final Map<String, Integer> m = ImmutableMap.<String, Integer>builder().
      put("a", 1).
      put("b", 2).
      build();

Dan jika Anda membutuhkan peta yang bisa berubah, Anda bisa memasukkannya ke konstruktor HashMap.

final Map<String, Integer> m = Maps.newHashMap(
    ImmutableMap.<String, Integer>builder().
        put("a", 1).
        put("b", 2).
        build());
Sean Patrick Floyd
sumber
43
ImmutableMaptidak mendukung nullnilai-nilai. Jadi, ada batasan dari pendekatan ini: Anda tidak dapat menetapkan nilai di HashMapto null.
vitaly
5
sean-patrick-floyd Nah, satu contoh praktis: Spring's NamedParameterJdbcTemplate mengharapkan peta nilai yang dikunci oleh nama parameter. Katakanlah saya ingin menggunakan NamedParameterJdbcTemplate untuk menetapkan nilai kolom ke null. Saya tidak melihat: a) bagaimana hal itu merupakan bau kode; b) bagaimana menggunakan pola objek nol di sini
vitaly
2
@vitaly tidak bisa membantahnya
Sean Patrick Floyd
2
Apakah ada yang salah dengan menggunakan new HashMapkonstruktor Java daripada Maps.newHashMapmetode statis ?
CorayThan
1
@CorayThan - Jonik benar, ini hanya jalan pintas yang mengandalkan inferensi tipe statis. stackoverflow.com/a/13153812
AndersDJohnson
46

Bukan pembuatnya, tapi menggunakan penginisialisasi:

Map<String, String> map = new HashMap<String, String>() {{
    put("a", "1");
    put("b", "2");
}};
Johan Sjöberg
sumber
Tunggu. Bukankah itu map instanceof HashMapsalah? Sepertinya ide yang tidak terlalu bagus.
Elazar Leibovich
3
@Elazar map.getClass()==HashMap.classakan mengembalikan false. Tapi itu tes bodoh. HashMap.class.isInstance(map)harus lebih disukai, dan itu akan menjadi kenyataan.
Sean Patrick Floyd
59
Yang mengatakan: Saya masih menganggap solusi ini jahat.
Sean Patrick Floyd
11
Ini adalah penginisialisasi instance, bukan penginisialisasi statis. Ini dijalankan setelah konstruktor super, tetapi sebelum tubuh konstruktor, untuk setiap konstruktor di kelas. Siklus hidup tidak terlalu terkenal, jadi saya menghindari idiom ini.
Joe Coder
14
Ini adalah solusi yang sangat buruk dan harus dihindari: stackoverflow.com/a/27521360/3253277
Alexandre DuBreuil
36

Ini mirip dengan jawaban yang diterima, tetapi sedikit lebih bersih, menurut saya:

ImmutableMap.of("key1", val1, "key2", val2, "key3", val3);

Ada beberapa variasi dari metode di atas, dan metode ini bagus untuk membuat peta yang statis, tidak berubah, dan tidak berubah.

Jake Toronto
sumber
4
Saya meminta seorang pembangun. Anda dibatasi pada beberapa elemen.
Elazar Leibovich
Bagus dan bersih, tetapi itu membuat saya merindukan operator Perl's => ... yang merupakan perasaan yang aneh.
Aaron Maenpaa
10

Ini yang sangat sederhana ...

public class FluentHashMap<K, V> extends java.util.HashMap<K, V> {
  public FluentHashMap<K, V> with(K key, V value) {
    put(key, value);
    return this;
  }

  public static <K, V> FluentHashMap<K, V> map(K key, V value) {
    return new FluentHashMap<K, V>().with(key, value);
  }
}

kemudian

import static FluentHashMap.map;

HashMap<String, Integer> m = map("a", 1).with("b", 2);

Lihat https://gist.github.com/culmat/a3bcc646fa4401641ac6eb01f3719065

culmat
sumber
Saya suka kesederhanaan pendekatan Anda. Terutama karena ini 2017 (hampir 2018 sekarang!), Dan masih belum ada API semacam itu di JDK
Milad Naseri
Terlihat sangat keren, terima kasih. @MiladNaseri sungguh gila bahwa JDK masih belum memiliki sesuatu seperti ini di API-nya, sungguh memalukan.
tidak mungkin
9

Pembuat peta sederhana itu mudah untuk ditulis:

public class Maps {

    public static <Q,W> MapWrapper<Q,W> map(Q q, W w) {
        return new MapWrapper<Q, W>(q, w);
    }

    public static final class MapWrapper<Q,W> {
        private final HashMap<Q,W> map;
        public MapWrapper(Q q, W w) {
            map = new HashMap<Q, W>();
            map.put(q, w);
        }
        public MapWrapper<Q,W> map(Q q, W w) {
            map.put(q, w);
            return this;
        }
        public Map<Q,W> getMap() {
            return map;
        }
    }

    public static void main(String[] args) {
        Map<String, Integer> map = Maps.map("one", 1).map("two", 2).map("three", 3).getMap();
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }
}
Agnes
sumber
6

Kamu bisa memakai:

HashMap<String,Integer> m = Maps.newHashMap(
    ImmutableMap.of("a",1,"b",2)
);

Ini tidak berkelas dan mudah dibaca, tetapi berhasil.

Elazar Leibovich
sumber
1
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);lebih baik?
lschin
Sama seperti pembuatnya, tetapi dengan jumlah data yang terbatas, karena diterapkan dengan kelebihan beban. Jika Anda hanya memiliki sedikit elemen - saya rasa itu lebih disukai.
Elazar Leibovich
4

HashMapbisa berubah; tidak perlu seorang pembangun.

Map<String, Integer> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 2);
ColinD
sumber
Bagaimana jika Anda ingin menginisialisasi bidang dengannya? Semua logika dalam baris yang sama lebih baik daripada logika yang tersebar antara field dan c'tor.
Elazar Leibovich
@Elazar: Jika Anda ingin menginisialisasi bidang dengan nilai tertentu yang diketahui pada waktu kompilasi seperti ini, Anda biasanya ingin bidang tersebut tidak dapat diubah dan harus digunakan ImmutableSet. Jika Anda benar-benar ingin ini bisa berubah, Anda bisa menginisialisasinya di konstruktor atau blok penginisialisasi instance atau blok penginisialisasi statis jika itu bidang statis.
ColinD
1
Er, seharusnya mengatakan di ImmutableMapsana dengan jelas.
ColinD
Saya kira tidak. Saya lebih suka melihat inisialisasi di baris definisi yang sama, lalu memasukkannya ke dalam inisialisasi non-statis {{init();}}(bukan di konstruktor, karena konstruktor lain mungkin melupakannya). Dan itu bagus karena itu semacam aksi atom. Jika peta mudah berubah, kemudian menginisialisasi dengan pembangun memastikan peta itu selalu baik nullatau dalam keadaan akhir, tidak pernah terisi setengah.
Elazar Leibovich
1

Anda dapat menggunakan API yang lancar di Koleksi Eclipse :

Map<String, Integer> map = Maps.mutable.<String, Integer>empty()
        .withKeyValue("a", 1)
        .withKeyValue("b", 2);

Assert.assertEquals(Maps.mutable.with("a", 1, "b", 2), map);

Berikut adalah blog dengan lebih detail dan contoh.

Catatan: Saya seorang pelaku untuk Koleksi Eclipse.

Donald Raab
sumber
0

Saya memiliki persyaratan serupa beberapa waktu lalu. Ini tidak ada hubungannya dengan Guava tetapi Anda dapat melakukan hal seperti ini untuk dapat membangun dengan rapi Mapmenggunakan pembangun yang lancar.

Buat kelas dasar yang memperluas Map.

public class FluentHashMap<K, V> extends LinkedHashMap<K, V> {
    private static final long serialVersionUID = 4857340227048063855L;

    public FluentHashMap() {}

    public FluentHashMap<K, V> delete(Object key) {
        this.remove(key);
        return this;
    }
}

Kemudian buat pembangun yang fasih dengan metode yang sesuai dengan kebutuhan Anda:

public class ValueMap extends FluentHashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    public ValueMap() {}

    public ValueMap withValue(String key, String val) {
        super.put(key, val);
        return this;
    }

... Add withXYZ to suit...

}

Anda kemudian dapat menerapkannya seperti ini:

ValueMap map = new ValueMap()
      .withValue("key 1", "value 1")
      .withValue("key 2", "value 2")
      .withValue("key 3", "value 3")
tarka
sumber
0

Ini adalah sesuatu yang selalu saya inginkan, terutama saat menyiapkan perlengkapan tes. Akhirnya, saya memutuskan untuk menulis pembuat fasih sederhana milik saya yang dapat membangun implementasi Peta apa pun - https://gist.github.com/samshu/b471f5a2925fa9d9b718795d8bbdfe42#file-mapbuilder-java

    /**
     * @param mapClass Any {@link Map} implementation type. e.g., HashMap.class
     */
    public static <K, V> MapBuilder<K, V> builder(@SuppressWarnings("rawtypes") Class<? extends Map> mapClass)
            throws InstantiationException,
            IllegalAccessException {
        return new MapBuilder<K, V>(mapClass);
    }

    public MapBuilder<K, V> put(K key, V value) {
        map.put(key, value);
        return this;
    }

    public Map<K, V> build() {
        return map;
    }
aathif
sumber
0

Ini yang saya tulis

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

public class MapBuilder<K, V> {

    private final Map<K, V> map;

    /**
     * Create a HashMap builder
     */
    public MapBuilder() {
        map = new HashMap<>();
    }

    /**
     * Create a HashMap builder
     * @param initialCapacity
     */
    public MapBuilder(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * Create a Map builder
     * @param mapFactory
     */
    public MapBuilder(Supplier<Map<K, V>> mapFactory) {
        map = mapFactory.get();
    }

    public MapBuilder<K, V> put(K key, V value) {
        map.put(key, value);
        return this;
    }

    public Map<K, V> build() {
        return map;
    }

    /**
     * Returns an unmodifiable Map. Strictly speaking, the Map is not immutable because any code with a reference to
     * the builder could mutate it.
     *
     * @return
     */
    public Map<K, V> buildUnmodifiable() {
        return Collections.unmodifiableMap(map);
    }
}

Anda menggunakannya seperti ini:

Map<String, Object> map = new MapBuilder<String, Object>(LinkedHashMap::new)
    .put("event_type", newEvent.getType())
    .put("app_package_name", newEvent.getPackageName())
    .put("activity", newEvent.getActivity())
    .build();
Dónal
sumber
0

Menggunakan java 8:

Ini adalah pendekatan Java-9 Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)

public class MapUtil {
    import static java.util.stream.Collectors.toMap;

    import java.util.AbstractMap.SimpleEntry;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.stream.Stream;

    private MapUtil() {}

    @SafeVarargs
    public static Map<String, Object> ofEntries(SimpleEntry<String, Object>... values) {
        return Stream.of(values).collect(toMap(Entry::getKey, Entry::getValue));
    }

    public static SimpleEntry<String, Object> entry(String key, Object value) {
        return new SimpleEntry<String, Object>(key, value);
    }
}

Cara Penggunaan:

import static your.package.name.MapUtil.*;

import java.util.Map;

Map<String, Object> map = ofEntries(
        entry("id", 1),
        entry("description", "xyz"),
        entry("value", 1.05),
        entry("enable", true)
    );
Leandro Fantinel
sumber
0

Ada ImmutableMap.builder()di Jambu.

Michal
sumber
0

Underscore-java dapat membuat peta hash.

Map<String, Object> value = U.objectBuilder()
        .add("firstName", "John")
        .add("lastName", "Smith")
        .add("age", 25)
        .add("address", U.arrayBuilder()
            .add(U.objectBuilder()
                .add("streetAddress", "21 2nd Street")
                .add("city", "New York")
                .add("state", "NY")
                .add("postalCode", "10021")))
        .add("phoneNumber", U.arrayBuilder()
            .add(U.objectBuilder()
                .add("type", "home")
                .add("number", "212 555-1234"))
            .add(U.objectBuilder()
                .add("type", "fax")
                .add("number", "646 555-4567")))
        .build();
    // {firstName=John, lastName=Smith, age=25, address=[{streetAddress=21 2nd Street,
    // city=New York, state=NY, postalCode=10021}], phoneNumber=[{type=home, number=212 555-1234},
    // {type=fax, number=646 555-4567}]}
Valentyn Kolesnikov
sumber