1.Set集合
在集合中没有重复元素,通过equals()方法进行判断
2.HashSet
- 保证元素唯一性的原理:
当使用add 方法时,会先调用hashCode()方法获得一个哈希值,通过为每个对象分配哈希值,如果没有哈希值相同的对象,则存进集合,当哈希值相同时,就对对象中的属性通过equals比较,如果对象相同则不存储,如果对象不同则存储
(1)底层由哈希表实现
(2)实现了Set接口
(3)存取顺序不一致
HashSet<String> hashSet = new HashSet<>();
hashSet.add("a");
hashSet.add("a");//不会添加重复元素
hashSet.add("b");
hashSet.add("c");
hashSet.add("d");
System.out.print(hashSet); //存取的顺序不一致
存储自定义引用数据类型时
如果想达到不重复的效果,必须要重写equals和hashCode方法,在Idea中可以使用快捷键自动生成:右键->Generate->equals()和hashCode()方法可以自动生成
HashSet<Student> hashSet = new HashSet<>();
hashSet.add(new Student("张三",23));
hashSet.add(new Student("张三",23));
hashSet.add(new Student("李四",24));
hashSet.add(new Student("张三",23));
hashSet.add(new Student("李四",24));
for (Student student:hashSet) {
System.out.println(student.getName() + " " + student.getAge());
}
}
}
class Student {
private String name;
private int age;
public Student(String name,int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@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 &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
- LinkedHashSet
(1)元素不重复
(2)Set实现类中唯一保证存取顺序一致的集合
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("a");
linkedHashSet.add("a");
linkedHashSet.add("b");
linkedHashSet.add("c");
linkedHashSet.add("d");
linkedHashSet.add("b");
System.out.println(linkedHashSet);
}
查看hashCode()源码
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
采用数值31相乘的原因:
(1)31为质数,当属性不同时,产生相同的哈希值的几率较小
(2)31数值不算不太大,在正常情况下不会超出int 的范围,31也不算太小,当属性值不同时,会尽可能的去避免产生相同的哈希值
(3)31为2的5次方减1,计算机运算过程不会相对复杂
当对数据的存放顺序没有要求时,使用HashSet,因为其效率更高
3.TreeSet
(1)保证元素不重复
(2)可以给元素进行排序
当存储字符串时由于String 类中重写了compareTo()方法,在存储时会自动按照字典顺序进行存储
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("b");
treeSet.add("d");
treeSet.add("a");
treeSet.add("b");
treeSet.add("f");
treeSet.add("e");
treeSet.add("c");
System.out.println(treeSet);
当存储自定义对象时,必须实现comparable接口根据需求重写compareTo(),然后才能在TreeSet中存储元素
- TreeSet根据compareTo()方法进行存储,用二叉树实现
(1)compare To()返回值为0时,则不存储
(2)compareTo()方法返回值为正数时存储在右边
(3)compareTo()方法返回值为负数时存储在左边
TreeSet<Student> treeSet = new TreeSet<>();
treeSet.add(new Student("张三",23));
treeSet.add(new Student("李四",24));
treeSet.add(new Student("王五",16));
for (Student s:treeSet
) {
System.out.println(s.getName() + " " + s.getAge());
}
}
}
class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name,int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public int compareTo(Student o) {
int num = this.age - o.age;
return num == 0 ? this.name.compareTo(o.name):num;
}
}
比较器实现比较原理
如果我们在存储字符串时,希望根据字符串的长度去存储,也需要定义类实现comparable接口根据需求重写compareTo()方法。
TreeSet<String> treeSet = new TreeSet<>(new Compare());
treeSet.add("aaaaaa");
treeSet.add("d");
treeSet.add("aop");
treeSet.add("app");
treeSet.add("uidd");
treeSet.add("fnjri");
System.out.println(treeSet);
}
}
class Compare implements Comparator<String>{
@Override
public int compare(String s, String s1) {
int num = s.length() - s1.length();
return num == 0 ? s.compareTo(s1):num;
}
}
查看Comparator的API
但是会默认继承Objects类,此类中重写了equals()方法,所以在我们自定义时不用再次重写