JDK源码--HashSet

一、概念

类定义:

public class HashSet
    extends AbstractSet
    implements Set, Cloneable, java.io.Serializable
  • 继承了AbstractSet抽象类,实现了Set接口,拥有一组Set通用的操作。
  • 实现了Cloneable接口,可进行浅层次拷贝。
  • 实现了Serializable接口,可进行序列化。

特点:

  • 允许存放空对象。
  • 没有重复元素。
  • 不保证元素顺序。
  • 非线程安全类,可通过Collections.synchronizedSet(new HashSet())获得线程安全的HashSet。

二、使用

//TestHashSet
public class TestHashSet {
    private static final String TAG = "TestHashSet";
    private HashSet  set = new HashSet();

    public void testAdd() {
        set.add("Android");
        set.add("Java");
        set.add("网络协议");
        set.add("数据结构与算法");
        set.add("Android");
        Log.d(TAG, "zwm, add set: " + set);
    }

    public void testRemove() {
        set.remove("网络协议");
        Log.d(TAG, "zwm, remove set: " + set);
    }

    public void testContains() {
        Log.d(TAG, "zwm, contains Java: " + set.contains("Java"));
    }

    public void testSize() {
        Log.d(TAG, "zwm, size: " + set.size());
    }

    public void testClear() {
        set.clear();
        Log.d(TAG, "zwm, clear set: " + set);
    }

    public void testEmpty() {
        Log.d(TAG, "zwm, isEmpty: " + set.isEmpty());
    }

    public void testIterator() {
        Iterator iterator = set.iterator();
        while(iterator.hasNext()) {
            Log.d(TAG, "zwm, iterator item: " + iterator.next());
        }
    }

    public void testClone() {
        Log.d(TAG, "zwm, set hashcode: " + set.hashCode());
        Iterator iterator = set.iterator();
        while(iterator.hasNext()) {
            Log.d(TAG, "zwm, set item hashcode: " + iterator.next().hashCode());
        }

        HashSet cloneSet = (HashSet)set.clone(); //浅拷贝
        Log.d(TAG, "zwm, cloneSet hashcode: " + cloneSet.hashCode());
        Iterator cloneIterator = cloneSet.iterator();
        while(cloneIterator.hasNext()) {
            Log.d(TAG, "zwm, cloneSet item hashcode: " + cloneIterator.next().hashCode());
        }
        Log.d(TAG, "zwm, set.equals(cloneSet): " + set.equals(cloneSet));
        Log.d(TAG, "zwm, set==cloneSet: " + (set==cloneSet));
    }

    public void testAddAll() {
        HashSet temp = new HashSet();
        temp.add("tempAAA");
        temp.add("tempBBB");
        temp.add("tempCCC");
        set.addAll(temp);
        Log.d(TAG, "zwm, addAll set: " + set);
    }

    public void testContainsAll() {
        HashSet temp = new HashSet();
        temp.add("tempAAA");
        temp.add("tempBBB");
        Log.d(TAG, "zwm, containsAll: " + set.containsAll(temp));
    }

    public void testRemoveAll() {
        HashSet temp = new HashSet();
        temp.add("tempAAA");
        temp.add("tempBBB");
        temp.add("tempDDD");
        Log.d(TAG, "zwm, removeAll: " + set.removeAll(temp));
        Log.d(TAG, "zwm, set: " + set);
    }

    public void testRetainAll() {
        HashSet temp = new HashSet();
        temp.add("Android");
        temp.add("Java");
        temp.add("DDD");
        Log.d(TAG, "zwm, retainAll: " + set.retainAll(temp));
        Log.d(TAG, "zwm, set: " + set);
    }

    public void testToArray() {
        //注意:以下注释的语句会抛异常java.lang.ClassCastException: java.lang.Object[] cannot be cast to java.lang.String[]
        //Java中的强制类型转换只是针对单个对象的,想要偷懒将整个数组转换成另外一种类型的数组是不行的
        //String[] arrays = (String[])set.toArray();
        Object[] arrays = set.toArray();
        for(Object item : arrays) {
            Log.d(TAG, "zwm, for item: " + item);
        }
    }

    public void testToArray2() {
        String[] param = new String[set.size()];
        String[] result = set.toArray(param);
        for(String item : param) {
            Log.d(TAG, "zwm, for param item: " + item);
        }
        for(String item : result) {
            Log.d(TAG, "zwm, for result item: " + item);
        }
    }
}

