Làm cách nào để trực tiếp khởi tạo HashMap (theo nghĩa đen)?


1094

Có cách nào để khởi tạo Java HashMap như thế này không?:

Map<String,String> test = 
    new HashMap<String, String>{"test":"test","test":"test"};

Điều gì sẽ là cú pháp chính xác? Tôi đã không tìm thấy bất cứ điều gì liên quan đến điều này. Điều này có thể không? Tôi đang tìm cách ngắn nhất / nhanh nhất để đặt một số giá trị "cuối cùng / tĩnh" vào bản đồ không bao giờ thay đổi và được biết trước khi tạo Bản đồ.



Liên quan chặt chẽ: stackoverflow.com/questions/507602/ (Cả hai câu hỏi là về việc khởi tạo một bản đồ không đổi với các giá trị tĩnh, cuối cùng.)
Jonik



Nếu bạn sử dụng apache.commons.collections, bạn có thể sử dụng commons.apache.org/proper/commons-collections/javadocs/ Lỗi
ax.

Câu trả lời:


1344

Tất cả các phiên bản

Trong trường hợp bạn tình cờ chỉ cần một mục duy nhất: Có Collections.singletonMap("key", "value").

Đối với phiên bản Java 9 trở lên:

Vâng, điều này là có thể bây giờ. Trong Java 9, một vài phương thức nhà máy đã được thêm vào để đơn giản hóa việc tạo bản đồ:

// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
    "a", "b",
    "c", "d"
);

// this works for any number of elements:
import static java.util.Map.entry;    
Map<String, String> test2 = Map.ofEntries(
    entry("a", "b"),
    entry("c", "d")
);

Trong ví dụ trên cả hai testtest2sẽ giống nhau, chỉ với các cách khác nhau để thể hiện Bản đồ. Các Map.ofphương pháp được định nghĩa cho đến yếu tố mười trong bản đồ, trong khi Map.ofEntriesphương pháp sẽ không có giới hạn như vậy.

Lưu ý rằng trong trường hợp này, bản đồ kết quả sẽ là một bản đồ bất biến. Nếu bạn muốn bản đồ có thể thay đổi, bạn có thể sao chép lại, ví dụ: sử dụngmutableMap = new HashMap<>(Map.of("a", "b"));

(Xem thêm JEP 269Javadoc )

Cho tới phiên bản Java 8:

Không, bạn sẽ phải thêm tất cả các yếu tố bằng tay. Bạn có thể sử dụng một trình khởi tạo trong một lớp con ẩn danh để làm cho cú pháp ngắn hơn một chút:

Map<String, String> myMap = new HashMap<String, String>() {{
        put("a", "b");
        put("c", "d");
    }};

Tuy nhiên, lớp con ẩn danh có thể đưa ra hành vi không mong muốn trong một số trường hợp. Điều này bao gồm ví dụ:

  • Nó tạo ra một lớp bổ sung làm tăng mức tiêu thụ bộ nhớ, mức tiêu thụ dung lượng ổ đĩa và thời gian khởi động
  • Trong trường hợp phương thức không tĩnh: Nó giữ một tham chiếu đến đối tượng, phương thức tạo được gọi. Điều đó có nghĩa là đối tượng của lớp bên ngoài không thể được thu gom rác trong khi đối tượng bản đồ được tạo vẫn được tham chiếu, do đó chặn bộ nhớ bổ sung

Sử dụng chức năng để khởi tạo cũng sẽ cho phép bạn tạo bản đồ trong trình khởi tạo, nhưng tránh các tác dụng phụ khó chịu:

Map<String, String> myMap = createMap();

private static Map<String, String> createMap() {
    Map<String,String> myMap = new HashMap<String,String>();
    myMap.put("a", "b");
    myMap.put("c", "d");
    return myMap;
}

3
Điều này sẽ không hoạt động nếu bạn muốn khởi tạo các thành phần trong một chức năng ...
Michael

9
@Michael: Vâng, nếu bạn muốn sử dụng một chức năng hơn bạn không thể sử dụng một chức năng không. Nhưng tại sao bạn muốn?
yankee

6
và đối với các trường hợp khi bạn cần một Bản đồ với một mục duy nhất đó Collections.singletonMap():)
skwisgaar

