Làm cách nào tôi có thể khởi tạo Bản đồ tĩnh?


1131

Làm thế nào bạn sẽ khởi tạo một tĩnh Maptrong Java?

Phương thức một: trình khởi tạo tĩnh
Phương thức hai: trình khởi tạo cá thể (lớp con ẩn danh) hoặc một số phương thức khác?

Những ưu và nhược điểm của mỗi là gì?

Dưới đây là một ví dụ minh họa hai phương pháp:

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

2
Để khởi tạo bản đồ trong Java 8: stackoverflow.com/a/37384773/1216775
akhil_mittal

2
Xin vui lòng, không bao giờ sử dụng khởi tạo cú đúp - đó là một hack và một cách dễ dàng để rò rỉ bộ nhớ và gây ra các vấn đề khác.
dimo414

Java 9? Nếu các mục nhập được tính <= 10 sử dụng Map.ofkhác Map.ofEntries, hãy kiểm tra stackoverflow.com/a/37384773/1216775
akhil_mittal

Câu trả lời:


1106

Trình khởi tạo cá thể chỉ là đường cú pháp trong trường hợp này, phải không? Tôi không thấy lý do tại sao bạn cần một lớp ẩn danh bổ sung chỉ để khởi tạo. Và nó sẽ không hoạt động nếu lớp được tạo là cuối cùng.

Bạn cũng có thể tạo bản đồ bất biến bằng cách sử dụng trình khởi tạo tĩnh:

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

10
Đây là thành ngữ tôi đã sử dụng trong nhiều năm và tôi chưa bao giờ có ai để mắt đến nó. Tôi cũng làm như vậy đối với các Bộ và Danh sách không đổi có thể thay đổi.
jasonmp85

3
Làm cách nào để tôi xử lý HashMap <String, String> bằng khóa String. Đối tượng Map không cho phép tôi có khóa String vì vậy tôi không thể sử dụng unmodifiableMap (). Tôi đoán việc truyền tới HashMap cũng sẽ đánh bại mục đích. Có ý kiến ​​gì không?
Lu-ca

30
@Luke Tôi thực sự nghi ngờ rằng Android có giới hạn như vậy. Nó làm cho không có ý nghĩa gì cả. Một tìm kiếm nhanh đã tìm thấy câu hỏi này ở đây (và nhiều câu hỏi khác) dường như ngụ ý rằng bạn có thể sử dụng khóa Chuỗi cho đối tượng Bản đồ trong Android.
mluisbrown

11
Vì vậy, không ai khác bận tâm điều tra, tôi có thể xác nhận không có vấn đề gì với việc sử dụng khóa Chuỗi cho đối tượng Bản đồ trên Android.
Jordan

11
Jordan: bây giờ là một chủ đề cũ nhưng tôi nghi ngờ @Luke đang cố sử dụng một chuỗi làm khóa trong bản đồ có loại khóa khác, ví dụ: Map <Integer, String>.
Biến khổ sở

445

Tôi thích cách ổi khởi tạo một bản đồ tĩnh, bất biến:

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

Như bạn có thể thấy, nó rất súc tích (vì các phương pháp nhà máy thuận tiện trong ImmutableMap).

Nếu bạn muốn bản đồ có nhiều hơn 5 mục, bạn không thể sử dụng nữa ImmutableMap.of(). Thay vào đó, hãy thử ImmutableMap.builder()dọc theo những dòng này:

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

Để tìm hiểu thêm về lợi ích của các tiện ích bộ sưu tập bất biến của Guava, hãy xem Bộ sưu tập không thay đổi được giải thích trong Hướng dẫn sử dụng ổi .

(Một tập hợp con) Quả ổi từng được gọi là Bộ sưu tập của Google . Nếu bạn không sử dụng thư viện này trong dự án Java của bạn nêu ra, tôi mạnh mẽ khuyên cố gắng nó ra! Quả ổi đã nhanh chóng trở thành một trong những libs bên thứ 3 miễn phí phổ biến và hữu ích nhất cho Java, khi những người dùng SO đồng ý . (Nếu bạn chưa quen với nó, có một số tài nguyên học tập tuyệt vời đằng sau liên kết đó.)


