Set系列集合特点
- 无序:存取顺序不一致
- 不重复:可以去除重复
- 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
Set集合实现类特点:
- Hashset:无序、不重复、无索引。
- LinkedHashset:有序、不重复、无索引。
- Treeset:排序、不重复、无索引。
Set集合的工能基本上与Collection的API一致。
哈希值
- 是DK根据对象的地址,按照某种规则算出来的int类型的数值。
Object类的API
- public int hashCode(): 返回对象的哈希值
对象的哈希值特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。
代码演示如下:
public class SetDemo2 { public static void main(String[] args) { //目标:学会获取对象的哈希值,并确认一下 String name = "itBirdBIrd"; System.out.println(name.hashCode()); System.out.println(name.hashCode()); String name1 = "itBirdBIrd1"; System.out.println(name.hashCode()); System.out.println(name.hashCode()); } }
JDK1.8版本开始Hashset原理解析
- 底层结构:哈希表(数组、链表、红黑树的结合体)
- 当挂在元素下面的数据过多时,查询性能降低,从DK8开始后,当链表长度超过8的时候,自动转换为红黑树。
- 结论:JDK8开始后,哈希表对于红黑树的引入进一步提高了操作数据的性能。
代码演示如下:
学生类:
public class Student { private String name; private int age; private char sex; public Student() { } public Student(String name, int age, char sex) { this.name = name; this.age = age; this.sex = sex; } 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; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && sex == student.sex && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(name, age, sex); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", sex=" + sex + '}'; } }
测试类:
public class SetDemo3 { public static void main(String[] args) { Set<Student> sets = new HashSet<>(); Student s1 = new Student("鸟儿",21,'男'); Student s2 = new Student("鸟儿",21,'男'); Student s3 = new Student("大鸟儿",21,'男'); sets.add(s1); sets.add(s2); sets.add(s3); System.out.println(sets); } }
运行结果如图:
由运行结果可以看出,s1和s2一样,所以只存了一个。
LinkHashSet集合概述和特点
- 有序、不重复、无索引。
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
TreeSet集合概述和特点
- 不重复、无索引、可排序
- 可排序:按照元素的大小默认升序(有小到大)排序。
- Treeset集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
- 注意:TreeSet集合是一定要排序的,可以将元素按照指定规则进行排序。
TreeSet集合默认的规则
- 对于数值类型:Integer,Double,官方默认按照大小进行升序排序。
- 对于字符串类型:默认按照首字符的编号升序排序。
- 对于自定义类型如Student对象,Treeset无法直接排序。
- 结论:想要使用TreeSet存储自定义类型,需要制定排序规则。
代码演示如下:
/** 目标:观察TreeSet对于有值特性的数据如何排序。 学会对自定义类型的对象进行指定规则排序。 */ public class SetDemo5 { public static void main(String[] args) { Set<Integer> sets = new TreeSet<>();// 不重复 无索引 可排序 sets.add(23); sets.add(24); sets.add(22); sets.add(8); System.out.println(sets);// [8, 22, 23, 24] Set<String> sets1 = new TreeSet<>();// 不重复 无索引 可排序 sets1.add("Java"); sets1.add("Java"); sets1.add("About"); sets1.add("Python"); sets1.add("UI"); sets1.add("UI"); System.out.println(sets1);// [About, Java, Python, UI] System.out.println("------------------"); //方式二:集合自带比较器对象进行规则定制 Set<Apple> apples = new TreeSet<>(new Comparator<Apple>() { @Override public int compare(Apple o1, Apple o2) { return o1.getWeight() - o2.getWeight() >=0 ? 1 : -1; // 升序且保留重复元素 } }); //方式一:类自定义比较规则 // Set<Apple> apples = new TreeSet<>(); apples.add(new Apple("红富士","红色",9.9,500)); apples.add(new Apple("青苹果","绿色",15.9,300)); apples.add(new Apple("花牛","深红色",29.9,400)); apples.add(new Apple("黄苹果","黄色",9.8,500)); System.out.println(apples); } }
Apple类:
public class Apple implements Comparable<Apple>{ private String name; private String color; private double price; private int weight; public Apple() { } public Apple(String name, String color, double price, int weight) { this.name = name; this.color = color; this.price = price; this.weight = weight; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } @Override public String toString() { return "Apple{" + "name='" + name + '\'' + ", color='" + color + '\'' + ", price=" + price + ", weight=" + weight + '}'; } /** 方式一:类自定义比较规则 01.compareTo(o2) * @param o * @return */ @Override public int compareTo(Apple o) { //按照重量比较的 return this.weight - o.weight; // 去重量重复的元素 //return this.weight - o.weight >= 0 ? 1 : -1; // 保留重量重复的元素 } }
两种方式中,关于返回值的规则:
- 如果认为第一个元素大于第二个元素返回正整数即可。
- 如果认为第一个元素小于第二个元素返回负整数即可。
- 如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
- 注意:如果Treeset集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序。
总结:
1. 如果希望元素可以重复,又有索引,索引查询要快?
- 用ArrayList集合,基于数组的。(用的最多)
2.如果希望元素可以重复,又有索引,增删首尾操作快?
- 用LinkedList集合,基于链表的。
3.如果希望增删改查都快,但是元素不重复、无序、无索引。
- 用Hashset集合,基于哈希表的。
4.如果希望增删改查都快,但是元素不重复、有序、无素引。
- 用LinkedHashset集合,基于哈希表和双链表。
5.如果要对对象进行排序。
- 用Treeset集合,基于红黑树。后续也可以用List集合实现排序。