一、概念
类定义:
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 extends E> 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 extends E> c)
//将给定的Collection对象的所有元素添加到HashSet中
public boolean addAll(Collection extends E> 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