3
Bây giờ Java 9 ổn định đã được phát hành, tôi thích liên kết này cho Javadoc . Và +1 vì ít phụ thuộc hơn!
Franklin Yu

3
entryTài liệu Java 9 ở đâu?
tộc

1030

Đây là một cách.

HashMap<String, String> h = new HashMap<String, String>() {{
    put("a","b");
}};

Tuy nhiên, bạn nên cẩn thận và đảm bảo rằng bạn hiểu mã ở trên (nó tạo ra một lớp mới kế thừa từ HashMap). Do đó, bạn nên đọc thêm tại đây: http://www.c2.com/cgi/wiki?DoubleBraceInitialization hoặc đơn giản là sử dụng Guava:

Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);

72
Nó hoạt động nhưng nó xấu và có tác dụng phụ vô hình mà người dùng nên hiểu trước khi thực hiện - ví dụ, tạo ra toàn bộ một lớp ẩn danh ngay tại chỗ.
18 giờ 48 phút

96
vâng, đó là cách tôi đã viết về việc cẩn thận và đưa ra một liên kết đến mô tả.
gregory561

6
Liên kết tuyệt vời. Tham chiếu trong liên kết đến GreencoddsTenthRuleOfProgramming đáng để đọc.
michaelok

19
bạn có thể thêm "as ImmutableMap.builder.put (" k1 "," v1 "). put (" k2 "," v2 "). build ()" vì phương thức "of" được giới hạn tối đa 5 cặp không?
kommradHomer


342

Nếu bạn cho phép lib của bên thứ 3, bạn có thể sử dụng Bản đồ bất biến của Guava để đạt được sự ngắn gọn giống như nghĩa đen:

Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");

Công trình này lên đến 5 cặp khóa / giá trị , nếu không bạn có thể sử dụng nó xây dựng :

Map<String, String> test = ImmutableMap.<String, String>builder()
    .put("k1", "v1")
    .put("k2", "v2")
    ...
    .build();


  • lưu ý rằng việc triển khai ImmutableMap của Guava khác với HashMap của Java (đáng chú ý nhất là nó không thay đổi và không cho phép các khóa / giá trị null)
  • để biết thêm thông tin, hãy xem bài viết hướng dẫn sử dụng của Guava về các loại bộ sưu tập bất biến của nó

26
Ngoài ra, ổi có ImmutableMap.builder.put ("k1", "v1"). Put ("k2", "v2"). Build ();
Xetius

17
ImmutableMap không giống với HashMap, vì nó sẽ thất bại với các giá trị null, trong khi bản đồ HashMap thì không.
Gewthen

2
Chỉ để giúp đỡ những người khác có thể phải đối mặt với vấn đề này. Bạn phải nhập trình xây dựng để biến nó thành Map <String, String>, như thế này: Map <String, String> test = ImmutableMap. <String, String> builder (). Put ("k1", "v1"). đặt ("k2", "v2"). build ();
Thiago

Đây là Jens tuyệt vời!
gaurav

105

Không có cách nào trực tiếp để làm điều này - Java không có chữ Map (tuy nhiên - tôi nghĩ rằng chúng đã được đề xuất cho Java 8).

Một số người thích điều này:

Map<String,String> test = new HashMap<String, String>(){{
       put("test","test"); put("test","test");}};

Điều này tạo ra một lớp con ẩn danh của HashMap, có trình khởi tạo cá thể đặt các giá trị này. (Nhân tiện, một bản đồ không thể chứa hai lần cùng một giá trị, lần đặt thứ hai của bạn sẽ ghi đè lên lần đầu tiên. Tôi sẽ sử dụng các giá trị khác nhau cho các ví dụ tiếp theo.)

Cách thông thường sẽ là thế này (đối với một biến cục bộ):

Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");

Nếu testbản đồ của bạn là một biến đối tượng, hãy đặt khởi tạo vào hàm tạo hoặc trình khởi tạo cá thể:

Map<String,String> test = new HashMap<String, String>();
{
    test.put("test","test");
    test.put("test1","test2");
}

Nếu testbản đồ của bạn là một biến lớp, hãy đặt khởi tạo vào một trình khởi tạo tĩnh:

static Map<String,String> test = new HashMap<String, String>();
static {
    test.put("test","test");
    test.put("test1","test2");
}

