zoukankan      html  css  js  c++  java
  • 如何用Map对象创建Set对象

    如何用Map对象创建Set对象

    http://www.importnew.com/9639.html

    本文由 ImportNew - 夏千林 翻译自 javaspecialists。欢迎加入翻译小组。转载请见文末要求。

    Java中的Map和Set有不少相似之处。本文将分享一个把Map类转化成Set类的小技巧。

    或许你已经知道,HashSet其实是一个披着Set方法外衣的HashMap;同样,TreeSet其实也是一个披着Set方法外衣的TreeMap。Map并不支持直接用迭代器进行遍历,因此下面的这段代码编译无法通过:

    1
    2
    3
    Map<String, Double> salaries = new HashMap<>();
    for(double salary : salaries) { // does not compile
    }

    我们可以通过遍历Map中的key集合、value集合和entry集合来实现Map的遍历。由于Map中的value是可以重复出现的,因此values()方法返回的是一个Collection类型的集合。而Map中的key是不允许重复的,因此keySet()方法和entrySet()返回的都是Set类型的集合。

    因此,我们可以采用下面的方法来遍历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而烦恼,现在你就可以自己创建一个了。

    原文链接: javaspecialists 翻译: ImportNew.com 夏千林
    译文链接: http://www.importnew.com/9639.html
    转载请保留原文出处、译者和译文链接。]

    关于作者: 夏千林

  • 相关阅读:
    【后缀自动机例题】
    【BZOJ-1146】网络管理Network DFS序 + 带修主席树
    【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集
    【BZOJ-2653】middle 可持久化线段树 + 二分
    【Codeforces163E】e-Government AC自动机fail树 + DFS序 + 树状数组
    【BZOJ-2938】病毒 Trie图 + 拓扑排序
    【BZOJ-4726】Sabota? 树形DP
    【BZOJ-3143】游走 高斯消元 + 概率期望
    【BZOJ-3270】博物馆 高斯消元 + 概率期望
    laravel框架中所用到的依赖注入
  • 原文地址:https://www.cnblogs.com/handsome1013/p/7325975.html
Copyright © 2011-2022 走看看