目录
集合类体系结构
HashSet
Hash值:哈希值是JDK根据对象的地址或者字符串或者数字计算出来的int类型的数值(object类中有一个方法可以获取对象的哈希值)
HashSet保证元素唯一性的源码分析:
首先进入HashSet的add方法,底层是调用了HashMap的put方法
点击进入put方法
再进入hash方法,可以看到该方法是计算key的hash值,然后再作为参数传递到putVal方法
点击进入putVal方法
简单地看下该方法的前半部分,首先第一个if判断哈希表是否为空,为空则初始化哈希表;如果不为空,则判断该哈希值在哈希表中对应数组位置是否为空,为空的话直接插入;如果不为空,则判断key值是否相同,相同的替换掉旧节点,不同则判断是否树化,树化就按树的方式进行存储,没有树化就按链表的方式进行存储,保证了元素的唯一性。
可以看出,HashSet的唯一性则是通过重写hashCode()方法和equal()方法实现的。如果插入的是String类型,Interger类型等包装类型时,可以明显地看出已经实现了唯一性,那是因为包装类的源码中已经重写了Object公共父类的hashCode()方法和equal()方法。(下面是String包装类的源码,已经重写了hashCode方法和equal方法)
所以如果自定义类要实现唯一性,必须重写Object公共父类的hashCode()方法和equal()方法
在自定义类中没有重写Object公共父类的hashCode()方法和equal()方法可以看到输出结果是没有去重的
package collection;
import java.util.HashSet;
public class Student {
private String id; //学号
private String name; //姓名
public Student(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
public static void main(String[] args) {
Student stu0 = new Student("222","张三");
Student stu1 = new Student("111","张三");
Student stu2 = new Student("111","张三");
HashSet<Student> hashSet = new HashSet<Student>();
hashSet.add(stu1);
hashSet.add(stu2);
hashSet.add(stu0);
for(Student student:hashSet) {
System.out.println(student.hashCode());
System.out.println(student);
}
}
}
再来看看在自定义类中重写Object公共父类的hashCode()方法和equal()方法的输出结果,显然是去重的
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return getId().equals(student.getId()) &&
getName().equals(student.getName());
}
@Override
public int hashCode() {
return Objects.hash(getId(), getName());
}
常见数据结构之哈希表:
JDK8之后,哈希表是由于“数组+链表+红黑树”实现的,数组用来存放哈希值,链表用来存放相应的元素,当元素增加一定程度的时候,链表会自动转换成红黑树,但元素减少到一定程度的时候,红黑树又会自动转换成链表。
TreeSet
TreeSet:元素有序,这里的有序不是指存储和取出的顺序,而是按照一定的规则进行排序
TreeSet():根据其元素的自然排序进行排序,需要实现Comparable接口,并覆盖其compareTo方法。
TreeSet(Comparator comparator):根据指定的比较器进行排序,则需要实现Comparator接口,并重写其compare方法。
先来看看TreeSet中添加String元素,不难发现已经实现了重排序和唯一性
TreeSet treeSet = new TreeSet();
treeSet.add("111");
treeSet.add("333");
treeSet.add("222");
treeSet.add("444");
treeSet.add("111");
System.out.println(treeSet);
那是因为String的源码中,实现了Comparable接口,并且重写了compareTo方法
那么自定义类要想实现这样的效果,同样可以通过需要实现Comparable接口,并覆盖其compareTo方法。
实现Comparable接口,并覆盖其compareTo方法
public class Customer implements Comparable{
private String name;
private int age;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/*@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Customer)) return false;
Customer customer = (Customer) o;
return getAge() == customer.getAge() &&
Objects.equals(getName(), customer.getName());
}*/
/*@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}*/
@Override
public int compareTo(Object o) {
Customer other = (Customer) o;
//先按照name属性排序
if (this.name.compareTo(other.getName())>0)
return 1;
if (this.name.compareTo(other.getName())<0)
return -1;
//再按照age属性排序
if (this.getAge()>((Customer) o).getAge())
return 1;
if (this.getAge()<((Customer) o).getAge())
return -1;
return 0;
}
public static void main(String[] args) {
TreeSet<Customer> cs = new TreeSet<>();
cs.add(new Customer("张三",19));
cs.add(new Customer("李四",15));
cs.add(new Customer("张三",13));
cs.add(new Customer("李四",12));
for(Customer c:cs){
System.out.println(c.name+" "+c.age);
}
}
}
实现Comparator接口,并覆盖其compare方法
public class CustomerComparator{
public static void main(String[] args) {
Set<Customer> set = new TreeSet<Customer>(new Comparator<Customer>() {
@Override
public int compare(Customer o1, Customer o2) {
if (o1.getName().compareTo(o2.getName())>0) return -1;
if (o1.getName().compareTo(o2.getName())<0) return 1;
if (o1.getAge()>o2.getAge())
return 1;
if (o1.getAge()<o2.getAge())
return -1;
return 0;
}
});
Customer customer1 = new Customer("Tom",5);
Customer customer2 = new Customer("Tom",9);
Customer customer3 = new Customer("Tom",2);
set.add(customer1);
set.add(customer2);
set.add(customer3);
Iterator<Customer> it = set.iterator();
while(it.hasNext()){
Customer customer = it.next();
System.out.println(customer.getName()+" "+customer.getAge());
}
}
}
注:本文实属原创,如有错误,请指出。如对你有帮助,不妨点个赞呗!