1.集合的分类:如下图
2.HashSet集合的概述和特点
● 无序(不代表乱序)
●不可重复(不能存储重复元素),
●没有索引,不能用普通的for进行遍历,但是能用迭代器或者增强for循环进行遍历
● 底层采用数组+链表+红黑树的结构形式来存储数据,其实就是用HashMap的key键来存储数据,在创建HashSet对象的时候就会创建一个HashMap来存储数据(如下图),所以其采用HashMap存储数
public HashSet() { map = new HashMap<>(); }
● 扩容机制:
① 当底层数组长度大于等于阈值的时候(阈值也就是数组长度*负载因子),扩容为原来的2倍。
② 当链表长度大于等于8的时候,并且数组长度小于64。
注意:(如果链表长度大于8,并且数组长度大于等于64该链表会变成红黑树)
3.LinkedHashSet集合
LinkedHashSet是HashSet的子类,其底层用的是LinkedHashMap,LinkedHashSet在HashSet的基础上增加了一个双向链表来维护元素的插入顺序。这意味着元素的迭代顺序与它们被添加到集合中的顺序相同。因此,LinkedHashSet是有序的。
4.TreeSet集合。
●结构:TreeSet是采用红黑树进行存储数据。
●不可重复性:能够和HashSet一样去重●排序:能够在存放数据的时候对数据排序,相较于HashSet,TreeSet是有序的,能够对对元素按照一定的规则(可以自定义规则)进行排序。
●注意:想要使用TreeSet添加数据,那么添加的那个对象的类需要实现Comparable接口且实现compareTo方法,如果不想实现Comparable接口,
可以在创建TreeSet集合的时候传入该类的比较器(comparator)
演示如下:①类实现Comparable接口并且重写compareTo方法代码演示
实体类
public class Teacher implements Comparable<Teacher>{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Teacher{" + "name='" + name + '\'' + ", age=" + age + '}'; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Teacher() { } public Teacher(String name, int age) { this.name = name; this.age = age; } @Override public int compareTo(Teacher o) { //年龄相同按照名字进行排序 if (this.age==o.age){ return this.name.compareTo(o.name); } //年龄不同按照年龄进行升序排序 return this.age-o.age; } }
测试类
import java.util.*; public class test { public static void main(String[] args) { TreeSet<Teacher> set = new TreeSet<>(); Teacher teacher1 = new Teacher("b", 2); Teacher teacher2 = new Teacher("a", 1); Teacher teacher3 = new Teacher("b", 1); set.add(teacher1); set.add(teacher2); set.add(teacher3); for (Teacher teacher:set){ System.out.println(teacher.toString()); } } }
测试结果:
②在创建TreeSet集合的时候传入该类比较器作为参数实现TreeSet排序功能 代码演示
实体类
public class Teacher { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Teacher{" + "name='" + name + '\'' + ", age=" + age + '}'; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Teacher() { } public Teacher(String name, int age) { this.name = name; this.age = age; } }
测试类
import java.util.*; public class test { public static void main(String[] args) { //Teacher类的比较器,与Teacher类实现Comparable接口并重写compareTo方法效果一致 Comparator<Teacher> comparator = new Comparator<Teacher>() { @Override public int compare(Teacher o1, Teacher o2) { if (o1.getAge()==o2.getAge()){ return o1.getName().compareTo(o2.getName()); } return o1.getAge()-o2.getAge(); } }; TreeSet<Teacher> set = new TreeSet<>(comparator); Teacher teacher1 = new Teacher("b", 2); Teacher teacher2 = new Teacher("a", 1); Teacher teacher3 = new Teacher("b", 1); set.add(teacher1); set.add(teacher2); set.add(teacher3); for (Teacher teacher:set){ System.out.println(teacher.toString()); } } }
测试结果
注意:实现Comparable接口重写的compareTo方法和比较器重写的compare方法的返回值代表什么?
返回值为0 新的元素和旧的元素相同 新的元素不进行添加
返回值为>0 将新的元素方法放在旧的元素右边的子节点上
返回值为<0 将新的元素方法放在旧的元素左边的子节点上
5.常见面试题
①hashCode相等,equals一定相等吗?
答:不一定。在java中,hashcode方法是用来获取对象的哈希码,而equals是用来比较两个对象是否相等。但是在某些特殊情况下,可能会出现不同的对象拥有相同的哈希码,这种情况就被我们称之为hash冲突。所以hashcode相等也会出现equals不相等的情况
②equals相等,hashcode一定相等吗?
答:一定,因为当两个对象用equals作比较之后,如果返回true,那么他们的哈希码也一定会相等。比如HashSet和HashMap集合在添加元素的时候,元素的位置就是考哈希码来决定的,如果两个相等的对象拥有不同的哈希码,那么在集合操作中就会出现问题,比如去重失败等等。
③3、既然hashset基于hashmap实现,你说一下 hashset的add方法中,为什么要在map.put的val上放上一个object类型的静态常量PRESENT?
答:hashset的add方法默认的调用的map的put方法,如果put方法添加数据成功,那么put方法就会返回null,添加失败,则会返回对应的PRESENT的值,如果PRESENT的值为null,那么我们则无法判断是否添加数据成功