Nếu tôi có một đối tượng thực hiện Map
giao diện trong Java và tôi muốn lặp lại mọi cặp có trong nó, cách hiệu quả nhất để đi qua bản đồ là gì?
Việc sắp xếp các phần tử có phụ thuộc vào việc triển khai bản đồ cụ thể mà tôi có cho giao diện không?
Nếu tôi có một đối tượng thực hiện Map
giao diện trong Java và tôi muốn lặp lại mọi cặp có trong nó, cách hiệu quả nhất để đi qua bản đồ là gì?
Việc sắp xếp các phần tử có phụ thuộc vào việc triển khai bản đồ cụ thể mà tôi có cho giao diện không?
Câu trả lời:
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
remove
phương thức của nó . Nếu đó là trường hợp, câu trả lời khác này cho bạn thấy làm thế nào để làm điều đó. Mặt khác, vòng lặp nâng cao như trong câu trả lời ở trên là cách để đi.
map.values()
hoặc map.keySet()
nếu bạn chỉ muốn lặp qua các giá trị hoặc khóa.
Để tóm tắt các câu trả lời khác và kết hợp chúng với những gì tôi biết, tôi đã tìm thấy 10 cách chính để làm điều này (xem bên dưới). Ngoài ra, tôi đã viết một số bài kiểm tra hiệu suất (xem kết quả bên dưới). Ví dụ: nếu chúng ta muốn tìm tổng của tất cả các khóa và giá trị của bản đồ, chúng ta có thể viết:
Sử dụng iterator và Map.Entry
long i = 0;
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> pair = it.next();
i += pair.getKey() + pair.getValue();
}
Sử dụng foreach và Map.Entry
long i = 0;
for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
i += pair.getKey() + pair.getValue();
}
Sử dụng forEach từ Java 8
final long[] i = {0};
map.forEach((k, v) -> i[0] += k + v);
Sử dụng keyset và foreach
long i = 0;
for (Integer key : map.keySet()) {
i += key + map.get(key);
}
Sử dụng keyset và iterator
long i = 0;
Iterator<Integer> itr2 = map.keySet().iterator();
while (itr2.hasNext()) {
Integer key = itr2.next();
i += key + map.get(key);
}
Sử dụng cho và Map.Entry
long i = 0;
for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
Map.Entry<Integer, Integer> entry = entries.next();
i += entry.getKey() + entry.getValue();
}
Sử dụng API luồng Java 8
final long[] i = {0};
map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
Sử dụng song song API luồng Java 8
final long[] i = {0};
map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
Sử dụng IterableMap củaApache Collections
long i = 0;
MapIterator<Integer, Integer> it = iterableMap.mapIterator();
while (it.hasNext()) {
i += it.next() + it.getValue();
}
Sử dụng các bộ sưu tập MutableMap của Eclipse (CS)
final long[] i = {0};
mutableMap.forEachKeyValue((key, value) -> {
i[0] += key + value;
});
Kiểm tra độ hoàn hảo (chế độ = AverageTime, system = Windows 8.1 64-bit, Intel i7-4790 3.60 GHz, 16 GB)
Đối với bản đồ nhỏ (100 yếu tố), điểm 0,388 là tốt nhất
Benchmark Mode Cnt Score Error Units
test3_UsingForEachAndJava8 avgt 10 0.308 ± 0.021 µs/op
test10_UsingEclipseMap avgt 10 0.309 ± 0.009 µs/op
test1_UsingWhileAndMapEntry avgt 10 0.380 ± 0.014 µs/op
test6_UsingForAndIterator avgt 10 0.387 ± 0.016 µs/op
test2_UsingForEachAndMapEntry avgt 10 0.391 ± 0.023 µs/op
test7_UsingJava8StreamApi avgt 10 0.510 ± 0.014 µs/op
test9_UsingApacheIterableMap avgt 10 0.524 ± 0.008 µs/op
test4_UsingKeySetAndForEach avgt 10 0.816 ± 0.026 µs/op
test5_UsingKeySetAndIterator avgt 10 0.863 ± 0.025 µs/op
test8_UsingJava8StreamApiParallel avgt 10 5.552 ± 0.185 µs/op
Đối với bản đồ có 10000 yếu tố, điểm 37.606 là tốt nhất
Benchmark Mode Cnt Score Error Units
test10_UsingEclipseMap avgt 10 37.606 ± 0.790 µs/op
test3_UsingForEachAndJava8 avgt 10 50.368 ± 0.887 µs/op
test6_UsingForAndIterator avgt 10 50.332 ± 0.507 µs/op
test2_UsingForEachAndMapEntry avgt 10 51.406 ± 1.032 µs/op
test1_UsingWhileAndMapEntry avgt 10 52.538 ± 2.431 µs/op
test7_UsingJava8StreamApi avgt 10 54.464 ± 0.712 µs/op
test4_UsingKeySetAndForEach avgt 10 79.016 ± 25.345 µs/op
test5_UsingKeySetAndIterator avgt 10 91.105 ± 10.220 µs/op
test8_UsingJava8StreamApiParallel avgt 10 112.511 ± 0.365 µs/op
test9_UsingApacheIterableMap avgt 10 125.714 ± 1.935 µs/op
Đối với bản đồ có 100000 yếu tố, điểm 1184.767 là tốt nhất
Benchmark Mode Cnt Score Error Units
test1_UsingWhileAndMapEntry avgt 10 1184.767 ± 332.968 µs/op
test10_UsingEclipseMap avgt 10 1191.735 ± 304.273 µs/op
test2_UsingForEachAndMapEntry avgt 10 1205.815 ± 366.043 µs/op
test6_UsingForAndIterator avgt 10 1206.873 ± 367.272 µs/op
test8_UsingJava8StreamApiParallel avgt 10 1485.895 ± 233.143 µs/op
test5_UsingKeySetAndIterator avgt 10 1540.281 ± 357.497 µs/op
test4_UsingKeySetAndForEach avgt 10 1593.342 ± 294.417 µs/op
test3_UsingForEachAndJava8 avgt 10 1666.296 ± 126.443 µs/op
test7_UsingJava8StreamApi avgt 10 1706.676 ± 436.867 µs/op
test9_UsingApacheIterableMap avgt 10 3289.866 ± 1445.564 µs/op
Đồ thị (kiểm tra hiệu suất tùy thuộc vào kích thước bản đồ)
Bảng (kiểm tra độ hoàn hảo tùy thuộc vào kích thước bản đồ)
100 600 1100 1600 2100
test10 0.333 1.631 2.752 5.937 8.024
test3 0.309 1.971 4.147 8.147 10.473
test6 0.372 2.190 4.470 8.322 10.531
test1 0.405 2.237 4.616 8.645 10.707
test2 0.376 2.267 4.809 8.403 10.910
test7 0.473 2.448 5.668 9.790 12.125
test9 0.565 2.830 5.952 13.220 16.965
test4 0.808 5.012 8.813 13.939 17.407
test5 0.810 5.104 8.533 14.064 17.422
test8 5.173 12.499 17.351 24.671 30.403
Tất cả các bài kiểm tra là trên GitHub .
long sum = 0; map.forEach( /* accumulate in variable sum*/);
chụp sum
lâu, có thể chậm hơn nói stream.mapToInt(/*whatever*/).sum
chẳng hạn. Tất nhiên bạn không thể luôn luôn tránh trạng thái bắt, nhưng đó có thể là một bổ sung hợp lý đến băng ghế dự bị.
AtomicInteger
giải quyết vấn đề.
x±e
ngụ ý rằng đã có kết quả trong khoảng thời gian từ x-e
đến x+e
, vì vậy kết quả nhanh nhất ( 1184.767±332.968
) dao động từ 852
để 1518
, trong khi chậm nhất thứ hai ( 1706.676±436.867
) chạy giữa 1270
và 2144
, vì vậy kết quả vẫn chồng chéo đáng kể. Bây giờ nhìn vào kết quả chậm nhất, 3289.866±1445.564
mà ngụ ý phân kỳ giữa 1844
và 4735
và bạn biết rằng những kết quả xét nghiệm là vô nghĩa.
while
so với for
vòng lặp không phải là một kỹ thuật khác để lặp lại. Và tôi ngạc nhiên khi họ có sự khác biệt như vậy giữa chúng trong các bài kiểm tra của bạn. Điều đó cho thấy các bài kiểm tra không được cách ly đúng cách với các yếu tố bên ngoài không liên quan đến những điều bạn dự định kiểm tra.
Trong Java 8, bạn có thể làm sạch và nhanh bằng các tính năng lambdas mới:
Map<String,String> map = new HashMap<>();
map.put("SomeKey", "SomeValue");
map.forEach( (k,v) -> [do something with key and value] );
// such as
map.forEach( (k,v) -> System.out.println("Key: " + k + ": Value: " + v));
Kiểu k
và v
sẽ được trình biên dịch suy ra và không cần sử dụngMap.Entry
nữa.
Dễ như ăn bánh!
map.entrySet().stream()
docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Có, thứ tự phụ thuộc vào việc thực hiện Bản đồ cụ thể.
@ ScArcher2 có cú pháp Java 1.5 thanh lịch hơn . Trong 1.4, tôi sẽ làm một cái gì đó như thế này:
Iterator entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
Entry thisEntry = (Entry) entries.next();
Object key = thisEntry.getKey();
Object value = thisEntry.getValue();
// ...
}
for
cấu trúc như for (Entry e : myMap.entrySet)
sẽ không cho phép bạn sửa đổi bộ sưu tập, nhưng ví dụ như @HanuAthena đã đề cập đến nó sẽ hoạt động, vì nó cung cấp cho bạn Iterator
phạm vi. (Trừ khi tôi là thiếu một cái gì đó ...)
Entry thisEntry = (Entry) entries.next();
: không nhận ra Entry
. Đó có phải là mã giả cho cái gì khác?
java.util.Map.Entry
.
Mã tiêu biểu để lặp lại trên bản đồ là:
Map<String,Thing> map = ...;
for (Map.Entry<String,Thing> entry : map.entrySet()) {
String key = entry.getKey();
Thing thing = entry.getValue();
...
}
HashMap
là việc thực hiện bản đồ chính tắc và không đảm bảo (hoặc mặc dù nó sẽ không thay đổi thứ tự nếu không có thao tác đột biến nào được thực hiện trên đó). SortedMap
sẽ trả về các mục dựa trên thứ tự tự nhiên của các phím hoặc a Comparator
, nếu được cung cấp. LinkedHashMap
sẽ trả về các mục theo thứ tự chèn hoặc thứ tự truy cập tùy thuộc vào cách nó được xây dựng. EnumMap
trả về các mục theo thứ tự tự nhiên của các phím.
(Cập nhật: Tôi nghĩ rằng điều này không còn đúng nữa. ) Lưu ý, IdentityHashMap
entrySet
iterator hiện có một triển khai đặc biệt trả về cùng một Map.Entry
thể hiện cho mọi mục trong entrySet
! Tuy nhiên, mỗi khi một trình lặp mới tiến bộ Map.Entry
sẽ được cập nhật.
LinkedHashMap
số đếm. Những qua iterator
, spliterator
, entrySet
, vv, không thay đổi thứ tự.
Ví dụ về việc sử dụng iterator và generic:
Iterator<Map.Entry<String, String>> entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, String> entry = entries.next();
String key = entry.getKey();
String value = entry.getValue();
// ...
}
Iterator
một vòng lặp for để giới hạn phạm vi của nó.
for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }
. Bằng cách sử dụng cấu trúc đó, chúng tôi giới hạn phạm vi (mức độ hiển thị của biến) entries
cho vòng lặp for.
Đây là một câu hỏi gồm hai phần:
Cách lặp lại các mục của Bản đồ - @ ScArcher2 đã trả lời một cách hoàn hảo.
Thứ tự lặp là gì - nếu bạn chỉ đang sử dụng Map
, thì nói đúng ra, không có đảm bảo đặt hàng . Vì vậy, bạn không nên thực sự dựa vào thứ tự được đưa ra bởi bất kỳ thực hiện. Tuy nhiên, SortedMap
giao diện mở rộng Map
và cung cấp chính xác những gì bạn đang tìm kiếm - việc triển khai sẽ luôn đưa ra một thứ tự sắp xếp nhất quán.
NavigableMap
là một tiện ích mở rộng hữu ích khác - đây là một SortedMap
phương pháp bổ sung để tìm các mục theo vị trí được sắp xếp của chúng trong bộ khóa. Vì vậy, khả năng này có thể loại bỏ sự cần thiết lặp lại ở nơi đầu tiên - bạn có thể có thể tìm ra cụ thể entry
bạn là sau khi sử dụng higherEntry
, lowerEntry
, ceilingEntry
, hoặc floorEntry
phương pháp. Các descendingMap
phương pháp thậm chí cung cấp cho bạn một phương pháp rõ ràng của đảo ngược thứ tự traversal .
Có một số cách để lặp lại trên bản đồ.
Dưới đây là so sánh hiệu suất của chúng cho một tập dữ liệu chung được lưu trữ trên bản đồ bằng cách lưu trữ một triệu cặp giá trị chính trong bản đồ và sẽ lặp lại trên bản đồ.
1) Sử dụng entrySet()
cho mỗi vòng lặp
for (Map.Entry<String,Integer> entry : testMap.entrySet()) {
entry.getKey();
entry.getValue();
}
50 mili giây
2) Sử dụng keySet()
cho mỗi vòng lặp
for (String key : testMap.keySet()) {
testMap.get(key);
}
76 mili giây
3) Sử dụng entrySet()
và lặp
Iterator<Map.Entry<String,Integer>> itr1 = testMap.entrySet().iterator();
while(itr1.hasNext()) {
Map.Entry<String,Integer> entry = itr1.next();
entry.getKey();
entry.getValue();
}
50 mili giây
4) Sử dụng keySet()
và lặp
Iterator itr2 = testMap.keySet().iterator();
while(itr2.hasNext()) {
String key = itr2.next();
testMap.get(key);
}
75 mili giây
Tôi đã giới thiệu this link
.
Cách chính xác để làm điều này là sử dụng câu trả lời được chấp nhận vì nó là hiệu quả nhất. Tôi thấy đoạn mã sau có vẻ sạch hơn một chút.
for (String key: map.keySet()) {
System.out.println(key + "/" + map.get(key));
}
O(1) = 2*O(1)
là khá nhiều định nghĩa của ký hiệu O lớn. Bạn đúng ở chỗ nó chạy chậm hơn một chút, nhưng về độ phức tạp thì chúng giống nhau.
2
, vì lặp đi lặp lại một cái entrySet()
không chịu một cái nhìn nào cả; nó chỉ là một đường ngang tuyến tính của tất cả các mục. Ngược lại, lặp đi lặp lại keySet()
và thực hiện tra cứu trên mỗi khóa mang một lần tra cứu cho mỗi khóa, vì vậy chúng ta đang nói về việc tra cứu bằng 0 so với n tra cứu ở đây, n là kích thước của Map
. Vì vậy, các yếu tố là cách xa hơn 2
...
Với Java 8 , bạn có thể lặp lại Bản đồ bằng biểu thức forEach và lambda,
map.forEach((k, v) -> System.out.println((k + ":" + v)));
Với Bộ sưu tập Eclipse , bạn sẽ sử dụng forEachKeyValue
phương thức trên MapIterable
giao diện, được kế thừa bởi MutableMap
và các ImmutableMap
giao diện và các triển khai của chúng.
MutableBag<String> result = Bags.mutable.empty();
MutableMap<Integer, String> map = Maps.mutable.of(1, "One", 2, "Two", 3, "Three");
map.forEachKeyValue((key, value) -> result.add(key + value));
Assert.assertEquals(Bags.mutable.of("1One", "2Two", "3Three"), result);
Sử dụng một lớp bên trong ẩn danh, bạn có thể viết mã như sau:
final MutableBag<String> result = Bags.mutable.empty();
MutableMap<Integer, String> map = Maps.mutable.of(1, "One", 2, "Two", 3, "Three");
map.forEachKeyValue(new Procedure2<Integer, String>()
{
public void value(Integer key, String value)
{
result.add(key + value);
}
});
Assert.assertEquals(Bags.mutable.of("1One", "2Two", "3Three"), result);
Lưu ý: Tôi là người đi làm cho Bộ sưu tập Eclipse.
Biểu thức Lambda Java 8
Trong Java 1.8 (Java 8), điều này đã trở nên dễ dàng hơn nhiều bằng cách sử dụng phương thức forEach từ các hoạt động Tổng hợp ( Hoạt động luồng ) trông tương tự như các trình vòng lặp từ Iterable Interface.
Chỉ cần sao chép dán bên dưới câu lệnh vào mã của bạn và đổi tên biến HashMap từ hm sang biến HashMap của bạn để in ra cặp khóa-giá trị.
HashMap<Integer,Integer> hm = new HashMap<Integer, Integer>();
/*
* Logic to put the Key,Value pair in your HashMap hm
*/
// Print the key value pair in one line.
hm.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));
// Just copy and paste above line to your code.
Dưới đây là mã mẫu mà tôi đã thử sử dụng Lambda Expression . Công cụ này là rất mát mẻ. Phải thử.
HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();
Random rand = new Random(47);
int i = 0;
while(i < 5) {
i++;
int key = rand.nextInt(20);
int value = rand.nextInt(50);
System.out.println("Inserting key: " + key + " Value: " + value);
Integer imap = hm.put(key, value);
if( imap == null) {
System.out.println("Inserted");
} else {
System.out.println("Replaced with " + imap);
}
}
hm.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));
Output:
Inserting key: 18 Value: 5
Inserted
Inserting key: 13 Value: 11
Inserted
Inserting key: 1 Value: 29
Inserted
Inserting key: 8 Value: 0
Inserted
Inserting key: 2 Value: 7
Inserted
key: 1 value:29
key: 18 value:5
key: 2 value:7
key: 8 value:0
key: 13 value:11
Ngoài ra người ta có thể sử dụng Spliterator cho cùng.
Spliterator sit = hm.entrySet().spliterator();
CẬP NHẬT
Bao gồm các tài liệu liên kết đến Oracle Docs. Để biết thêm về Lambda, hãy truy cập liên kết này và phải đọc Hoạt động tổng hợp và đối với Spliterator, hãy truy cập liên kết này .
Về lý thuyết, cách hiệu quả nhất sẽ phụ thuộc vào việc triển khai Bản đồ. Cách chính thức để làm điều này là gọi map.entrySet()
, trong đó trả về một tập hợp Map.Entry
, mỗi bộ chứa một khóa và một giá trị ( entry.getKey()
và entry.getValue()
).
Trong một triển khai idiosyncratic, nó có thể tạo ra một số khác biệt cho dù bạn sử dụng map.keySet()
,map.entrySet()
hay cái gì khác. Nhưng tôi không thể nghĩ ra một lý do tại sao mọi người sẽ viết nó như thế. Nhiều khả năng nó không có sự khác biệt để thực hiện những gì bạn làm.
Và có, thứ tự sẽ phụ thuộc vào việc thực hiện - cũng như (có thể) thứ tự chèn và các yếu tố khó kiểm soát khác.
[sửa] Tôi đã viết valueSet()
ban đầu nhưng tất nhiên entrySet()
thực sự là câu trả lời.
Chúng ta có forEach
phương thức chấp nhận biểu thức lambda . Chúng tôi cũng đã có API luồng . Hãy xem xét một bản đồ:
Map<String,String> sample = new HashMap<>();
sample.put("A","Apple");
sample.put("B", "Ball");
Lặp lại các phím:
sample.keySet().forEach((k) -> System.out.println(k));
Lặp lại các giá trị:
sample.values().forEach((v) -> System.out.println(v));
Lặp lại các mục (Sử dụng forEach và Luồng):
sample.forEach((k,v) -> System.out.println(k + ":" + v));
sample.entrySet().stream().forEach((entry) -> {
Object currentKey = entry.getKey();
Object currentValue = entry.getValue();
System.out.println(currentKey + ":" + currentValue);
});
Ưu điểm với các luồng là chúng có thể được song song dễ dàng trong trường hợp chúng ta muốn. Chúng tôi chỉ cần sử dụng parallelStream()
thay cho stream()
ở trên.
forEachOrdered
vs forEach
với stream?
Việc forEach
không tuân theo thứ tự gặp gỡ (nếu được xác định) và về bản chất là không xác định trong tự nhiên forEachOrdered
. Vì vậy, forEach
không đảm bảo rằng thứ tự sẽ được giữ. Cũng kiểm tra điều này để biết thêm.
Java 8:
Bạn có thể sử dụng biểu thức lambda:
myMap.entrySet().stream().forEach((entry) -> {
Object currentKey = entry.getKey();
Object currentValue = entry.getValue();
});
Để biết thêm thông tin, hãy làm theo điều này .
myMap.forEach( (currentKey,currentValue) -> /* action */ );
ngắn gọn hơn nhiều
Trong Bản đồ, người ta có thể Lặp lại keys
và / hoặc values
/ hoặc both (e.g., entrySet)
phụ thuộc vào người quan tâm đến_ Thích:
Lặp lại thông qua keys -> keySet()
bản đồ:
Map<String, Object> map = ...;
for (String key : map.keySet()) {
//your Business logic...
}
Lặp lại thông qua values -> values()
bản đồ:
for (Object value : map.values()) {
//your Business logic...
}
Lặp lại thông qua both -> entrySet()
bản đồ:
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
//your Business logic...
}
_ Hơn nữa, có 3 cách khác nhau để lặp qua một HashMap. Họ như dưới đây__
//1.
for (Map.Entry entry : hm.entrySet()) {
System.out.print("key,val: ");
System.out.println(entry.getKey() + "," + entry.getValue());
}
//2.
Iterator iter = hm.keySet().iterator();
while(iter.hasNext()) {
Integer key = (Integer)iter.next();
String val = (String)hm.get(key);
System.out.println("key,val: " + key + "," + val);
}
//3.
Iterator it = hm.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Integer key = (Integer)entry.getKey();
String val = (String)entry.getValue();
System.out.println("key,val: " + key + "," + val);
}
public class abcd{
public static void main(String[] args)
{
Map<Integer, String> testMap = new HashMap<Integer, String>();
testMap.put(10, "a");
testMap.put(20, "b");
testMap.put(30, "c");
testMap.put(40, "d");
for (Integer key:testMap.keySet()) {
String value=testMap.get(key);
System.out.println(value);
}
}
}
HOẶC LÀ
public class abcd {
public static void main(String[] args)
{
Map<Integer, String> testMap = new HashMap<Integer, String>();
testMap.put(10, "a");
testMap.put(20, "b");
testMap.put(30, "c");
testMap.put(40, "d");
for (Entry<Integer, String> entry : testMap.entrySet()) {
Integer key=entry.getKey();
String value=entry.getValue();
}
}
}
Nếu tôi có một đối tượng triển khai giao diện Bản đồ trong Java và tôi muốn lặp lại qua mọi cặp có trong đó, cách hiệu quả nhất để đi qua bản đồ là gì?
Nếu hiệu quả của việc lặp các khóa là ưu tiên cho ứng dụng của bạn, thì hãy chọn Map
triển khai duy trì các khóa theo thứ tự bạn muốn.
Việc sắp xếp các phần tử có phụ thuộc vào việc triển khai bản đồ cụ thể mà tôi có cho giao diện không?
Phải, chắc chắn rồi.
Map
triển khai hứa hẹn một thứ tự lặp nhất định, một số khác thì không.Map
duy trì thứ tự khác nhau của các cặp khóa-giá trị.Xem bảng này tôi đã tạo tóm tắt các Map
triển khai khác nhau được gói cùng với Java 11. Cụ thể, hãy chú ý cột thứ tự lặp . Nhấn / chạm để phóng to.
Bạn có thể thấy có bốn Map
triển khai duy trì một trật tự :
TreeMap
ConcurrentSkipListMap
LinkedHashMap
EnumMap
NavigableMap
giao diệnHai trong số đó thực hiện NavigableMap
giao diện: TreeMap
& ConcurrentSkipListMap
.
SortedMap
Giao diện cũ được thay thế một cách hiệu quả bởi NavigableMap
giao diện mới hơn . Nhưng bạn có thể thấy các triển khai của bên thứ 3 chỉ thực hiện giao diện cũ hơn.
Nếu bạn muốn một Map
cặp giữ cho các cặp của nó được sắp xếp theo thứ tự tự nhiên, thì hãy sử dụng TreeMap
hoặc ConcurrentSkipListMap
. Thuật ngữ thứ tự tự nhiên, có nghĩa là lớp các khóa thực hiện Comparable
. Giá trị được trả về bởicompareTo
phương thức được sử dụng để so sánh trong sắp xếp.
Nếu bạn muốn chỉ định một thói quen sắp xếp tùy chỉnh cho các khóa của bạn sẽ được sử dụng để duy trì thứ tự được sắp xếp, hãy chuyển một Comparator
triển khai phù hợp với lớp các khóa của bạn. Sử dụng TreeMap
hoặc ConcurrentSkipListMap
, vượt qua của bạn Comparator
.
Nếu bạn muốn các cặp bản đồ của mình được giữ theo thứ tự ban đầu mà bạn đã chèn chúng vào bản đồ, hãy sử dụng LinkedHashMap
.
Nếu bạn đang sử dụng một enum như DayOfWeek
hoặc Month
như các khóa của mình, hãy sử dụng EnumMap
lớp. Lớp này không chỉ được tối ưu hóa cao để sử dụng rất ít bộ nhớ và chạy rất nhanh, nó còn duy trì các cặp của bạn theo thứ tự được xác định bởi enum. Ví DayOfWeek
dụ, khóa của DayOfWeek.MONDAY
sẽ được tìm thấy đầu tiên khi lặp và khóa củaDayOfWeek.SUNDAY
sẽ là cuối cùng.
Trong việc lựa chọn Map
thực hiện, cũng xem xét:
Collections::synchronizedMap
(ít thích hợp hơn).Cả hai cân nhắc này được đề cập trong bảng đồ họa ở trên.
EnumMap
, vì đây là lần đầu tiên tôi nghe về nó. Có lẽ nhiều trường hợp điều này có thể có ích.
Việc đặt hàng sẽ luôn phụ thuộc vào việc thực hiện bản đồ cụ thể. Sử dụng Java 8, bạn có thể sử dụng một trong hai cách sau:
map.forEach((k,v) -> { System.out.println(k + ":" + v); });
Hoặc là:
map.entrySet().forEach((e) -> {
System.out.println(e.getKey() + " : " + e.getValue());
});
Kết quả sẽ giống nhau (cùng thứ tự). Mục nhập được hỗ trợ bởi bản đồ để bạn có được thứ tự tương tự. Cách thứ hai rất tiện lợi vì nó cho phép bạn sử dụng lambdas, ví dụ nếu bạn chỉ muốn in chỉ các đối tượng Integer lớn hơn 5:
map.entrySet()
.stream()
.filter(e-> e.getValue() > 5)
.forEach(System.out::println);
Mã dưới đây cho thấy sự lặp lại thông qua LinkedHashMap và HashMap bình thường (ví dụ). Bạn sẽ thấy sự khác biệt theo thứ tự:
public class HMIteration {
public static void main(String[] args) {
Map<Object, Object> linkedHashMap = new LinkedHashMap<>();
Map<Object, Object> hashMap = new HashMap<>();
for (int i=10; i>=0; i--) {
linkedHashMap.put(i, i);
hashMap.put(i, i);
}
System.out.println("LinkedHashMap (1): ");
linkedHashMap.forEach((k,v) -> { System.out.print(k + " (#="+k.hashCode() + "):" + v + ", "); });
System.out.println("\nLinkedHashMap (2): ");
linkedHashMap.entrySet().forEach((e) -> {
System.out.print(e.getKey() + " : " + e.getValue() + ", ");
});
System.out.println("\n\nHashMap (1): ");
hashMap.forEach((k,v) -> { System.out.print(k + " (#:"+k.hashCode() + "):" + v + ", "); });
System.out.println("\nHashMap (2): ");
hashMap.entrySet().forEach((e) -> {
System.out.print(e.getKey() + " : " + e.getValue() + ", ");
});
}
}
LinkedHashMap (1):
10 (# = 10): 10, 9 (# = 9): 9, 8 (# = 8): 8, 7 (# = 7): 7, 6 (# = 6): 6, 5 (# = 5 ): 5, 4 (# = 4): 4, 3 (# = 3): 3, 2 (# = 2): 2, 1 (# = 1): 1, 0 (# = 0): 0,
LinkedHashMap (2):
10: 10, 9: 9, 8: 8, 7: 7, 6: 6, 5: 5, 4: 4, 3: 3, 2: 2, 1: 1, 0: 0,
HashMap (1):
0 (#: 0): 0, 1 (#: 1): 1, 2 (#: 2): 2, 3 (#: 3): 3, 4 (#: 4): 4, 5 (#: 5 ): 5, 6 (#: 6): 6, 7 (#: 7): 7, 8 (#: 8): 8, 9 (#: 9): 9, 10 (#: 10): 10,
HashMap (2):
0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10,
Sử dụng Java 8:
map.entrySet().forEach(entry -> System.out.println(entry.getValue()));
Bạn có thể làm điều đó bằng cách sử dụng thuốc generic:
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Một giải pháp lặp lại hiệu quả trên Bản đồ là một vòng lặp 'cho mỗi' từ Java 5 đến Java 7. Đây là:
for (String key : phnMap.keySet()) {
System.out.println("Key: " + key + " Value: " + phnMap.get(key));
}
Từ Java 8, bạn có thể sử dụng biểu thức lambda để lặp lại trên Bản đồ. Nó là một 'forEach' nâng cao
phnMap.forEach((k,v) -> System.out.println("Key: " + k + " Value: " + v));
Nếu bạn muốn viết một điều kiện cho lambda, bạn có thể viết nó như thế này:
phnMap.forEach((k,v)->{
System.out.println("Key: " + k + " Value: " + v);
if("abc".equals(k)){
System.out.println("Hello abc");
}
});
//Functional Oprations
Map<String, String> mapString = new HashMap<>();
mapString.entrySet().stream().map((entry) -> {
String mapKey = entry.getKey();
return entry;
}).forEach((entry) -> {
String mapValue = entry.getValue();
});
//Intrator
Map<String, String> mapString = new HashMap<>();
for (Iterator<Map.Entry<String, String>> it = mapString.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, String> entry = it.next();
String mapKey = entry.getKey();
String mapValue = entry.getValue();
}
//Simple for loop
Map<String, String> mapString = new HashMap<>();
for (Map.Entry<String, String> entry : mapString.entrySet()) {
String mapKey = entry.getKey();
String mapValue = entry.getValue();
}
Có rất nhiều cách để làm điều này. Dưới đây là một vài bước đơn giản:
Giả sử bạn có một Bản đồ như:
Map<String, Integer> m = new HashMap<String, Integer>();
Sau đó, bạn có thể làm một cái gì đó như dưới đây để lặp lại các yếu tố bản đồ.
// ********** Using an iterator ****************
Iterator<Entry<String, Integer>> me = m.entrySet().iterator();
while(me.hasNext()){
Entry<String, Integer> pair = me.next();
System.out.println(pair.getKey() + ":" + pair.getValue());
}
// *********** Using foreach ************************
for(Entry<String, Integer> me : m.entrySet()){
System.out.println(me.getKey() + " : " + me.getValue());
}
// *********** Using keySet *****************************
for(String s : m.keySet()){
System.out.println(s + " : " + m.get(s));
}
// *********** Using keySet and iterator *****************
Iterator<String> me = m.keySet().iterator();
while(me.hasNext()){
String key = me.next();
System.out.println(key + " : " + m.get(key));
}
package com.test;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("ram", "ayodhya");
map.put("krishan", "mathura");
map.put("shiv", "kailash");
System.out.println("********* Keys *********");
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key);
}
System.out.println("********* Values *********");
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
System.out.println("***** Keys and Values (Using for each loop) *****");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + "\t Value: "
+ entry.getValue());
}
System.out.println("***** Keys and Values (Using while loop) *****");
Iterator<Entry<String, String>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, String> entry = (Map.Entry<String, String>) entries
.next();
System.out.println("Key: " + entry.getKey() + "\t Value: "
+ entry.getValue());
}
System.out
.println("** Keys and Values (Using java 8 using lambdas )***");
map.forEach((k, v) -> System.out
.println("Key: " + k + "\t value: " + v));
}
}