//测试代码
private void testMethod() {
    Log.d(TAG, "zwm, testMethod");
    TestHashSet testHashSet = new TestHashSet();
    testHashSet.testAdd();
    testHashSet.testRemove();
    testHashSet.testContains();
    testHashSet.testSize();
    testHashSet.testClear();
    testHashSet.testEmpty();
    testHashSet.testAdd();
    testHashSet.testAddAll();
    testHashSet.testIterator();
    testHashSet.testClone();
    testHashSet.testContainsAll();
    testHashSet.testRemoveAll();
    testHashSet.testRetainAll();
    testHashSet.testToArray();
    testHashSet.testToArray2();
}

//输出log
2019-07-31 10:36:23.647 zwm, testMethod
2019-07-31 10:36:23.650 zwm, add set: [Java, 数据结构与算法, 网络协议, Android]
2019-07-31 10:36:23.650 zwm, remove set: [Java, 数据结构与算法, Android]
2019-07-31 10:36:23.651 zwm, contains Java: true
2019-07-31 10:36:23.651 zwm, size: 3
2019-07-31 10:36:23.651 zwm, clear set: []
2019-07-31 10:36:23.651 zwm, isEmpty: true
2019-07-31 10:36:23.651 zwm, add set: [Java, 数据结构与算法, 网络协议, Android]
2019-07-31 10:36:23.652 zwm, addAll set: [Java, 数据结构与算法, 网络协议, tempAAA, tempBBB, Android, tempCCC]
2019-07-31 10:36:23.652 zwm, iterator item: Java
2019-07-31 10:36:23.652 zwm, iterator item: 数据结构与算法
2019-07-31 10:36:23.652 zwm, iterator item: 网络协议
2019-07-31 10:36:23.652 zwm, iterator item: tempAAA
2019-07-31 10:36:23.652 zwm, iterator item: tempBBB
2019-07-31 10:36:23.652 zwm, iterator item: Android
2019-07-31 10:36:23.653 zwm, iterator item: tempCCC
2019-07-31 10:36:23.653 zwm, set hashcode: 1030884738
2019-07-31 10:36:23.653 zwm, set item hashcode: 2301506
2019-07-31 10:36:23.653 zwm, set item hashcode: -788094787
2019-07-31 10:36:23.653 zwm, set item hashcode: 1002884394
2019-07-31 10:36:23.653 zwm, set item hashcode: -1428146227
2019-07-31 10:36:23.653 zwm, set item hashcode: -1428145234
2019-07-31 10:36:23.654 zwm, set item hashcode: 803262031
2019-07-31 10:36:23.654 zwm, set item hashcode: -1428144241
2019-07-31 10:36:23.654 zwm, cloneSet hashcode: 1030884738
2019-07-31 10:36:23.655 zwm, cloneSet item hashcode: 2301506
2019-07-31 10:36:23.655 zwm, cloneSet item hashcode: -788094787
2019-07-31 10:36:23.655 zwm, cloneSet item hashcode: 1002884394
2019-07-31 10:36:23.655 zwm, cloneSet item hashcode: -1428146227
2019-07-31 10:36:23.655 zwm, cloneSet item hashcode: -1428145234
2019-07-31 10:36:23.655 zwm, cloneSet item hashcode: 803262031
2019-07-31 10:36:23.655 zwm, cloneSet item hashcode: -1428144241
2019-07-31 10:36:23.656 zwm, set.equals(cloneSet): true
2019-07-31 10:36:23.656 zwm, set==cloneSet: false
2019-07-31 10:36:23.656 zwm, containsAll: true
2019-07-31 10:36:23.656 zwm, removeAll: true
2019-07-31 10:36:23.657 zwm, set: [Java, 数据结构与算法, 网络协议, Android, tempCCC]
2019-07-31 10:36:23.657 zwm, retainAll: true
2019-07-31 10:36:23.657 zwm, set: [Java, Android]
2019-07-31 10:36:23.657 zwm, for item: Java
2019-07-31 10:36:23.657 zwm, for item: Android
2019-07-31 10:36:23.657 zwm, for param item: Java
2019-07-31 10:36:23.658 zwm, for param item: Android
2019-07-31 10:36:23.658 zwm, for result item: Java
2019-07-31 10:36:23.658 zwm, for result item: Android