Nếu bạn muốn bản đồ của mình không bao giờ thay đổi, bạn nên sau khi khởi tạo bọc bản đồ của mình bằng Collections.unmodifiableMap(...). Bạn cũng có thể làm điều này trong một trình khởi tạo tĩnh:

static Map<String,String> test;
{
    Map<String,String> temp = new HashMap<String, String>();
    temp.put("test","test");
    temp.put("test1","test2");
    test = Collections.unmodifiableMap(temp);
}

(Tôi không chắc bây giờ bạn có thể vào testchung kết hay không ... hãy thử và báo cáo ở đây.)


61
Map<String,String> test = new HashMap<String, String>()
{
    {
        put(key1, value1);
        put(key2, value2);
    }
};

Ngắn gọn và đúng trọng tâm. Tôi nghĩ rằng điều này với một phần bình luận mở rộng sẽ là câu trả lời tốt nhất.
ooolala

15
Có những ý nghĩa bộ nhớ cần được lưu ý mặc dù. blog.jooq.org/2014/12/08/ cường
Amalgovinus

1
@Amalgovinus Về cơ bản, bằng cách tạo một lớp con mới, bạn khó mã hóa các đối số kiểu từ HashMapvào lớp con này. Điều này chỉ có thể làm việc nếu bạn thực sự cung cấp chúng. (Với HashMap mới (trống), các đối số loại không liên quan.)
Paŭlo Ebermann

1
Tôi thích sự sạch sẽ của nó, nhưng nó tạo ra lớp ẩn danh không cần thiết và có các vấn đề được mô tả ở đây: c2.com/cgi/wiki?DoubleBraceInitialization
udachny

1
@hello_its_me: Bởi vì nó giống như stackoverflow.com/a/6802512/1386911 trả lời, chỉ là định dạng khác nhau. Và trong trường hợp này, định dạng mở rộng này không có giá trị bổ sung trên định dạng nhỏ gọn để dễ đọc.
Daniel Hári

44

Một cách khác, sử dụng các lớp và vararg Java 7 đơn giản: tạo một lớp HashMapBuildervới phương thức này:

public static HashMap<String, String> build(String... data){
    HashMap<String, String> result = new HashMap<String, String>();

    if(data.length % 2 != 0) 
        throw new IllegalArgumentException("Odd number of arguments");      

    String key = null;
    Integer step = -1;

    for(String value : data){
        step++;
        switch(step % 2){
        case 0: 
            if(value == null)
                throw new IllegalArgumentException("Null key value"); 
            key = value;
            continue;
        case 1:             
            result.put(key, value);
            break;
        }
    }

    return result;
}

Sử dụng phương pháp như thế này:

HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");

Tôi đã viết một câu trả lời lấy cảm hứng từ bạn: stackoverflow.com/questions/507602/ từ
GeroldBroser phục hồi Monica