Cập nhật (2015) : Đối với Java 8 , tốt, tôi vẫn sẽ sử dụng cách tiếp cận Guava vì nó sạch hơn bất kỳ thứ gì khác. Nếu bạn không muốn phụ thuộc vào ổi, hãy xem xét một phương pháp init cũ đơn giản . Việc hack với mảng hai chiều và API Stream khá xấu nếu bạn hỏi tôi và trở nên xấu hơn nếu bạn cần tạo một Bản đồ có các khóa và giá trị không cùng loại (như Map<Integer, String>trong câu hỏi).

Về tương lai của Guava nói chung, liên quan đến Java 8, Louis Wasserman cho biết điều này trở lại vào năm 2014 và [ cập nhật ] vào năm 2016 đã thông báo rằng Guava 21 sẽ yêu cầu và hỗ trợ đúng cách cho Java 8 .


Cập nhật (2016) : Như Tagir Valeev đã chỉ ra , Java 9 cuối cùng sẽ làm cho điều này trở nên rõ ràng để không sử dụng gì ngoài JDK thuần túy, bằng cách thêm các phương thức nhà máy tiện lợi cho các bộ sưu tập:

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

21
Có vẻ như các quản trị viên SO đồng nghiệp của chúng tôi đã xóa câu hỏi "Thư viện Java bên thứ ba miễn phí hữu ích nhất" mà tôi đã liên kết. :( Chết tiệt họ.
Jonik

2
Tôi đồng ý, đây là cách tốt nhất để khởi tạo một bản đồ không đổi. Không chỉ dễ đọc hơn mà còn vì Collections.unmodifiableMap trả về chế độ xem chỉ đọc của bản đồ bên dưới (vẫn có thể được sửa đổi).
crunchdog

11
Bây giờ tôi có thể thấy các câu hỏi đã bị xóa (với 10k + rep), vì vậy đây là bản sao của 'Thư viện Java bên thứ ba miễn phí hữu ích nhất' . Đây chỉ là trang đầu tiên, nhưng ít nhất bạn có thể tìm thấy các tài nguyên ổi được đề cập ở trên.
Jonik

2
Tôi thực sự thích cách tiếp cận này, mặc dù nó có ích để biết cách thực hiện mà không cần phụ thuộc thêm.
Cờ lê

2
JEP 186 vẫn chưa đóng cửa, vì vậy nó có thể giới thiệu các tính năng mới liên quan đến bộ sưu tập chữ
cybersoft

182

Tôi sẽ dùng:

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. nó tránh một lớp ẩn danh, mà cá nhân tôi coi là một phong cách xấu, và tránh
  2. nó làm cho việc tạo bản đồ rõ ràng hơn
  3. nó làm cho bản đồ không thể thay đổi
  4. vì MY_MAP là hằng số, tôi sẽ đặt tên nó là hằng số

3
Trong số các tùy chọn JDK thuần túy (không có lib), tôi thích điều này nhất, bởi vì định nghĩa bản đồ được liên kết rõ ràng với khởi tạo của nó. Cũng đồng ý về việc đặt tên liên tục.
Jonik

Nó chưa bao giờ xảy ra với tôi rằng bạn có thể làm điều này.
romulusnr

181

Java 5 cung cấp cú pháp nhỏ gọn hơn này:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};

46
Kỹ thuật đó được gọi là khởi tạo cú đúp kép: stackoverflow.com/questions/1372113/ Khăn Đây không phải là cú pháp Java 5 đặc biệt, nó chỉ là một mẹo với một lớp ẩn danh với trình khởi tạo cá thể.
Jesper

