一、HashSet类的介绍
HashSet使用的是相当复杂的方式来存储元素的,使用HashSet能够最快的获取集合中的元素,效率非常高(以空间换时间)。它会根据hashcode和equals来庞端是否是同一个对象,如果hashcode一样,并且equals返回true,则是同一个对象,不能重复存放。
1.1:构造函数
HashSet()
构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
HashSet(Collection<? extends E> c)
构造一个包含指定 collection 中的元素的新 set。
HashSet(int initialCapacity)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)
构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。 放
1.2:方法
1.equals()方法
用来实现Set中元素的不重复性,如果不覆盖(override)equals()方法,默认使用父类Object的equals方法,则只是比较对象的引用是否相同。
2.hashCode()
hashCode()方法时为了实现HashSet和LinkedHashSet而实现的。只有知道对象的hash值,才能根据这个hash值确定 存放在散列表的槽的index。同样,如果不覆盖(override)hashCode()方法,默认使用父类Object的hashCode()方法。
3.toString()方法
toString()方法在打印对象时会调用。如果不覆盖(override)toString()方法,默认使用父类Object的。
4.compareTo()方法
用户类要实现Comparable接口。这个方法主要用于将对象存放在TreeSet()时保证顺序的。由于是接口,所以用户类必须要实现这个方法。
1.3:继承关系
|--HashSet 底层是由HashMap实现的,通过对象的hashCode方法与equals方法来保证插入元素的唯一性,无序(存储顺序和取出顺序不一致),。
|--LinkedHashSet 底层数据结构由哈希表和链表组成。哈希表保证元素的唯一性,链表保证元素有序。(存储和取出是一致)
1.4:实现原理
(1)往Haset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,然后通过元素 的哈希值经过移位等运算,就可以算出该元素在哈希表中的存储位置。见下面2种情况:
情况1: 如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。
情况2: 如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素运行 添加。
class Person {
int id;
String name;
public Person(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override public String toString() {
return "{ 编号:" + this.id + " 姓名:" + this.name + "}";
}
@Override public int hashCode() {
System.out.println("=======hashCode=====");
return this.id;
}
@Override public boolean equals(Object obj) {
System.out.println("======equals======");
Person p = (Person) obj;
return this.id == p.id;
}
}
public class Demo2 {
public static void main(String[] args) {
/*
HashSet set = new HashSet();
set.add("狗娃");
set.add("狗剩");
set.add("铁蛋");
System.out.println("集合的元素:"+ set);
*/
HashSet set = new HashSet();
set.add(new Person(110, "狗娃"));
set.add(new Person(220, "狗剩"));
set.add(new Person(330, "铁蛋"));
//在现实生活中只要编号一致就为同一个人.
System.out.println("添加成功吗?" + set.add(new Person(110, "狗娃")));
System.out.println("集合的元素:" + set);
}
}
运行结果:
=======hashCode=====
=======hashCode=====
=======hashCode=====
=======hashCode=====
======equals======
添加成功吗?false
集合的元素:[{ 编号:220 姓名:狗剩}, { 编号:110 姓名:狗娃}, { 编号:330 姓名:铁蛋}]
1.5:总结
(1)HashSet是Set接口的实现。HashSet按Hash算法来存储集合中的元素,具有很好的存取和查找性能。
(2)HashSet不是同步的,多个线程访问是需要通过代码保证同步
(3)HashSet集合元素值可以使null。
(4)HashSet不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
(5)当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值决定该对象在HashSet中的存储位置。如果有两个元素通过equals()方法比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。即,HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。
二、HashSet去重4种方法
2.1:LinkedHashSet去重
去重后保持原有顺序(重复数据只保留一条)
String[] arr = new String[] {"i", "think", "i", "am", "the", "best"};
Collection<string> noDups = new LinkedHashSet<string>(Arrays.asList(arr));
System.out.println("(LinkedHashSet) distinct words: " + noDups);
2.2:HashSet去重方法一
去重后顺序打乱(重复数据只保留一条)
String[] arr = new String[] {"i", "think", "i", "am", "the", "best"};
Collection<string> noDups = new HashSet<string>(Arrays.asList(arr));
System.out.println("(HashSet) distinct words: " + noDups);
2.3:HashSet去重方法二
去重后顺序打乱(重复数据只保留一条)
String[] arr = new String[] {"i", "think", "i", "am", "the", "best"};
Set<string> s = new HashSet<string>();
for (String a : arr)
{
if (!s.add(a))
{
System.out.println("Duplicate detected: " + a);
}
}
System.out.println(s.size() + " distinct words: " + s);
2.4:HashSet去重方法三
去重后顺序打乱(相同的数据一条都不保留,取唯一)
String[] arr = new String[] {"i", "think", "i", "am", "the", "best"};
Set<string> uniques = new HashSet<string>();
Set<string> dups = new HashSet<string>();
for (String a : arr)
{
{
if (!uniques.add(a))
dups.add(a);
}
}
uniques.removeAll(dups);
System.out.println("Unique words: " + uniques);
System.out.println("Duplicate words: " + dups);