1
Một giải pháp khác với Apache Utils không bao giờ được đề cập nhưng có thể đọc được, sử dụng các phiên bản Java trước: MapUtils.put ALL (HashMap mới <String, String> (), Object mới [] {"Khóa của tôi", "giá trị của tôi", ...
Rolintocour

4

tl; dr

Sử dụng Map.of…các phương thức trong Java 9 trở lên.

Map< String , String > animalSounds =
    Map.of(
        "dog"  , "bark" ,   // key , value
        "cat"  , "meow" ,   // key , value
        "bird" , "chirp"    // key , value
    )
;

Map.of

Java 9 đã thêm một loạt các Map.ofphương thức tĩnh để làm những gì bạn muốn: Khởi tạo một bất biến Mapbằng cách sử dụng cú pháp theo nghĩa đen .

Bản đồ (một bộ sưu tập các mục) là bất biến, vì vậy bạn không thể thêm hoặc xóa các mục sau khi khởi tạo. Ngoài ra, khóa và giá trị của mỗi mục là bất biến, không thể thay đổi. Xem Javadoc để biết các quy tắc khác, chẳng hạn như không cho phép NULL, không cho phép các khóa trùng lặp và thứ tự lặp của ánh xạ là tùy ý.

Chúng ta hãy xem xét các phương pháp này, sử dụng một số dữ liệu mẫu cho bản đồ ngày trong tuần cho một người mà chúng ta mong đợi sẽ làm việc vào ngày đó.

Person alice = new Person( "Alice" );
Person bob = new Person( "Bob" );
Person carol = new Person( "Carol" );

Map.of()

Map.oftạo ra một sản phẩm nào Map. Không thể thay đổi, vì vậy bạn không thể thêm các mục. Dưới đây là một ví dụ về bản đồ như vậy, trống rỗng không có mục.

Map < DayOfWeek, Person > dailyWorkerEmpty = Map.of();

DailyWorkerEmpty.toString (): {}

Map.of( … )

Map.of( k , v , k , v , …)là một số phương thức lấy 1 đến 10 cặp khóa-giá trị. Dưới đây là một ví dụ về hai mục.

Map < DayOfWeek, Person > weekendWorker = 
        Map.of( 
            DayOfWeek.SATURDAY , alice ,     // key , value
            DayOfWeek.SUNDAY , bob           // key , value
        )
;

cuối tuầnWorker.toString (): {SUNDAY = Person {name = 'Bob'}, SATURDAY = Person {name = 'Alice'}}

Map.ofEntries( … )

Map.ofEntries( Map.Entry , … )mất bất kỳ số lượng đối tượng thực hiện Map.Entrygiao diện. Java gói hai lớp thực hiện giao diện đó, một lớp có thể thay đổi, lớp kia không thay đổi: AbstractMap.SimpleEntry, AbstractMap.SimpleImmutableEntry. Nhưng chúng ta không cần chỉ định một lớp cụ thể. Chúng ta chỉ cần gọi Map.entry( k , v )phương thức, truyền khóa và giá trị của chúng ta và chúng ta lấy lại một đối tượng của một Map.Entrygiao diện triển khai lớp nào đó .

Map < DayOfWeek, Person > weekdayWorker = Map.ofEntries(
        Map.entry( DayOfWeek.MONDAY , alice ) ,            // Call to `Map.entry` method returns an object implementing `Map.Entry`. 
        Map.entry( DayOfWeek.TUESDAY , bob ) ,
        Map.entry( DayOfWeek.WEDNESDAY , bob ) ,
        Map.entry( DayOfWeek.THURSDAY , carol ) ,
        Map.entry( DayOfWeek.FRIDAY , carol )
);

weekdayWorker.toString (): {WEDNESDAY = Person {name = 'Bob'}, TUESDAY = Person {name = 'Bob'}, THURSDAY = Person {name = 'Carol'}, FRIDAY = Person {name = 'Carol'} , MONDAY = Người {name = 'Alice'}}

Map.copyOf

Java 10 đã thêm phương thức Map.copyOf. Vượt qua một bản đồ hiện có, lấy lại một bản sao bất biến của bản đồ đó.

Ghi chú

Chú ý rằng thứ tự iterator của bản đồ được sản xuất thông qua Map.ofkhông đảm bảo. Các mục có một thứ tự tùy ý. Không viết mã dựa trên đơn hàng đã xem, vì tài liệu cảnh báo đơn hàng có thể thay đổi.

Lưu ý rằng tất cả các Map.of…phương pháp trả về một Mapcủa một lớp học không xác định . Lớp cụ thể bên dưới thậm chí có thể thay đổi từ một phiên bản Java sang một phiên bản khác. Tính ẩn danh này cho phép Java chọn từ các triển khai khác nhau, bất cứ điều gì tối ưu phù hợp với dữ liệu cụ thể của bạn. Ví dụ: nếu các khóa của bạn đến từ enum , Java có thể sử dụng khóa EnumMapbên dưới.


1

Bạn có thể Map.ofdễ dàng tạo phương thức của riêng mình (vốn chỉ khả dụng trong Java 9 trở lên) theo 2 cách dễ dàng

Làm cho nó với một lượng tham số đã đặt

Thí dụ

public <K,V> Map<K,V> mapOf(K k1, V v1, K k2, V v2 /* perhaps more parameters */) {
    return new HashMap<K, V>() {{
      put(k1, v1);
      put(k2,  v2);
      // etc...
    }};
}

Làm cho nó bằng cách sử dụng một danh sách

Bạn cũng có thể thực hiện điều này bằng cách sử dụng một danh sách, thay vì tạo ra nhiều phương thức cho một bộ tham số nhất định.

Thí dụ

public <K, V> Map<K, V> mapOf(List<K> keys, List<V> values) {
   if(keys.size() != values.size()) {
        throw new IndexOutOfBoundsException("amount of keys and values is not equal");
    }

    return new HashMap<K, V>() {{
        IntStream.range(0, keys.size()).forEach(index -> put(keys.get(index), values.get(index)));
    }};
}

Lưu ý Không nên sử dụng điều này cho mọi thứ vì điều này tạo ra một lớp ẩn danh mỗi khi bạn sử dụng nó.


1

JAVA 8

Trong java 8 đơn giản, bạn cũng có khả năng sử dụng Streams/Collectorsđể thực hiện công việc.

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", "value2"),
         new SimpleEntry<>("key3", "value3"))
        .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue));

