trình tạo cho HashMap


Câu trả lời:


20

Mapgiao diện Java 9 chứa:

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

Hạn chế của các phương pháp nhà máy đó là:

  • không thể giữ nulls làm khóa và / hoặc giá trị (nếu bạn cần lưu trữ null, hãy xem các câu trả lời khác)
  • tạo bản đồ bất biến

Nếu chúng ta cần bản đồ có thể thay đổi (như HashMap), chúng ta có thể sử dụng hàm tạo bản sao của nó và cho phép nó sao chép nội dung của bản đồ được tạo quaMap.of(..)

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

2
Lưu ý rằng các phương thức Java 9 không cho phép nullcác giá trị, có thể là một vấn đề tùy thuộc vào trường hợp sử dụng.
Per Lundberg

@JoshM. IMO Map.of(k1,v1, k2,v2, ...)có thể được sử dụng một cách an toàn khi chúng ta không có nhiều giá trị. Đối với số lượng giá trị lớn hơn Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)cung cấp cho chúng tôi mã dễ đọc hơn và ít bị lỗi hơn (trừ khi tôi hiểu nhầm bạn).
Pshemo

Bạn hiểu tốt. Điều trước đây chỉ thực sự thô thiển với tôi; Tôi từ chối sử dụng nó!
Josh M.

164

Không có điều đó cho HashMaps, nhưng bạn có thể tạo ImmutableMap với một trình tạo:

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

Và nếu bạn cần một bản đồ có thể thay đổi, bạn chỉ có thể cung cấp bản đồ đó cho hàm tạo HashMap.

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

43
ImmutableMapkhông hỗ trợ nullcác giá trị. Vì vậy, hạn chế của phương pháp này: bạn không thể đặt giá trị trong bạn HashMapđể null.
importanty

5
sean-Patrick-floyd Vâng, một ví dụ thực tế: Spring's NamedParameterJdbcTemplate mong đợi bản đồ các giá trị được khóa bằng tên tham số. Giả sử tôi muốn sử dụng NamedParameterJdbcTemplate để đặt giá trị cột thành null. Tôi không thấy: a) nó là mùi mã như thế nào; b) cách sử dụng mẫu đối tượng null ở đây
importanty

2
@vitaly không thể tranh cãi với điều đó
Sean Patrick Floyd

2
Có điều gì sai khi sử dụng hàm tạo new HashMapJava thay vì Maps.newHashMapphương thức tĩnh không?
CorayThan

1
@CorayThan - Jonik đúng, nó chỉ là một phím tắt dựa trên suy luận kiểu tĩnh. stackoverflow.com/a/13153812
AndersDJohnson

46

Không hẳn là một trình xây dựng, nhưng sử dụng một trình khởi tạo:

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

Chờ đợi. Điều đó có làm map instanceof HashMapsai không? Có vẻ như một ý tưởng không quá tuyệt vời.
Elazar Leibovich

3
@Elazar map.getClass()==HashMap.classsẽ trả về false. Nhưng dù sao đó cũng là một bài kiểm tra ngu ngốc. HashMap.class.isInstance(map)nên được ưu tiên, và điều đó sẽ trả về true.
Sean Patrick Floyd

59
Điều đó nói rằng: Tôi vẫn nghĩ rằng giải pháp này là xấu xa.
Sean Patrick Floyd

11
Đây là một trình khởi tạo thể hiện, không phải là một trình khởi tạo tĩnh. Nó được chạy sau hàm tạo của super, nhưng trước phần thân của hàm tạo, đối với mọi hàm tạo trong lớp. Vòng đời không được nhiều người biết đến nên tôi tránh câu thành ngữ này.
Joe Coder

14
Đây là một giải pháp rất xấu và cần tránh: stackoverflow.com/a/27521360/3253277
Alexandre Dubreuil

36

Điều này tương tự với câu trả lời được chấp nhận, nhưng rõ ràng hơn một chút, theo quan điểm của tôi:

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

Có một số biến thể của phương pháp trên và chúng rất tốt để tạo bản đồ tĩnh, không thay đổi, bất biến.


