• java集合类源码分析之Set


    Set集合与List一样,都是继承自Collection接口,常用的实现类有HashSet和TreeSet。值得注意的是,HashSet是通过HashMap来实现的而TreeSet是通过TreeMap来实现的,所以HashSet和TreeSet都没有自己的数据结构,具体可以归纳如下:

    • Set集合中的元素不能重复,即元素唯一
    • HashSet按元素的哈希值存储,所以是无序的,并且最多允许一个null对象
    • TreeSet按元素的大小存储,所以是有序的,并且不允许null对象
    • Set集合没有get方法,所以只能通过迭代器(Iterator)来遍历元素,不能随机访问

    1.HashSet

    下面给出HashSet的部分源码,以理解它的实现方式。

    1     static final long serialVersionUID = -5024744406713321676L;
    2 
    3     private transient HashMap<E,Object> map;
    4 
    5     // Dummy value to associate with an Object in the backing Map
    6     private static final Object PRESENT = new Object();

     观察源码,我们知道HashSet的数据是存储在HashMap的实例对象map中的,并且对应于map中的key,而Object类型的引用PRESENT则是对应于map中的value的一个虚拟值,没有实际意义。联想到HashMap的一些特性:无序存储、key值唯一等等,我们就可以很自然地理解Set集合元素不能重复以及HashSet无序存储的特性了。

    下面从源代码的角度来理解HashSet的基本用法:

    • 构造器(四种)

    1.HashSet() 空的构造器,初始化一个空的HashMap

    2.HashSet(Collection<? extends E> c) 传入一个子集c,用于初始化HashMap

    3.HashSet(int initialCapacity, float loadFactor) 初始化一个空的HashMap,并指定初始容量和加载因子

    4.HashSet(int initialCapacity) 初始化一个空的HashMap,并指定初始容量

        public HashSet() {
            map = new HashMap<>();
        }
    
        public HashSet(Collection<? extends E> c) {
            map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
            addAll(c);
        }
    
        public HashSet(int initialCapacity, float loadFactor) {
            map = new HashMap<>(initialCapacity, loadFactor);
        }
    
        public HashSet(int initialCapacity) {
            map = new HashMap<>(initialCapacity);
        }
    • 插入元素

    1.add(E e) 插入指定元素(调用HashMap的put方法实现)

    1         Set<String> hashSet = new HashSet<String>();
    2         hashSet.add("D");
    3         hashSet.add("B");
    4         hashSet.add("C");
    5         hashSet.add("A");
    • 查找元素

    1.contains(Object o) 判断集合中是否包含指定的元素(调用HashMap的containsKey方法实现)

    1     public boolean contains(Object o) {
    2         return map.containsKey(o);
    3     }

    2.由于HashSet的实现类中没有get方法,所以只能通过迭代器依次遍历,而不能随机访问(调用HashMap中keySet的迭代器实现)

    1     public Iterator<E> iterator() {
    2         return map.keySet().iterator();
    3     }

    应用示例:

    1         Set<String> hashSet = new HashSet<String>();
    2         hashSet.add("D");
    3         hashSet.add("B");
    4         hashSet.add("C");
    5         hashSet.add("A");
    6         for (Iterator iterator = hashSet.iterator(); iterator.hasNext();) {
    7             String string = (String) iterator.next();
    8             System.out.print(string+" ");
    9         }//D A B C 
    View Code
    • 修改元素

    由于HashMap中的key值不能修改,所以HashSet不能进行修改元素的操作

    • 删除元素

    1.remove(Object o) 删除指定元素(调用HashMap中的remove方法实现,返回值为true或者false)

    1     public boolean remove(Object o) {
    2         return map.remove(o)==PRESENT;
    3     }

    2.clear() 清空元素(调用HashMap中的clear方法实现,没有返回值)

    1     public void clear() {
    2         map.clear();
    3     }
    View Code

     

    2.TreeSet

     TreeSet是SortedSet接口的唯一实现类。前面说过,TreeSet没有自己的数据结构而是通过TreeMap实现的,所以TreeSet也是基于红黑二叉树的一种存储结构,所以TreeSet不允许null对象,并且是有序存储的(默认升序)

    1     private transient NavigableMap<E,Object> m;
    2 
    3     // Dummy value to associate with an Object in the backing Map
    4     private static final Object PRESENT = new Object();

    上述源代码中的NavigableMap是继承自SrotedMap的一个接口,其实现类为TreeMap,因此TreeSet中的数据是通过TreeMap来存储的,此处的PRESENT也是没有实际意义的虚拟值。

    下面从源代码的角度来理解HashSet的基本用法:

    • 构造器(四种)

    1.TreeSet() 空的构造器,初始化一个空的TreeMap,默认升序排列

    2.TreeSet(Comparator<? super E> comparator) 传入一个自定义的比较器,常常用于实现降序排列

    3.TreeSet(Collection<? extends E> c) 传入一个子集c,用于初始化TreeMap对象,默认升序

    4.TreeSet(SortedSet<E> s) 传入一个有序的子集s,用于初始化TreeMap对象,采用子集的比较器

     1     public TreeSet() {
     2         this(new TreeMap<E,Object>());
     3     }
     4 
     5     public TreeSet(Comparator<? super E> comparator) {
     6         this(new TreeMap<>(comparator));
     7     }
     8 
     9     public TreeSet(Collection<? extends E> c) {
    10         this();
    11         addAll(c);
    12     }
    13 
    14     public TreeSet(SortedSet<E> s) {
    15         this(s.comparator());
    16         addAll(s);
    17     }

     应用示例:

     1         //自定义一个比较器,实现降序排列
     2         Set<Integer> treeSet = new TreeSet<Integer>(new Comparator<Integer>() {
     3 
     4             @Override
     5             public int compare(Integer o1, Integer o2) {
     6 //                return 0;        //默认升序
     7                 return o2.compareTo(o1);//降序
     8             }
     9         });
    10         treeSet.add(200);
    11         treeSet.add(120);
    12         treeSet.add(150);
    13         treeSet.add(110);
    14         for (Iterator iterator = treeSet.iterator(); iterator.hasNext();) {
    15             Integer integer = (Integer) iterator.next();
    16             System.out.print(integer+" ");
    17         }    //200 150 120 110 
    View Code
     1         ArrayList<Integer> list = new ArrayList<Integer>();
     2         list.add(300);
     3         list.add(120);
     4         list.add(100);
     5         list.add(150);
     6         System.out.println(list); //[300, 120, 100, 150]
     7         
     8         //传入一个子集,默认升序排列
     9         TreeSet<Integer> treeSet = new TreeSet<Integer>(list);
    10         for (Iterator iterator = treeSet.iterator(); iterator.hasNext();) {
    11             Integer integer = (Integer) iterator.next();
    12             System.out.print(integer+" ");
    13         }//100 120 150 300 
    View Code
     1         /*
     2          * 传入一个有序的子集,采用子集的比较器
     3          *     注意子集的类型必须是SortedSet及其子类或者实现类,否则将采用默认的比较器
     4          *     所以此处subSet的类型也可以是TreeSet。
     5          */
     6         
     7         SortedSet<Integer> subSet = new TreeSet<Integer>(new Comparator<Integer>() {
     8 
     9             @Override
    10             public int compare(Integer o1, Integer o2) {
    11 //                return 0;        //默认升序
    12                 return o2.compareTo(o1);//降序
    13             }
    14         });
    15         subSet.add(200);
    16         subSet.add(120);
    17         subSet.add(150);
    18         subSet.add(110);
    19         for (Iterator iterator = subSet.iterator(); iterator.hasNext();) {
    20             Integer integer = (Integer) iterator.next();
    21             System.out.print(integer+" ");
    22         }    //200 150 120 110 
    23         
    24         System.out.println();
    25         Set<Integer> treeSet = new TreeSet<Integer>(subSet);
    26         for (Iterator iterator = treeSet.iterator(); iterator.hasNext();) {
    27             Integer integer = (Integer) iterator.next();
    28             System.out.print(integer+" ");
    29         }//200 150 120 110 
    30         
    31         System.out.println();
    32         treeSet.add(500);
    33         treeSet.add(100);
    34         treeSet.add(105);
    35         for (Iterator iterator = treeSet.iterator(); iterator.hasNext();) {
    36             Integer integer = (Integer) iterator.next();
    37             System.out.print(integer+" ");
    38         }//500 200 150 120 110 105 100 
    View Code
    •  插入元素

     1.add(E e) 插入指定的元素(调用TreeMap的put方法实现)

    2.addAll(Collection<? extends E> c) 插入一个子集c

     1         ArrayList<Integer> list = new ArrayList<Integer>();
     2         list.add(300);
     3         list.add(120);
     4         list.add(100);
     5         list.add(150);
     6         System.out.println(list); //[300, 120, 100, 150]
     7         
     8         Set<Integer> treeSet  = new TreeSet<Integer>();
     9         
    10         //插入一个子集,默认升序
    11         treeSet.addAll(list);
    12         for (Iterator iterator = treeSet.iterator(); iterator.hasNext();) {
    13             Integer integer = (Integer) iterator.next();
    14             System.out.print(integer+" ");
    15         }//100 120 150 300 
    • 查找元素

    1.contains(Object o)  判断集合中是否包含指定对象(调用TreeMap的containsKey方法实现)

    2.与HashSet一样,TreeSet的实现类中没有get方法,所以只能通过迭代器依次遍历,而不能随机访问(调用TreeMap中keySet的迭代器实现)。

    • 修改元素

    TreeSet不能进行修改元素的操作,原因与HashSet一样。

    • 删除元素

    1.remove(Object o) 删除指定元素(调用TreeMap中的remove方法实现,返回true或者false)

    1     public boolean remove(Object o) {
    2         return m.remove(o)==PRESENT;
    3     }

    2.clear() 清空元素(调用TreeMap中的clear方法实现,无返回值)

    1     public void clear() {
    2         m.clear();
    3     }

    应用示例:

     1         ArrayList<Integer> list = new ArrayList<Integer>();
     2         list.add(300);
     3         list.add(120);
     4         list.add(100);
     5         list.add(150);
     6         System.out.println(list); //[300, 120, 100, 150]
     7         
     8         Set<Integer> treeSet  = new TreeSet<Integer>();
     9         
    10         //插入一个子集,默认升序
    11         treeSet.addAll(list);
    12         for (Iterator iterator = treeSet.iterator(); iterator.hasNext();) {
    13             Integer integer = (Integer) iterator.next();
    14             System.out.print(integer+" ");
    15         }//100 120 150 300 
    16         
    17         System.out.println(treeSet.remove(100));//true
    18         for (Iterator iterator = treeSet.iterator(); iterator.hasNext();) {
    19             Integer integer = (Integer) iterator.next();
    20             System.out.print(integer+" ");
    21         }//120 150 300 
    22         
    23         treeSet.clear();
    24         System.out.println(treeSet.size());//0

    至此,HashSet和TreeSet的存储结构及基本用法介绍完毕。

  • 相关阅读:
    NO17--vue父子组件间单向数据流的解决办法
    NO--16 vue之父子组件传值
    使用Element-ui开发桌面应用的小问题
    微信小程序右到左联动
    微信小程序左到右联动
    微信小程序选项卡之跳转
    微信小程序
    回调函数和钩子函数的说明
    最近项目中遇到了一个场景,其实很常见,就是定时获取接口刷新数据。那么问题来了,假设我设置的定时时间为1s,而数据接口返回大于1s,应该用同步阻塞还是异步?
    组件的相互嵌套slot内容分发
  • 原文地址:https://www.cnblogs.com/Wilange/p/7638646.html
Copyright © 2020-2023  润新知