ผู้สร้าง HashMap


109

Guava ให้วิธีการโรงงานที่ยอดเยี่ยมสำหรับประเภท Java แก่เราเช่นMaps.newHashMap().

แต่ยังมีผู้สร้างสำหรับแผนที่ java ด้วยหรือไม่?

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

คำตอบ:


20

เนื่องจากMapอินเทอร์เฟซJava 9 ประกอบด้วย:

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

ข้อ จำกัด ของวิธีการโรงงานเหล่านั้นคือ:

  • ไม่สามารถถือnulls เป็นคีย์และ / หรือค่าได้ (หากคุณต้องการเก็บค่าว่างลองดูคำตอบอื่น ๆ )
  • สร้างแผนที่ที่ ไม่เปลี่ยนรูป

หากเราต้องการแผนที่ที่เปลี่ยนแปลงได้ (เช่น HashMap) เราสามารถใช้ตัวสร้างการคัดลอกและปล่อยให้มันคัดลอกเนื้อหาของแผนที่ที่สร้างผ่านMap.of(..)

Map<Integer, String> map = new HashMap<>( Map.of(1,"a", 2,"b", 3,"c") );

2
โปรดสังเกตว่าเมธอด Java 9 ไม่อนุญาตให้ใช้nullค่าซึ่งอาจเป็นปัญหาได้ขึ้นอยู่กับกรณีการใช้งาน
Per Lundberg

@ จอช. IMO Map.of(k1,v1, k2,v2, ...)สามารถใช้ได้อย่างปลอดภัยเมื่อเราไม่มีค่ามากมาย สำหรับค่าจำนวนMap.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)มากขึ้นทำให้เราสามารถอ่านโค้ดได้มากขึ้นซึ่งมีโอกาสเกิดข้อผิดพลาดน้อยกว่า (เว้นแต่ฉันเข้าใจคุณผิด)
Pshemo

คุณเข้าใจดี อดีตนั้นเลวร้ายมากสำหรับฉัน ฉันไม่ยอมใช้มัน!
Josh M.

165

ไม่มีสิ่งนี้สำหรับ HashMaps แต่คุณสามารถสร้าง ImmutableMap ด้วยตัวสร้าง:

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

และหากคุณต้องการแผนที่ที่เปลี่ยนแปลงได้คุณสามารถป้อนข้อมูลนั้นไปยังตัวสร้าง HashMap

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

43
ImmutableMapไม่รองรับnullค่า ดังนั้นจึงมีข้อ จำกัด ของวิธีการนี้: คุณไม่สามารถตั้งค่าในของคุณจะHashMap null
vitaly

5
sean-patrick-floyd ตัวอย่างที่ใช้ได้จริงอย่างหนึ่ง: NamedParameterJdbcTemplateของ Spring คาดว่าจะมีการกำหนดแผนที่ของค่าที่ป้อนโดยชื่อพารามิเตอร์ สมมติว่าฉันต้องการใช้ NamedParameterJdbcTemplate เพื่อตั้งค่าคอลัมน์เป็น null ฉันไม่เห็น: a) มันเป็นกลิ่นรหัสอย่างไร b) วิธีใช้รูปแบบวัตถุว่างที่นี่
vitaly

2
@vitaly ไม่สามารถเถียงเรื่องนั้นได้
Sean Patrick Floyd

2
มีอะไรผิดปกติกับการใช้new HashMapJava constructor แทนMaps.newHashMapวิธีการแบบคงที่หรือไม่?
CorayThan

1
@CorayThan - Jonik ถูกต้องเป็นเพียงทางลัดที่อาศัยการอนุมานประเภทคงที่ stackoverflow.com/a/13153812
AndersDJohnson

46

ไม่ใช่ตัวสร้าง แต่ใช้ตัวเริ่มต้น:

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

รอ. จะไม่ทำให้map instanceof HashMapเป็นเท็จ? ดูเหมือนเป็นความคิดที่ไม่ค่อยดีนัก
Elazar Leibovich

3
@ เอลาซาร์map.getClass()==HashMap.classจะกลับเท็จ แต่นั่นเป็นการทดสอบที่โง่อยู่แล้ว HashMap.class.isInstance(map)ควรเป็นที่ต้องการและจะกลับมาเป็นจริง
Sean Patrick Floyd

59
ที่กล่าวว่า: ฉันยังคิดว่าการแก้ปัญหานี้เป็นสิ่งที่ชั่วร้าย
Sean Patrick Floyd

11
นี่คือตัวเริ่มต้นอินสแตนซ์ไม่ใช่ตัวเริ่มต้นแบบคงที่ มันถูกเรียกใช้หลังจากตัวสร้างของซุปเปอร์ แต่อยู่ก่อนเนื้อหาของตัวสร้างสำหรับตัวสร้างทุกตัวในคลาส วงจรชีวิตไม่เป็นที่รู้จักมากนักดังนั้นฉันจึงหลีกเลี่ยงสำนวนนี้
Joe Coder

14
นี่เป็นวิธีแก้ปัญหาที่แย่มากและควรหลีกเลี่ยง: stackoverflow.com/a/27521360/3253277
Alexandre DuBreuil

36

สิ่งนี้คล้ายกับคำตอบที่ยอมรับ แต่ดูสะอาดกว่าเล็กน้อยในมุมมองของฉัน:

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