Điều này có lợi thế là không tạo ra một lớp Ẩn danh.

Lưu ý rằng hàng nhập khẩu là:

import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;

Tất nhiên, như đã lưu ý trong các câu trả lời khác, trong java 9 trở đi, bạn có những cách đơn giản hơn để làm tương tự.


0

Thật không may, sử dụng varargs nếu loại khóa và giá trị không giống nhau là không hợp lý vì bạn phải sử dụng Object...và mất hoàn toàn an toàn loại. Nếu bạn luôn muốn tạo ví dụ a Map<String, String>, tất nhiên toMap(String... args)là có thể, nhưng không đẹp lắm vì sẽ dễ trộn lẫn các khóa và giá trị, và một số lượng đối số lẻ sẽ không hợp lệ.

Bạn có thể tạo một lớp con của HashMap có phương thức chuỗi

public class ChainableMap<K, V> extends HashMap<K, V> {
  public ChainableMap<K, V> set(K k, V v) {
    put(k, v);
    return this;
  }
}

và sử dụng nó như new ChainableMap<String, Object>().set("a", 1).set("b", "foo")

Một cách tiếp cận khác là sử dụng mẫu xây dựng chung:

public class MapBuilder<K, V> {
  private Map<K, V> mMap = new HashMap<>();

  public MapBuilder<K, V> put(K k, V v) {
    mMap.put(k, v);
    return this;
  }

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

và sử dụng nó như new MapBuilder<String, Object>().put("a", 1).put("b", "foo").build();

Tuy nhiên, giải pháp tôi đã sử dụng bây giờ và sau đó sử dụng varargs và Pairlớp:

public class Maps {
  public static <K, V> Map<K, V> of(Pair<K, V>... pairs) {
    Map<K, V> = new HashMap<>();

    for (Pair<K, V> pair : pairs) {
      map.put(pair.first, pair.second);
    }

    return map;
  }
}

Map<String, Object> map = Maps.of(Pair.create("a", 1), Pair.create("b", "foo");

Độ dài của Pair.create()làm phiền tôi một chút, nhưng điều này hoạt động khá tốt. Nếu bạn không nhớ nhập khẩu tĩnh, tất nhiên bạn có thể tạo một trình trợ giúp:

public <K, V> Pair<K, V> p(K k, V v) {
  return Pair.create(k, v);
}

Map<String, Object> map = Maps.of(p("a", 1), p("b", "foo");

(Thay vì Pairngười ta có thể tưởng tượng bằng cách sử dụng Map.Entry, nhưng vì nó là một giao diện nên nó yêu cầu một lớp triển khai và / hoặc một phương thức nhà máy trợ giúp. Nó cũng không bất biến và chứa logic khác không hữu ích cho nhiệm vụ này.)


0

Bạn có thể sử dụng Luồng trong Java 8 (đây là exmaple của Set):

@Test
public void whenInitializeUnmodifiableSetWithDoubleBrace_containsElements() {
    Set<String> countries = Stream.of("India", "USSR", "USA")
      .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

    assertTrue(countries.contains("India"));
}

Tham chiếu: https://www.baeldung.com/java-double-brace-initialization


0

Nếu bạn chỉ cần đặt một cặp khóa-giá trị, bạn có thể sử dụng Collections.singletonMap (khóa, giá trị);


1
mã định dạng làm cho bài đăng dễ đọc hơn nhiều
Renato
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.