13
Câu hỏi nhanh liên quan đến khởi tạo cú đúp: Khi thực hiện điều này, Eclipse đưa ra Cảnh báo về một ID nối tiếp bị thiếu. Một mặt, tôi không thấy lý do tại sao cần có ID sê-ri trong trường hợp cụ thể này, nhưng mặt khác, tôi thường không thích cảnh báo áp chế. Suy nghĩ của bạn về điều này là gì?
nbarraille

8
@nbarraille Đó là vì HashMap implements Serializable. Vì bạn thực sự tạo một lớp con của HashMap bằng cách sử dụng "mẹo" này, nên bạn hoàn toàn tạo ra một lớp Nối tiếp. Và để làm điều này, bạn nên cung cấp một serialUID.
không có ai vào

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 - Việc sử dụng được đề xuất một bối cảnh tĩnh. Hiệu suất có thể kém hơn, nhưng không đáng chú ý vì vậy khi xử lý một số lượng nhỏ các bản đồ được xác định tĩnh. HashMap.equalsđược định nghĩa AbstractMapvà hoạt động trên bất kỳ lớp con nào của Bản đồ, vì vậy đó không phải là vấn đề đáng lo ngại ở đây. Điều điều hành kim cương là gây phiền nhiễu, nhưng như đã đề cập hiện đã được giải quyết.
Jules

95

Một lợi thế của phương pháp thứ hai là bạn có thể gói nó Collections.unmodifiableMap()để đảm bảo rằng không có gì sẽ cập nhật bộ sưu tập sau:

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!

3
Bạn có thể dễ dàng làm điều này trong phương thức đầu tiên bằng cách di chuyển toán tử mới vào khối {} tĩnh và gói nó không?
Patrick

2
Tôi sẽ chuyển cuộc gọi constructor vào tĩnh được khởi tạo. Bất cứ điều gì khác chỉ trông kỳ lạ.
Tom Hawtin - tackline

2
bất kỳ ý tưởng nào hiệu suất đạt được có thể có từ việc sử dụng một lớp ẩn danh trái ngược với một lớp cụ thể?
Kip

62

Đây là trình khởi tạo bản đồ tĩnh một dòng 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]));

Chỉnh sửa: để khởi tạo Map<Integer, String>như trong câu hỏi, bạn cần một cái gì đó như thế này:

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

Chỉnh sửa (2): Có một phiên bản tốt hơn, có khả năng hỗn hợp của i_am_zero sử dụng luồng new SimpleEntry<>(k, v)cuộc gọi. Kiểm tra câu trả lời đó: https://stackoverflow.com/a/37384773/3950982


7
Tôi đã tự do thêm một phiên bản tương đương với câu hỏi và các câu trả lời khác: khởi tạo một Bản đồ có các khóa và giá trị thuộc loại khác nhau (vì vậy String[][]sẽ không cần, Object[][]là cần thiết). IMHO, cách tiếp cận này là xấu xí (thậm chí nhiều hơn với các diễn viên) và khó nhớ; sẽ không sử dụng nó cho mình.
Jonik

57

Map.of trong Java 9+

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

Xem JEP 269 để biết chi tiết. JDK 9 đạt mức khả dụng chung vào tháng 9 năm 2017.


7
Hoặc nếu bạn muốn có hơn 10 cặp khóa-giá trị, bạn có thể sử dụngMap.ofEntries
ZhekaKozlov

8
Điều này là sạch sẽ và tất cả, cho đến khi bạn nhận ra nó đã được thực hiện như thế nào
giữa

Thật đáng buồn - có vẻ như nó chỉ hỗ trợ 10 mục, sau đó bạn cần sử dụng ofEntries. Què.
Somaiah Kumbera

2
Sự sạch sẽ trong triển khai trong JDK không quan trọng miễn là nó hoạt động và thỏa mãn hợp đồng. Giống như bất kỳ hộp đen nào, chi tiết triển khai luôn có thể được sửa trong tương lai nếu thực sự cần thiết ...
vikingsteve

@mid Đó là cách an toàn duy nhất để làm điều này trong Java.
Luke Hutchison

44

Java 9