4
Tôi yêu cầu một người xây dựng. Bạn bị giới hạn trong một số ít các yếu tố.
Elazar Leibovich,

Đẹp và sạch sẽ, nhưng nó khiến tôi khao khát điều hành viên của Perl => ... đó là một cảm giác kỳ lạ.
Aaron Maenpaa

10

Đây là một cái rất đơn giản ...

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

sau đó

import static FluentHashMap.map;

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

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


Tôi thích sự đơn giản trong cách tiếp cận của bạn. Đặc biệt vì đây là năm 2017 (gần 2018 bây giờ!), Và vẫn còn là API không có như vậy trong JDK
Milad Naseri

Trông rất tuyệt, cảm ơn. @MiladNaseri thật điên rồ khi JDK không có thứ gì đó như thế này trong API của nó, thật là một sự xấu hổ.
không chắc chắn

9

Một người xây dựng bản đồ đơn giản thì không thể viết được:

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

Bạn có thể dùng:

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

Nó không sang trọng và dễ đọc, nhưng hoạt động hiệu quả.


1
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);, tốt hơn?
lschin

Giống như trình tạo, nhưng với số lượng dữ liệu hạn chế, vì nó được triển khai với quá tải. Nếu bạn chỉ có một vài yếu tố - tôi đoán nó sẽ tốt hơn.
Elazar Leibovich

4

HashMapcó thể thay đổi; không cần một người xây dựng.

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

Điều gì xảy ra nếu bạn muốn khởi tạo một trường với nó? Tất cả logic trong cùng một dòng tốt hơn logic nằm rải rác giữa trường và c'tor.
Elazar Leibovich

@Elazar: Nếu bạn muốn khởi tạo một trường với các giá trị cụ thể đã biết tại thời điểm biên dịch như thế này, bạn thường muốn trường đó là bất biến và nên sử dụng ImmutableSet. Nếu bạn thực sự muốn nó có thể thay đổi được, bạn có thể khởi tạo nó trong khối khởi tạo hoặc khối khởi tạo phiên bản hoặc khối khởi tạo tĩnh nếu đó là trường tĩnh.
ColinD

1
Er, lẽ ra phải nói ImmutableMaprõ ràng ở đó.
ColinD

Tôi không nghĩ vậy. Tôi muốn xem intialization trong cùng một dòng định nghĩa, sau đó đặt chúng trong một intialization {{init();}}không tĩnh (không phải trong hàm tạo, vì hàm tạo khác có thể quên nó). Và thật tuyệt khi đó là một loại hành động nguyên tử. Nếu bản đồ dễ thay đổi, thì việc phức tạp hóa nó bằng một trình xây dựng để đảm bảo rằng nó luôn nullở trạng thái cuối cùng, không bao giờ bị lấp đầy một nửa.
Elazar Leibovich

1

Bạn có thể sử dụng API thông thạo trong Bộ sưu tập 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);

Đây là một blog với nhiều chi tiết và ví dụ.

Lưu ý: Tôi là người cam kết cho Bộ sưu tập Eclipse.


0

Tôi đã có một yêu cầu tương tự một thời gian trước. Nó không liên quan gì đến Guava nhưng bạn có thể làm điều gì đó như thế này để có thể xây dựng một Mapcách sạch sẽ bằng cách sử dụng một trình xây dựng thông thạo.

Tạo một lớp cơ sở mở rộng Bản đồ.

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

Sau đó, tạo trình xây dựng thông thạo bằng các phương pháp phù hợp với nhu cầu của bạn:

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

}

Sau đó, bạn có thể triển khai nó như thế này:

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

0

Đây là điều mà tôi luôn mong muốn, đặc biệt là trong khi thiết lập các thiết bị kiểm tra. Cuối cùng, tôi quyết định viết một trình xây dựng thông thạo đơn giản của riêng tôi có thể xây dựng bất kỳ triển khai Bản đồ nào - 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

Đây là một trong những tôi đã viết

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

Bạn sử dụng nó như thế này:

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

Sử dụng java 8:

Đây là một cách tiếp cận của 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);
    }
}

Cách sử dụng:

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 có thể xây dựng bản đồ băm.

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}]}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.