Set集合,先从HashSet学起。
1.HashSet底层实际上是一个HashMap,HashMap底层采用了哈希表数据结构。
2.哈希表又叫做散列表,哈希表底层是一个数组,这个数组中每一个元素是一个单向链表,每一个单向链表都有一个独一无二的hash值,代表数组的下标。在某个单向链表中的每一节点上的hash值是相等的,hash值实际上是key调用hashCode方法,再通过“hash function”转换成的值。
3.如何向哈希表中添加元素:
先调用被存储的key的hashCode方法,经过某个算法得出hash值,如果在这个哈希表中不存在这个hash值,则直接加入元素。如果该hash值已经存在,继续调用key之间的equals方法,如果equals方法返回false,则将该元素添加,如果equals方法返回true,则放弃添加该元素。
4.HashSet其实是HashMap中的key部分,HashSet有什么特点,HashMap中的key应该有相同的特点。
5.HashMap和HashSet的初试化容量都是16,默认加载因子是0.75.
结合下面这个程序来看。
import java.util.*;
public class HashSetTest01{
public static void main(String[] args){
//创建Set集合
Set s=new HashSet();
//添加元素
s.add(1);
s.add(1);
s.add(23);
s.add(3);
s.add(10);
Iterator it=s.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
编译运行后输出:
1
3
23
10
上述代码中,首先创建一个Set集合s,然后用add()方法向其中添加元素,之后使用迭代器对其进行迭代并且输出其中的元素。从编译运行输出的结果我们可以看出,Set集合是无序并且不可重复的。
接下来看看看关于往Set集合中存储的元素,该元素的hashCode和equals方法。结合以下代码来看:
import java.util.*;
public class HashSetTest02{
public static void main(String[] args){
//创建Set集合
Set es=new HashSet();
//创建Employee类型的对象
Employee e1=new Employee("1000","JACK");
Employee e2=new Employee("1000","JACK");
Employee e3=new Employee("2000","JSDD");
Employee e4=new Employee("2001","CJS");
Employee e5=new Employee("3000","sdc");
Employee e6=new Employee("3001","mdwkm");
//Set集合中添加元素
es.add(e1);
es.add(e2);
es.add(e3);
es.add(e4);
es.add(e5);
es.add(e6);
//输出Set集合元素的个数
System.out.println(es.size());
//查看e1,e2的hashCode()方法
System.out.println(e1.hashCode());
System.out.println(e2.hashCode());
}
}
//根据现实的业务逻辑得知,该公司的员工编号是:1000-9999
class Employee{
String no;
String name;
Employee(String no,String name){
this.no=no;
this.name=name;
}
}
编译运行后输出:
6
366712642
1829164700
上述代码中:
1.首先创建一个Set集合es,接着创建六个Employee类型的对象,e1和e2的工号和名字相同,从编译运行后的结果可以看出,e1和e2都被添加到Set集合当中去了,在现实中出于实际需要的考虑,e2不应该被添加进去,因此必须重写Object中的hashCode()方法(Object中的方法一般都是让我们根据现实业务的需要重写的)。
2.重写hashCode()方法有固定的技巧,在上面代码实例中,以员工编号分组重写hashCode()方法,使其散列均匀分布。
public int hashCode(){
return no.hashCode();
}
3.当hashCode方法返回的值不同时,可以直接往集合中添加元素。当hashCode方法返回的值相同时,需要调用equals()方法,这里需要我们重写equals()方法。
public boolean equals(Object o){
if(this==o){
return true;
}
if(o instanceof Employee){
Employee e=(Employee)o;
if(e.no.equals(this.no) && e.name.equals(this.name)){
return true;
}
}
return false;
}
整个的代码如下所示:
import java.util.*;
public class HashSetTest02{
public static void main(String[] args){
//创建Set集合
Set es=new HashSet();
//创建Employee类型的对象
Employee e1=new Employee("1000","JACK");
Employee e2=new Employee("1000","JACK");
Employee e3=new Employee("2000","JSDD");
Employee e4=new Employee("2001","CJS");
Employee e5=new Employee("3000","sdc");
Employee e6=new Employee("3001","mdwkm");
//Set集合中添加元素
es.add(e1);
es.add(e2);
es.add(e3);
es.add(e4);
es.add(e5);
es.add(e6);
//输出Set集合元素的个数
System.out.println(es.size());
//查看e1,e2的hashCode()方法
System.out.println(e1.hashCode());
System.out.println(e2.hashCode());
}
}
class Employee{
String no;
String name;
Employee(String no,String name){
this.no=no;
this.name=name;
}
//重写equals方法
//如果员工编号相同,并且名字相同,则是同一个对象
public boolean equals(Object o){
if(this==o){
return true;
}
if(o instanceof Employee){
Employee e=(Employee)o;
if(e.no.equals(this.no) && e.name.equals(this.name)){
return true;
}
}
return false;
}
//重写hashCode方法
public int hashCode(){
return no.hashCode();
}
}
重写hashCode和equals方法之后,编译运行输出:
5
1507423
1507423
从上面的结果可以看出,当出现相同的员工编号和姓名时,只会添加进去一个对象,实现了现实业务的需求。