Chúng tôi có thể sử dụng Map.ofEntries, gọi Map.entry( k , v )để tạo từng mục.

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

Chúng tôi cũng có thể sử dụng Map.oftheo đề xuất của Tagir trong câu trả lời của mình tại đây nhưng chúng tôi không thể có hơn 10 mục sử dụngMap.of .

Java 8 (Giải pháp gọn gàng)

Chúng tôi có thể tạo một luồng các mục bản đồ. Chúng tôi đã có hai triển khai Entrytrong java.util.AbstractMapđó là SimpleEntrySimpleImmutableEntry . Trong ví dụ này, chúng ta có thể sử dụng trước đây như:

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

2
Các new SimpleEntry<>()cách là ít có thể đọc được hơn tĩnh put(): /
Danon

32

Với Bộ sưu tập Eclipse , tất cả những điều sau đây sẽ hoạt động:

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

Bạn cũng có thể khởi tạo tĩnh các bản đồ nguyên thủy với Bộ sưu tập Eclipse.

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

Lưu ý: Tôi là người đi làm cho Bộ sưu tập Eclipse


1
Tôi thực sự muốn Bộ sưu tập Eclipse là thư viện bộ sưu tập mặc định cho Java. Tôi thích nó nhiều hơn Guava + JCL.
Kenny Cason

29

Tôi sẽ không bao giờ tạo một lớp con ẩn danh trong tình huống này. Công cụ khởi tạo tĩnh hoạt động tốt như nhau, nếu bạn muốn làm cho bản đồ không thể thay đổi được, ví dụ:

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

1
Trong trường hợp nào bạn sẽ sử dụng một lớp con ẩn danh để khởi tạo một hashmap sau đó?
dogbane

6
Không bao giờ khởi tạo Bộ sưu tập.
eljenso

Bạn có thể giải thích tại sao sử dụng bộ khởi tạo tĩnh là lựa chọn tốt hơn so với việc tạo một lớp con ẩn danh?
leba-lev

3
@rookie Có một số lý do được đưa ra trong các câu trả lời khác có lợi cho init tĩnh. Mục tiêu ở đây để khởi tạo, vậy tại sao lại đưa vào phân lớp, ngoại trừ có thể để lưu một vài tổ hợp phím? .
eljenso

@eljenso - lý do tôi thường ủng hộ cú pháp lớp con cho việc này là vì nó đặt nội tuyến khởi tạo, nơi nó thuộc về . Lựa chọn tốt thứ hai là gọi một phương thức tĩnh trả về bản đồ đã khởi tạo. Nhưng tôi e rằng tôi đã xem mã của bạn và phải dành vài giây để tìm ra MY_MAP đến từ đâu và đó là thời gian mà tôi không muốn phải lãng phí. Bất kỳ cải thiện nào về khả năng đọc là một phần thưởng và hậu quả hiệu suất là tối thiểu, vì vậy nó có vẻ như là lựa chọn tốt nhất với tôi.
Jules

18

Có lẽ thật thú vị khi xem Bộ sưu tập của Google , ví dụ: video mà họ có trên trang của họ. Họ cung cấp nhiều cách khác nhau để khởi tạo bản đồ và bộ, đồng thời cung cấp các bộ sưu tập bất biến.

Cập nhật: Thư viện này hiện có tên là Guava .


17

Tôi thích lớp ẩn danh, vì nó dễ đối phó với nó:

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

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

Nếu chúng ta khai báo nhiều hơn một hằng số thì mã đó sẽ được viết trong khối tĩnh và điều đó khó duy trì trong tương lai. Vì vậy, tốt hơn là sử dụng lớp ẩn danh.

public class Test {

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

Và nó được đề xuất sử dụng unmodifiableMap cho các hằng số khác, nó không thể được coi là hằng số.


10

Tôi mạnh mẽ có thể đề xuất kiểu "khởi tạo cú đúp" so với kiểu khối tĩnh.

Ai đó có thể nhận xét rằng họ không thích lớp ẩn danh, chi phí chung, hiệu suất, v.v.

Nhưng điều mà tôi xem xét nhiều hơn là khả năng đọc và bảo trì mã. Theo quan điểm này, tôi đứng một cú đúp là một kiểu mã tốt hơn là phương thức tĩnh.

