java集合16-HashSet源码走读

来源:互联网 发布:网络平台借贷违法的吗 编辑:程序博客网 时间:2024/06/05 20:21

转载地址:http://blog.csdn.net/wangxiaotongfan/article/details/51346225

概要

这一章,我们对HashSet进行学习。 
我们先对HashSet有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashSet。内容包括: 

第1部分 HashSet介绍 
第2部分 HashSet数据结构 
第3部分 HashSet源码解析(基于JDK1.6.0_45) 
第4部分 HashSet遍历方式 
第5部分 HashSet示例

第1部分 HashSet介绍

HashSet 简介

HashSet 是一个没有重复元素的集合。 
它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。 
HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:

Set s = Collections.synchronizedSet(new HashSet(...));
  • 1

HashSet通过iterator()返回的迭代器是fail-fast的。

HashSet的构造函数

 // 默认构造函数public HashSet() // 带集合的构造函数public HashSet(Collection<? extends E> c) // 指定HashSet初始容量和加载因子的构造函数public HashSet(int initialCapacity, float loadFactor) // 指定HashSet初始容量的构造函数public HashSet(int initialCapacity) // 指定HashSet初始容量和加载因子的构造函数,dummy没有任何作用HashSet(int initialCapacity, float loadFactor, boolean dummy) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

HashSet的主要API

boolean         add(E object)void            clear()Object          clone()boolean         contains(Object object)boolean         isEmpty()Iterator<E>     iterator()boolean         remove(Object object)int             size()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

第2部分 HashSet数据结构

HashSet的继承关系如下:

java.lang.Object   ↳     java.util.AbstractCollection<E>         ↳     java.util.AbstractSet<E>               ↳     java.util.HashSet<E>public class HashSet<E>    extends AbstractSet<E>    implements Set<E>, Cloneable, java.io.Serializable { }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

HashSet与Map关系如下图:


从图中可以看出: 
(01) HashSet继承于AbstractSet,并且实现了Set接口。 
(02) HashSet的本质是一个”没有重复元素”的集合,它是通过HashMap实现的。HashSet中含有一个”HashMap类型的成员变量”map,HashSet的操作函数,实际上都是通过map实现的。

第3部分 HashSet源码解析(基于JDK1.6.0_45)

为了更了解HashSet的原理,下面对HashSet源码代码作出分析。

