如何用Map對象建立Set對象

Java中的Map和Set有很多類似之處。本文將分享一個把Map類轉化成Set類的小技巧。java

或許你已經知道,HashSet實際上是一個披着Set方法外衣的HashMap;一樣,TreeSet其實也是一個披着Set方法外衣的TreeMap。Map並不支持直接用迭代器進行遍歷,所以下面的這段代碼編譯沒法經過:程序員

1算法

2sql

3安全

Map<String, Double> salaries = new HashMap<>();架構

for(double salary : salaries) { // does not compile併發

}less

咱們能夠經過遍歷Map中的key集合、value集合和entry集合來實現Map的遍歷。因爲Map中的value是能夠重複出現的,所以values()方法返回的是一個Collection類型的集合。而Map中的key是不容許重複的,所以keySet()方法和entrySet()返回的都是Set類型的集合。jsp

所以,咱們能夠採用下面的方法來遍歷Map:分佈式

1

2

3

Map<String, Double> salaries = new HashMap<>();

for (double salary : salaries.values()) {

}

或者能夠經過遍歷key來遍歷Map:

1

2

3

Map<String, Double> salaries = new HashMap<>();

for (String name : salaries.keySet()) {

}

固然,還能夠經過遍歷entry來遍歷Map:

1

2

3

4

5

Map<String, Double> salaries = new HashMap<>();

for (Map.Entry<String, Double> entry : salaries.entrySet()) {

  String name = entry.getKey();

  double salary = entry.getValue();

}

我常常看到程序員這樣遍歷Map:先獲取keySet,而後對keys進行遍歷,並經過get()方法找到對應的value。

1

2

3

4

Map<String, Double> salaries = new HashMap<>();

for (String name : salaries.keySet()) { // less efficient way to

    double salary = salaries.get(name);   // iterate over entries

}

從直觀上看,採用遍歷entry的方式遍歷Map會更加高效一些,這種遍歷方式的時間複雜度是O(n)。然而,若是HashMap中的元素分佈均勻,調用get()方法查找元素的時間複雜度將是O(1),那麼這兩種方法遍歷HashMap的時間複雜度是同樣的,都是O(n)。這兩種遍歷方式雖然有所不一樣,但時間複雜度都是線性的。但這個結論並不適用於其它類型的Map,特別是TreeMap。TreeMap的平均查找效率是O(log n),所以經過keySet遍歷TreeMap的時間複雜度是O(n x log n)。

java.util包中有不少Map類,其中一些Map類有着對應類型的Set類實現,例如TreeMap和HashMap。這些Set類都是基於對應的Map類實現的,所以它們和對應的Map類保持相同的算法複雜度以及併發特性。

本文的重點來了。我在完成併發專修課程中的某道練習題時,須要一個快速高效而且線程安全的HashSet。起初,我直接把ConcurrentHashMap看成Set用,把要插入Set的元素以Key的形式插入Map,Key所對應的Value則是一個無心義的默認值。後來我發現,Java 6中的java.util.Collections類提供了一個newSetFromMap()方法,該方法可以基於指定的Map對象建立一個新的Set對象。在建立這個Map<K, V>對象時,K的數據類型必須與你想要建立的Set中元素的數據類型一致;而V必須是Boolean類型的,這是由於value字段用於標記該元素是否存在。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

import java.util.*;

import java.util.concurrent.*;

 

public class ConcurrentSetTest {

    public static void main(String[] args) {

        Set<String> names = Collections.newSetFromMap(

            new ConcurrentHashMap<String, Boolean>()

        );

        names.add("Brian Goetz");

        names.add("Victor Grazi");

        names.add("Heinz Kabutz");

        names.add("Brian Goetz");

        System.out.println("names = " + names);

    }

}

固然,newSetFromMap()方法只能返回標準Set接口類型的對象。若是你的Map類有着更豐富的接口(與標準Map<K, V>接口相比),你仍是須要自行封裝實現對應的Set類。

但願讀者能從本文中有所收穫。若是你曾經爲找不到ConcurrentHashSet而煩惱,如今你就能夠本身建立一個了。

歡迎工做一到五年的Java工程師朋友們加入Java架構開發:855801563

羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代

相關文章
相關標籤/搜索