  1. Các yếu tố được lồng và nội tuyến.
  2. Nó là OO nhiều hơn, không phải là thủ tục.
  3. tác động hiệu suất là rất nhỏ và có thể bị bỏ qua.
  4. Hỗ trợ phác thảo IDE tốt hơn (thay vào đó là nhiều khối {} tĩnh ẩn danh)
  5. Bạn đã lưu vài dòng bình luận để mang lại cho họ mối quan hệ.
  6. Ngăn chặn rò rỉ phần tử / phần tử dẫn của đối tượng chưa được khởi tạo từ ngoại lệ và trình tối ưu hóa mã byte.
  7. Không phải lo lắng về thứ tự thực hiện của khối tĩnh.

Ngoài ra, bạn biết được GC của lớp ẩn danh, bạn luôn có thể chuyển đổi nó thành HashMap bình thường bằng cách sử dụng new HashMap(Map map).

Bạn có thể làm điều này cho đến khi bạn phải đối mặt với một vấn đề khác. Nếu bạn làm như vậy, bạn nên sử dụng hoàn thành một kiểu mã hóa khác (ví dụ: không có lớp tĩnh, lớp nhà máy) cho nó.


8

Như apache-commons thông thường có phương thức thích hợp MapUtils.put ALL (Map, Object []) :

Ví dụ: để tạo bản đồ màu:

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });

Tôi bao gồm Apache Commons trong tất cả các bản dựng vì vậy, trong trường hợp không có phương thức Arrays.asMap( ... )trong Java đơn giản, tôi nghĩ đây là giải pháp tốt nhất. Phát minh lại bánh xe thường là ngớ ngẩn. Nhược điểm rất nhẹ là với thuốc generic, nó sẽ cần một chuyển đổi không được kiểm soát.
mike gặm nhấm

Phiên bản @mikerodent 4.1 là chung: Bản đồ tĩnh <K, V> Bản đồ <K, V> put ALL (bản đồ cuối cùng <K, V>, bản đồ đối tượng [])
agad

Tx ... vâng, tôi đang sử dụng 4.1 nhưng tôi vẫn phải SuppressWarnings( unchecked )vào Eclipse với một dòng nhưMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
loài gặm nhấm

@mikerodent không phải vì Object [] [] ? Xem thông tin cập nhật - Tôi không có bất kỳ cảnh báo nào trong Eclipse.
agad

Thật kỳ lạ ... ngay cả khi tôi đi String[][]tôi cũng nhận được "lời cảnh báo"! Và tất nhiên điều đó chỉ hoạt động nếu bạn KVcùng lớp. Tôi hiểu rằng bạn chưa (có thể hiểu được) đặt "chuyển đổi không được kiểm tra" thành "Bỏ qua" trong thiết lập Eclipse của bạn?
mike gặm nhấm

7

Đây là sở thích của tôi khi tôi không muốn (hoặc không thể) sử dụng Guava ImmutableMap.of()hoặc nếu tôi cần một người có thể thay đổi 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]);
        }
    }};
}

Nó rất nhỏ gọn và nó bỏ qua các giá trị đi lạc (tức là khóa cuối cùng không có giá trị).

Sử dụng:

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

7

Nếu bạn muốn bản đồ không thể thay đổi, cuối cùng java 9 đã thêm một phương thức xuất xưởng thú vị ofvào Mapgiao diện. Phương pháp tương tự được thêm vào Set, List.

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


6

Tôi thích sử dụng trình khởi tạo tĩnh để tránh tạo các lớp ẩn danh (sẽ không có mục đích nào nữa), vì vậy tôi sẽ liệt kê các mẹo khởi tạo với trình khởi tạo tĩnh. Tất cả các giải pháp / lời khuyên được liệt kê là loại an toàn.