วิธีการข้างต้นมีหลายรูปแบบและเหมาะสำหรับการสร้างแผนที่แบบคงที่ไม่เปลี่ยนแปลงไม่เปลี่ยนรูป


4
ฉันถามหาช่างก่อสร้าง คุณมีองค์ประกอบไม่มากนัก
Elazar Leibovich

สวยและสะอาด แต่มันทำให้ฉันโหยหา Perl's => operator ... ซึ่งเป็นความรู้สึกแปลก ๆ
Aaron Maenpaa

10

นี่คือสิ่งที่ง่ายมาก ...

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

แล้ว

import static FluentHashMap.map;

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

ดูhttps://gist.github.com/culmat/a3bcc646fa4401641ac6eb01f3719065


ฉันชอบความเรียบง่ายในแนวทางของคุณ โดยเฉพาะอย่างยิ่งเนื่องจากนี่คือปี 2017 (เกือบ 2018 แล้ว!) และยังไม่มี API ดังกล่าวใน JDK
Milad Naseri

ดูดีมากขอบคุณ @MiladNaseri มันบ้ามากที่ JDK ยังไม่มีอะไรแบบนี้ใน API มันช่างน่าละอายเหลือเกิน
ไม่น่าจะเป็นไปได้

9

เครื่องมือสร้างแผนที่ที่เรียบง่ายเป็นเรื่องเล็กน้อยที่จะเขียน:

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

6

คุณสามารถใช้ได้:

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

มันไม่ดีงามและอ่านง่าย แต่ใช้งานได้จริง


1
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);, ดีกว่า?
lschin

เช่นเดียวกับตัวสร้าง แต่มีข้อมูลจำนวน จำกัด เนื่องจากมีการใช้งานเกินพิกัด หากคุณมีองค์ประกอบเพียงไม่กี่ชิ้น - ฉันคิดว่าน่าจะดีกว่า
Elazar Leibovich

4

HashMapไม่แน่นอน; ไม่จำเป็นต้องมีผู้สร้าง

Map<String, Integer> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 2);

จะเกิดอะไรขึ้นถ้าคุณต้องการเริ่มต้นฟิลด์ด้วย? ตรรกะทั้งหมดในบรรทัดเดียวกันดีกว่าตรรกะที่กระจัดกระจายระหว่างฟิลด์และ c'tor
Elazar Leibovich

@Elazar: ถ้าคุณต้องการที่จะเริ่มต้นเขตข้อมูลที่มีค่าเฉพาะที่เป็นที่รู้จักที่รวบรวมเวลาเช่นนี้คุณมักจะImmutableSetต้องการข้อมูลที่จะไม่เปลี่ยนรูปและควรใช้ หากคุณต้องการให้มันเปลี่ยนแปลงได้จริงๆคุณสามารถเริ่มต้นได้ในตัวสร้างหรือบล็อกตัวเริ่มต้นอินสแตนซ์หรือบล็อกตัวเริ่มต้นแบบคงที่หากเป็นฟิลด์แบบคงที่
ColinD

1
เอ่อน่าจะพูดImmutableMapชัด ๆ
ColinD

ฉันไม่คิดอย่างนั้น ฉันอยากจะเห็น intialization ในบรรทัดเดียวกันของคำจำกัดความจากนั้นให้พวกเขาซ่อนไว้ใน intialization แบบไม่คงที่{{init();}}(ไม่ใช่ในตัวสร้างเนื่องจากตัวสร้างอื่นอาจลืมมัน) และเป็นเรื่องดีที่เป็นการกระทำของอะตอม หากแผนที่มีความผันผวนให้ทำการกำหนดค่าเริ่มต้นด้วยตัวสร้างเพื่อให้แน่ใจว่าแผนที่นั้นเสมอnullหรืออยู่ในสถานะสุดท้ายไม่ต้องเติมครึ่งหนึ่ง
Elazar Leibovich

1

คุณสามารถใช้ Fluent API ในEclipse Collections :

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

นี่คือบล็อกที่มีรายละเอียดและตัวอย่างเพิ่มเติม

หมายเหตุ: ฉันเป็นผู้กำหนดค่า Eclipse Collections


0

ฉันมีความต้องการที่คล้ายกันในขณะที่กลับมา มันไม่เกี่ยวอะไรกับฝรั่ง แต่คุณสามารถทำอะไรแบบนี้เพื่อให้สามารถสร้าง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;
    }
}

จากนั้นสร้างเครื่องมือสร้างที่คล่องแคล่วด้วยวิธีการที่เหมาะกับความต้องการของคุณ:

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

}

จากนั้นคุณสามารถใช้งานได้ดังนี้:

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

0

นี่คือสิ่งที่ฉันต้องการมาตลอดโดยเฉพาะอย่างยิ่งในขณะตั้งค่าการทดสอบ ในที่สุดฉันตัดสินใจที่จะเขียนโปรแกรมสร้างที่คล่องแคล่วง่าย ๆ ของฉันเองที่สามารถสร้างการใช้งานแผนที่ได้ - 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;
    }

0

นี่คือสิ่งที่ฉันเขียน

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

คุณใช้สิ่งนี้:

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

0

ใช้ java 8:

นี่คือแนวทางของ 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);
    }
}

วิธีใช้:

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


0

Underscore-javaสามารถสร้าง hashmap

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}]}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.