HashSet
此类实现Set接口,底层哈希表实现,本质就是HashMap的实例(对象)。存取的顺序不一致且唯一:特别是它不保证该顺序恒久不变,没有什么特殊的方法,都是从Set接口中实现的。
public static void main(String[] args){
HashSet<String> set = new HashSet<>();
set.add("xxx");
set.add("abc");
set.add("abc");
System.out.println(set);
}
LinkedHashSet
现实开发的时候对数据进行去重的同时还要保证原来的顺序HashSet做不到,需要子类LinkedHashSet完成
LinkedHashSet属于HashSet的子类,本质还是无序集合但是效果变得有序了。
LinkedHashSet:有序且唯一
import java.util.LinkedHashSet;
public class Demo02 {
public static void main(String[] args) {
LinkedHashSet<String> ls = new LinkedHashSet<>();
ls.add("张三");
ls.add("李四");
ls.add("王五");
ls.add("赵六");
ls.add("张三");
for (String l : ls) {
System.out.println(l);
}
}
}
案例:
键盘输入一个字符串,需要把字符串中的字符打印出来【要求重复的字母只打印一次同时保证原有字母的顺序】
import java.util.LinkedHashSet;
import java.util.Scanner;
public class Demo03 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
LinkedHashSet<Character> ls = new LinkedHashSet<>();
String s =sc.next();
for (int i = 0; i < s.length(); i++) {
ls.add(s.charAt(i));
}
for (Character l : ls) {
System.out.println(l);
}
}
}
TreeSet的自然排序和比较器排序
1、TreeSet集合的概述
TreeSet是Set集合的常用实现类,使用的时候自动对集合内部的元素进行排序 3 2 1 ---> 1 2 3
使用和两个构造方法的关系
TreeSet():默认升序排序(自然排序)
TreeSet(Comprator c):按照指定的排序原则进行排序(比较器排序)
2、自然排序
前提:
要求集合添加的元素对应的类必须实现Comparable接口重写compareTo方法
指定返回值的时候:
o.属性 - this.属性 是降序排序
this.属性 - o.属性 是升序排序
代码:
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(){}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
//return this.age < o.age ? -1 : (this.age == o.age ? 0 :1);
//当前对象在前参数对象在后TreeSet排序结果就是升序
//return this.age - o.age;
//当前对象在后,参数对象在前 TreeSet排序结果就是降序
return o.age - this.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 String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.TreeSet;
public class Demo04 {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(100);
set.add(200);
set.add(-40);
set.add(60);
System.out.println(set);
TreeSet<Person> ps = new TreeSet<Person>();
ps.add(new Person("lisd",28));
ps.add(new Person("dfd",20));
ps.add(new Person("dsdf",18));
ps.add(new Person("lqw",27));
System.out.println(ps);
}
}
结论:
存放的元素时jdk提供的类对象一般默认升序排序
存放的元素时自定义的类对象按现实的Comparable的compareTo指定原则来排序
3、比较器排序
创建TreeSet集合对象的时候传入一个Comparable比较器对象
重写compare方法指定排序原则
代码:
package com.offcn.demo02;
public class Monkey {
private String name;
private int age;
public Monkey(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 String toString() {
return "Monkey{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.offcn.demo02;
import java.util.Comparator;
import java.util.TreeSet;
public class Demo01 {
public static void main(String[] args) {
Comparator<Monkey> com = new Comparator<Monkey>() {
@Override
public int compare(Monkey o1,Monkey o2) {
if((o1.getAge() - o2.getAge())==0){
return o1.getName().compareTo(o2.getName());
}else {
return o1.getAge() - o2.getAge();
}
}
};
TreeSet<Monkey> set = new TreeSet<>(com);
set.add(new Monkey("a",10));
set.add(new Monkey("b",8));
set.add(new Monkey("c",8));
set.add(new Monkey("d",4));
System.out.println(set);
Comparator<String> com1 = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
};
TreeSet<String> set1 = new TreeSet<>(com1);
set1.add("4");
set1.add("8");
set1.add("6");
set1.add("9");
set1.add("3");
System.out.println(set1);
}
}
HashSet集合的元素唯一性原理分析
1、哈希值
哈希值简介:
是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值
如何获取哈希值
Object类中的public int hashCode();返回对象的哈希码值
哈希值的特点
不同对象获取的哈希值尽可能不一样
默认情况下,同一类的不同对象的哈希值是不同的,但是重写hashCode()方法相同属性值得不同对象哈希值相同
new Person("list",18);
new Person("list",18);
不重写hashCode方法,两个对象的地址不同,因此哈希值也不同
一旦重写hashCode方法,两个对象属性值一样,那么要做到他们哈希值也要一样
2、哈希表结构
jdk1.8之前HashSet底层数据结构
哈希表结构:数组 + 链表
HashSet默认创建之后会自带长度为16个数组,HashSet还有属性加载因子:0.75(当HashSet元素个数超过16*0.75=12之后,扩容)
JDK1.8以后HashSet底层结构
哈希表:数组+链表
数组+红黑树
规定:
①链表节点个数少于等于8个
数组+链表
②节点个数多于8个或者数组的容量大于64是
数组+红黑树
jdk1.7中HashSet原理分析(元素唯一性)
存储过程描述:
①添加元素的时候调用hashCode方法得到要添加的哈希值
②通过哈希值得到数组的索引值
③判断该位置是否有元素
没有元素:直接添加
有元素:
比较与这个位置下所有元素的哈希值是否有相同的,没有直接添加
哈希值有相同的,调用equals方法和位置上的所有哈希值相同元素进行比较
属性值相同 不添加
属性值不相同 添加到该位置的链表节点上
如何保证HashSet集合的元素唯一:
添加元素对应的类必须重写hashCode和equals方法
注意:
1.7版本哈希表初始数组长度是16
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
System.out.println("equals执行了!");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
System.out.println("执行了!");
return Objects.hash(name, age);
}
}
import java.util.LinkedHashSet;
public class Demo01 {
public static void main(String[] args) {
LinkedHashSet<Student> set = new LinkedHashSet<>();
set.add(new Student("tom",18));//第一次添加只调用HashCode方法
System.out.println("==============================");
set.add(new Student("tom",18));
}
}
jdk1.8中HashSet原理分析
存储过程描述:
①添加元素的时候调用hashCode方法得到要添加的元素的哈希值
②通过哈希值得到数组的索引值
③判断该位置是否有元素
没有元素:直接添加
有元素:
判断是不是链表的节点
判断链表的长度是不是大于8同时判断数组的长度是不是大于64
比较哈希值是否一样,哈希值一样,如果没有一样的。
链表的长度小于8,添加到链表节点上
链表的长度大于8但是数组的长度小于64
数组扩容 树化
链表的长度大于等于8,数组的长度也大于64
直接树化
哈希值有一样,调用equals方法和位置上的所有元素进行比较
属性值相同 不添加
属性值不相同 :添加元素
链表的长度小于8:添加到链表节点上
链表的长度大于8,但是数组的长度小于64
数组扩容 树化
链表的长度大于8, 数组的长度也大于64
直接树化
如何保证HashSet集合的元素唯一:
添加元素对应的类必须重写hashCode和equals方法