Lưu ý: Câu hỏi không nói bất cứ điều gì về việc làm cho bản đồ không thể thay đổi, vì vậy tôi sẽ bỏ qua nó, nhưng biết rằng nó có thể dễ dàng thực hiện được Collections.unmodifiableMap(map).

Mẹo đầu tiên

Mẹo đầu tiên là bạn có thể tạo một tham chiếu cục bộ cho bản đồ và bạn đặt cho nó một tên NGẮN:

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

Mẹo thứ hai

Mẹo thứ 2 là bạn có thể tạo một phương thức trợ giúp để thêm các mục; bạn cũng có thể công khai phương thức trợ giúp này nếu bạn muốn:

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

Phương thức trợ giúp ở đây không thể sử dụng lại được vì nó chỉ có thể thêm các phần tử vào myMap2. Để làm cho nó có thể sử dụng lại, chúng ta có thể biến bản đồ thành một tham số của phương thức trợ giúp, nhưng sau đó mã khởi tạo sẽ không ngắn hơn nữa.

Mẹo thứ ba

Mẹo thứ 3 là bạn có thể tạo một lớp trình trợ giúp giống như trình xây dựng có thể sử dụng lại với chức năng điền. Đây thực sự là một lớp trợ giúp 10 dòng đơn giản, an toàn cho loại:

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

5

Lớp ẩn danh bạn đang tạo hoạt động tốt. Tuy nhiên, bạn nên biết rằng đây là một lớp bên trong và do đó, nó sẽ chứa một tham chiếu đến thể hiện của lớp xung quanh. Vì vậy, bạn sẽ thấy bạn không thể làm một số điều nhất định với nó (sử dụng XStream cho một cái). Bạn sẽ nhận được một số lỗi rất lạ.

Phải nói rằng, miễn là bạn biết thì cách tiếp cận này là tốt. Tôi sử dụng nó hầu hết thời gian để khởi tạo tất cả các loại bộ sưu tập một cách súc tích.

EDIT: Đã chỉ ra một cách chính xác trong các ý kiến ​​rằng đây là một lớp tĩnh. Rõ ràng là tôi đã không đọc nó đủ chặt chẽ. Tuy nhiên ý kiến của tôi làm vẫn áp dụng đối với các lớp bên trong vô danh.


3
Trong trường hợp cụ thể này, nó là tĩnh, vì vậy không có trường hợp bên ngoài.
Tom Hawtin - tackline

Có thể cho rằng XStream không nên cố gắng tuần tự hóa những thứ như thế này (nó là tĩnh. Tại sao bạn cần phải tuần tự hóa một biến tĩnh?)
jasonmp85

5

Nếu bạn muốn một cái gì đó ngắn gọn và tương đối an toàn, bạn có thể chuyển dịch kiểm tra kiểu biên dịch sang thời gian chạy:

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

Việc thực hiện này sẽ bắt được bất kỳ lỗi nào:

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

4

Với Java 8 tôi đã sử dụng mẫu sau:

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

Nó không phải là đường vòng ngắn nhất và ngắn nhất, nhưng

  • nó không yêu cầu bất cứ điều gì bên ngoài java.util
  • nó an toàn và dễ dàng cung cấp các loại khác nhau cho khóa và giá trị.

nếu cần, người ta có thể sử dụng toMapchữ ký bao gồm nhà cung cấp bản đồ để chỉ định loại bản đồ.
zrvan


4

Bạn có thể sử dụng StickyMapMapEntrytừ Cactoos :

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

4

Cách tiếp cận thứ hai của bạn (khởi tạo Double Brace) được cho là kiểu chống , vì vậy tôi sẽ chọn cách tiếp cận đầu tiên.

Một cách dễ dàng khác để khởi tạo Bản đồ tĩnh là sử dụng chức năng tiện ích này:

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

Lưu ý: trong Java 9bạn có thể sử dụng Map.of


3