package java.util;public class HashSet<E>    extends AbstractSet<E>    implements Set<E>, Cloneable, java.io.Serializable{    static final long serialVersionUID = -5024744406713321676L;    // HashSet是通过map(HashMap对象)保存内容的    private transient HashMap<E,Object> map;    // PRESENT是向map中插入key-value对应的value    // 因为HashSet中只需要用到key,而HashMap是key-value键值对;    // 所以,向map中添加键值对时,键值对的值固定是PRESENT    private static final Object PRESENT = new Object();    // 默认构造函数    public HashSet() {        // 调用HashMap的默认构造函数,创建map        map = new HashMap<E,Object>();    }    // 带集合的构造函数    public HashSet(Collection<? extends E> c) {        // 创建map。        // 为什么要调用Math.max((int) (c.size()/.75f) + 1, 16),从 (c.size()/.75f) + 1 和 16 中选择一个比较大的树呢?                // 首先,说明(c.size()/.75f) + 1        //   因为从HashMap的效率(时间成本和空间成本)考虑,HashMap的加载因子是0.75。        //   当HashMap的“阈值”(阈值=HashMap总的大小*加载因子) < “HashMap实际大小”时,        //   就需要将HashMap的容量翻倍。        //   所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。        // 接下来,说明为什么是 16 。        //   HashMap的总的大小,必须是2的指数倍。若创建HashMap时,指定的大小不是2的指数倍;        //   HashMap的构造函数中也会重新计算,找出比“指定大小”大的最小的2的指数倍的数。        //   所以,这里指定为16是从性能考虑。避免重复计算。        map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));        // 将集合(c)中的全部元素添加到HashSet中        addAll(c);    }    // 指定HashSet初始容量和加载因子的构造函数    public HashSet(int initialCapacity, float loadFactor) {        map = new HashMap<E,Object>(initialCapacity, loadFactor);    }    // 指定HashSet初始容量的构造函数    public HashSet(int initialCapacity) {        map = new HashMap<E,Object>(initialCapacity);    }    HashSet(int initialCapacity, float loadFactor, boolean dummy) {        map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);    }    // 返回HashSet的迭代器    public Iterator<E> iterator() {        // 实际上返回的是HashMap的“key集合的迭代器”        return map.keySet().iterator();    }    public int size() {        return map.size();    }    public boolean isEmpty() {        return map.isEmpty();    }    public boolean contains(Object o) {        return map.containsKey(o);    }    // 将元素(e)添加到HashSet中    public boolean add(E e) {        return map.put(e, PRESENT)==null;    }    // 删除HashSet中的元素(o)    public boolean remove(Object o) {        return map.remove(o)==PRESENT;    }    public void clear() {        map.clear();    }    // 克隆一个HashSet,并返回Object对象    public Object clone() {        try {            HashSet<E> newSet = (HashSet<E>) super.clone();            newSet.map = (HashMap<E, Object>) map.clone();            return newSet;        } catch (CloneNotSupportedException e) {            throw new InternalError();        }    }    // java.io.Serializable的写入函数    // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”都写入到输出流中    private void writeObject(java.io.ObjectOutputStream s)        throws java.io.IOException {        // Write out any hidden serialization magic        s.defaultWriteObject();        // Write out HashMap capacity and load factor        s.writeInt(map.capacity());        s.writeFloat(map.loadFactor());        // Write out size        s.writeInt(map.size());        // Write out all elements in the proper order.        for (Iterator i=map.keySet().iterator(); i.hasNext(); )            s.writeObject(i.next());    }    // java.io.Serializable的读取函数    // 将HashSet的“总的容量,加载因子,实际容量,所有的元素”依次读出    private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        // Read in any hidden serialization magic        s.defaultReadObject();        // Read in HashMap capacity and load factor and create backing HashMap        int capacity = s.readInt();        float loadFactor = s.readFloat();        map = (((HashSet)this) instanceof LinkedHashSet ?               new LinkedHashMap<E,Object>(capacity, loadFactor) :               new HashMap<E,Object>(capacity, loadFactor));        // Read in size        int size = s.readInt();        // Read in all elements in the proper order.        for (int i=0; i<size; i++) {            E e = (E) s.readObject();            map.put(e, PRESENT);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141

说明: HashSet的代码实际上非常简单,通过上面的注释应该很能够看懂。它是通过HashMap实现的,若对HashSet的理解有困难,建议先学习以下HashMap;学完HashMap之后,在学习HashSet就非常容易了。

第4部分 HashSet遍历方式

4.1 通过Iterator遍历HashSet

第一步:根据iterator()获取HashSet的迭代器。 
第二步:遍历迭代器获取各个元素。

// 假设set是HashSet对象for(Iterator iterator = set.iterator();       iterator.hasNext(); ) {     iterator.next();}   
  • 1
  • 2
  • 3
  • 4
  • 5

4.2 通过for-each遍历HashSet

第一步:根据toArray()获取HashSet的元素集合对应的数组。 
第二步:遍历数组,获取各个元素。

// 假设set是HashSet对象,并且set中元素是String类型 
String[] arr = (String[])set.toArray(new String[0]); 
for (String str:arr) 
System.out.printf(“for each : %s\n”, str); 
HashSet的遍历测试程序如下:

import java.util.Random;import java.util.Iterator;import java.util.HashSet;/* * @desc 介绍HashSet遍历方法 * * @author skywang */public class HashSetIteratorTest {    public static void main(String[] args) {        // 新建HashSet        HashSet set = new HashSet();        // 添加元素 到HashSet中        for (int i=0; i<5; i++)            set.add(""+i);        // 通过Iterator遍历HashSet        iteratorHashSet(set) ;        // 通过for-each遍历HashSet        foreachHashSet(set);    }    /*     * 通过Iterator遍历HashSet。推荐方式     */    private static void iteratorHashSet(HashSet set) {        for(Iterator iterator = set.iterator();               iterator.hasNext(); ) {            System.out.printf("iterator : %s\n", iterator.next());        }    }    /*     * 通过for-each遍历HashSet。不推荐!此方法需要先将Set转换为数组     */    private static void foreachHashSet(HashSet set) {        String[] arr = (String[])set.toArray(new String[0]);        for (String str:arr)            System.out.printf("for each : %s\n", str);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

运行结果:

iterator : 3iterator : 2iterator : 1iterator : 0iterator : 4for each : 3for each : 2for each : 1for each : 0for each : 4
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

第5部分 HashSet示例

下面我们通过实例学习如何使用HashSet

import java.util.Iterator;import java.util.HashSet;/* * @desc HashSet常用API的使用。 * * @author skywang */public class HashSetTest {    public static void main(String[] args) {        // HashSet常用API        testHashSetAPIs() ;    }    /*     * HashSet除了iterator()和add()之外的其它常用API     */    private static void testHashSetAPIs() {        // 新建HashSet        HashSet set = new HashSet();        // 将元素添加到Setset.add("a");        set.add("b");        set.add("c");        set.add("d");        set.add("e");        // 打印HashSet的实际大小        System.out.printf("size : %d\n", set.size());        // 判断HashSet是否包含某个值        System.out.printf("HashSet contains a :%s\n", set.contains("a"));        System.out.printf("HashSet contains g :%s\n", set.contains("g"));        // 删除HashSet中的“e”        set.remove("e");        // 将Set转换为数组        String[] arr = (String[])set.toArray(new String[0]);        for (String str:arr)            System.out.printf("for each : %s\n", str);        // 新建一个包含b、c、f的HashSet        HashSet otherset = new HashSet();        otherset.add("b");        otherset.add("c");        otherset.add("f");        // 克隆一个removeset,内容和set一模一样        HashSet removeset = (HashSet)set.clone();        // 删除“removeset中,属于otherSet的元素”        removeset.removeAll(otherset);        // 打印removeset        System.out.printf("removeset : %s\n", removeset);        // 克隆一个retainset,内容和set一模一样        HashSet retainset = (HashSet)set.clone();        // 保留“retainset中,属于otherSet的元素”        retainset.retainAll(otherset);        // 打印retainset        System.out.printf("retainset : %s\n", retainset);        // 遍历HashSet        for(Iterator iterator = set.iterator();               iterator.hasNext(); )             System.out.printf("iterator : %s\n", iterator.next());        // 清空HashSet        set.clear();        // 输出HashSet是否为空        System.out.printf("%s\n", set.isEmpty()?"set is empty":"set is not empty");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

运行结果:

size : 5HashSet contains a :trueHashSet contains g :falsefor each : dfor each : bfor each : cfor each : aremoveset : [d, a]retainset : [b, c]iterator : diterator : biterator : citerator : aset is empty
0 0
原创粉丝点击