三、原理

重要参数

//底层使用HashMap来保存HashSet的元素
private transient HashMap map;

//由于Set只使用到了HashMap的key,所以此处定义一个静态的常量Object类,来充当HashMap的value
private static final Object PRESENT = new Object();

构造函数

//无参构造方法,使用HashMap的默认容量大小16和默认加载因子0.75初始化map,构造一个HashSet
public HashSet() {
    map = new HashMap<>();
}

//指定一个Collection参数构造HashSet,并将参数中的元素添加到HashSet中
public HashSet(Collection c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

//使用指定的初始容量大小和加载因子初始化map,构造一个HashSet
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

//使用指定的初始容量大小和默认的加载因子0.75初始化map,构造一个HashSet
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

//不对外公开的一个构造方法(默认default修饰),底层构造的是LinkedHashMap,dummy只是一个标示参数,只是为了区分其它构造方法,无具体意义
//实际上是提供给LinkedHashSet使用的
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

public boolean add(E e)

//添加一个元素到HashMap
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

public boolean remove(Object o)

//从HashMap中移除一个元素
public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

public boolean contains(Object o)

//判断HashMap中是否包含相应的对象
public boolean contains(Object o) {
    return map.containsKey(o);
}

public int size()

//返回HashMap的大小
public int size() {
    return map.size();
}

public void clear()

//清空HashMap
public void clear() {
    map.clear();
}

public boolean isEmpty()

//判断HashMap是否为空
public boolean isEmpty() {
    return map.isEmpty();
}

public Iterator iterator()

//先通过HashMap的keySet()方法得到key的Set集合,然后再获取该Set集合的迭代器对象
public Iterator iterator() {
    return map.keySet().iterator();
}

public Object clone()

//返回浅拷贝对象
public Object clone() {
    try {
        HashSet newSet = (HashSet) super.clone();
        newSet.map = (HashMap) map.clone();
        return newSet;
    } catch (CloneNotSupportedException e) {
        throw new InternalError(e);
    }
}

AbstractCollection public boolean addAll(Collection c)

//将给定的Collection对象的所有元素添加到HashSet中
public boolean addAll(Collection c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

AbstractCollection public boolean containsAll(Collection c)

//判断HashSet是否包含给定的Collection对象的所有元素
public boolean containsAll(Collection c) {
    for (Object e : c)
        if (!contains(e))
            return false;
    return true;
}

AbstractCollection public boolean removeAll(Collection c)

//在HashSet中移除给定的Collection对象的所有元素
public boolean removeAll(Collection c) {
    Objects.requireNonNull(c);
    boolean modified = false;
    Iterator it = iterator();
    while (it.hasNext()) {
        if (c.contains(it.next())) {
            it.remove();
            modified = true;
        }
    }
    return modified;
}

AbstractCollection public boolean retainAll(Collection c)

//在HashSet中保留给定的Collection对象的所有元素
public boolean retainAll(Collection c) {
    Objects.requireNonNull(c);
    boolean modified = false;
    Iterator it = iterator();
    while (it.hasNext()) {
        if (!c.contains(it.next())) {
            it.remove();
            modified = true;
        }
    }
    return modified;
}

AbstractCollection public Object[] toArray()

//将HashSet的所有元素保存到Object数组中,并返回该数组
public Object[] toArray() {
    // Estimate size of array; be prepared to see more or fewer elements
    Object[] r = new Object[size()];
    Iterator it = iterator();
    for (int i = 0; i < r.length; i++) {
        if (! it.hasNext()) // fewer elements than expected
            return Arrays.copyOf(r, i);
        r[i] = it.next();
    }
    return it.hasNext() ? finishToArray(r, it) : r;
}

AbstractCollection public T[] toArray(T[] a)

//将HashSet的所有元素保存到T类型的数组中,并返回该数组
public  T[] toArray(T[] a) {
    // Estimate size of array; be prepared to see more or fewer elements
    int size = size();
    T[] r = a.length >= size ? a :
              (T[])java.lang.reflect.Array
              .newInstance(a.getClass().getComponentType(), size);
    Iterator it = iterator();

    for (int i = 0; i < r.length; i++) {
        if (! it.hasNext()) { // fewer elements than expected
            if (a == r) {
                r[i] = null; // null-terminate
            } else if (a.length < i) {
                return Arrays.copyOf(r, i);
            } else {
                System.arraycopy(r, 0, a, 0, i);
                if (a.length > i) {
                    a[i] = null;
                }
            }
            return a;
        }
        r[i] = (T)it.next();
    }
    // more elements than expected
    return it.hasNext() ? finishToArray(r, it) : r;
}

四、主题

HashSet保存自定义对象

无重写hashCode及equals方法:

//TestObject
public class TestObject {
    public String name;
    public int age;

    public TestObject(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

//TestHashSet
public class TestHashSet {
    private static final String TAG = "TestHashSet";
    private HashSet  set = new HashSet<>();

    public void doSomething() {
        set.add(new TestObject("aaa", 1)); //注意此元素
        set.add(new TestObject("bbb", 2));
        set.add(new TestObject("ccc", 3));
        set.add(new TestObject("aaa", 1)); //注意此元素
        set.add(null);
        Log.d(TAG, "zwm, set size: " + set.size());
        Iterator iterator = set.iterator();
        while(iterator.hasNext()) {
            TestObject object = (TestObject)iterator.next();
            Log.d(TAG, "zwm, object: " + object);
            if(object != null) {
                Log.d(TAG, "zwm, item: name->" + object.name + ", age->" + object.age + ", hashcode->" + object.hashCode());
            }
        }
    }
}

//测试代码
private void testMethod() {
    Log.d(TAG, "zwm, testMethod");
    TestHashSet testHashSet = new TestHashSet();
    testHashSet.doSomething();
}

//输出log
2019-07-31 15:51:51.676 zwm, testMethod
2019-07-31 15:51:51.677 zwm, set size: 5
2019-07-31 15:51:51.678 zwm, item: name->aaa, age->1, hashcode->23649305 //注意此元素
2019-07-31 15:51:51.678 zwm, item: name->aaa, age->1, hashcode->268378508 //注意此元素
2019-07-31 15:51:51.678 zwm, item: name->bbb, age->2, hashcode->17532126
2019-07-31 15:51:51.679 zwm, item: name->ccc, age->3, hashcode->102196927

有重写hashCode及equals方法:

//TestObject
public class TestObject {
    public String name;
    public int age;

    public TestObject(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() { //重写hashCode方法
        int hash = 0;
        if(!TextUtils.isEmpty(name)) {
            hash += name.hashCode();
        }
        hash += age;
        return hash;
    }

    @Override
    public boolean equals(Object obj) { //重写equals方法
        if(obj == this) { //如果两个对象引用相同,则说明是同一个对象
            return true;
        }
        if(obj instanceof TestObject) {
            TestObject object = (TestObject)obj;
            if(TextUtils.equals(object.name, name) && object.age == age) {
                return true;
            }
        }
        return false;
    }
}

//TestHashSet
public class TestHashSet {
    private static final String TAG = "TestHashSet";
    private HashSet  set = new HashSet<>();

    public void doSomething() {
        set.add(new TestObject("aaa", 1)); //注意此元素
        set.add(new TestObject("bbb", 2));
        set.add(new TestObject("ccc", 3));
        set.add(new TestObject("aaa", 1)); //注意此元素
        set.add(null);
        Log.d(TAG, "zwm, set size: " + set.size());
        Iterator iterator = set.iterator();
        while(iterator.hasNext()) {
            TestObject object = (TestObject)iterator.next();
            if(object != null) {
                Log.d(TAG, "zwm, item: name->" + object.name + ", age->" + object.age + ", hashcode->" + object.hashCode());
            }
        }
    }
}

//测试代码
private void testMethod() {
    Log.d(TAG, "zwm, testMethod");
    TestHashSet testHashSet = new TestHashSet();
    testHashSet.doSomething();
}

//输出log
2019-07-31 16:10:26.405 zwm, testMethod
2019-07-31 16:10:26.409 zwm, set size: 4
2019-07-31 16:10:26.410 zwm, item: name->aaa, age->1, hashcode->96322 //注意此元素
2019-07-31 16:10:26.410 zwm, item: name->bbb, age->2, hashcode->97316
2019-07-31 16:10:26.410 zwm, item: name->ccc, age->3, hashcode->98310

LinkedHashSet

LinkedHashSet

你可能感兴趣的:(JDK源码--HashSet)