Tôi không thích cú pháp khởi tạo tĩnh và tôi không bị thuyết phục với các lớp con ẩn danh. Nói chung, tôi đồng ý với tất cả các khuyết điểm của việc sử dụng Trình khởi tạo tĩnh và tất cả các khuyết điểm của việc sử dụng các lớp con ẩn danh đã được đề cập trong các câu trả lời trước. Mặt khác - ưu điểm được trình bày trong các bài viết này là không đủ cho tôi. Tôi thích sử dụng phương thức khởi tạo tĩnh:

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

3

Tôi chưa thấy cách tiếp cận tôi sử dụng (và đã trở nên thích) được đăng trong bất kỳ câu trả lời nào, vì vậy đây là:

Tôi không thích sử dụng các trình khởi tạo tĩnh bởi vì chúng rất cồng kềnh và tôi không thích các lớp ẩn danh vì nó đang tạo một lớp mới cho mỗi trường hợp.

thay vào đó, tôi thích khởi tạo giống như thế này:

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

thật không may, các phương thức này không phải là một phần của thư viện Java tiêu chuẩn, vì vậy bạn sẽ cần tạo (hoặc sử dụng) một thư viện tiện ích xác định các phương thức sau:

 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)

(bạn có thể sử dụng 'nhập tĩnh' để tránh cần phải thêm tiền tố vào tên của phương thức)

Tôi thấy hữu ích khi cung cấp các phương thức tĩnh tương tự cho các bộ sưu tập khác (list, set, sortset, sortMap, v.v.)

Nó không hoàn toàn đẹp như khởi tạo đối tượng json, nhưng đó là một bước theo hướng đó, khi có liên quan đến khả năng đọc.


3

Bởi vì Java không hỗ trợ bằng chữ theo bản đồ, các thể hiện bản đồ phải luôn được khởi tạo và điền rõ ràng.

May mắn thay, có thể tính gần đúng hành vi của các chữ bản đồ trong Java bằng các phương thức xuất xưởng .

Ví dụ:

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

Đầu ra:

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

Nó thuận tiện hơn rất nhiều so với việc tạo và điền vào bản đồ một yếu tố tại một thời điểm.


2

JEP 269 cung cấp một số phương thức nhà máy tiện lợi cho Bộ sưu tập API. Các phương thức xuất xưởng này không có trong phiên bản Java hiện tại, là 8, nhưng được lên kế hoạch phát hành Java 9.

Mapcó hai phương pháp nhà máy: ofofEntries. Sử dụng of, bạn có thể chuyển các cặp khóa / giá trị xen kẽ. Ví dụ: để tạo một lượt Mapthích {age: 27, major: cs}:

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

Hiện tại có mười phiên bản quá tải cho of, vì vậy bạn có thể tạo bản đồ chứa mười cặp khóa / giá trị. Nếu bạn không thích giới hạn này hoặc khóa / giá trị xen kẽ, bạn có thể sử dụng ofEntries:

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

Cả hai ofofEntriessẽ trả lại một bất biến Map, vì vậy bạn không thể thay đổi các yếu tố của chúng sau khi xây dựng. Bạn có thể thử các tính năng này bằng cách sử dụng JDK 9 Early Access .


2

Chà ... tôi thích 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 ) );
}

2

Tôi đã đọc câu trả lời và tôi quyết định viết công cụ xây dựng bản đồ của riêng mình. Hãy sao chép-dán và thưởng thức.

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: Gần đây, tôi tiếp tục tìm phương pháp tĩnh công khai of khá thường xuyên và tôi khá thích nó. Tôi đã thêm nó vào mã và làm cho hàm tạo riêng tư, do đó chuyển sang mẫu phương thức tĩnh của nhà máy.

EDIT2: Thậm chí gần đây, tôi không còn thích phương thức tĩnh được gọi nữa of, vì nó trông khá tệ khi sử dụng nhập tĩnh. Tôi đã đổi tên nó thành mapOfthay thế, làm cho nó phù hợp hơn cho nhập khẩu tĩnh.

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.