Set接口
Set接口:
- 存储无序的
- 不可重复的数据
HashSet:
作为Set接口的主要实现类;
- 线程不安全的
- 可以存储null值
LinkedHashSet:
作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历 对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
TreeSet:
可以按照添加对象的指定属性,进行排序。
Set:存储无序的、不可重复的数据
1.无序性:
- 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
2. 不可重复性:
保证添加的元素按照 equals() 判断时,不能返回true.即:相同的元素只能添加一个。
要求:
- 向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
- 要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
重写的hashCode()
重写的equals()
案例: User对象 重写hashCode()和equals()
实现 Comparable
public class User implements Comparable{
private String name;
private int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
System.out.println("User equals()....");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() { //return name.hashCode() + age;
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
31最为系数,设置目的是减少哈希冲突.而且31是个素数且只占用5bit 造成数据溢出的概率很小
添加元素的过程:以HashSet为例:
HashSet底层结构
数组+链表结构
数组初始容量为16 扩展因子0.75
HashSet 底层 有个 HashMap
LinkedHashSet
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
优点:
对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet
概念:
- 向TreeSet中添加的数据,要求是相同类的对象。
- 两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator)
- 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
- 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().
结构:
自然排序:java.lang.Comparable
- Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo(T t)方法被称为它的自然比较方法。当前对象this与指定对象t比较“大小”,如果当前对象this大于指定对象t,则返回正整数,如果当前对象this小于指定对象t,则返回负整数,如果当前对象this等于指定对象t,则返回零。
- 实现Comparable接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparable的典型实现:
- String:按照字符串中字符的Unicode值进行比较
- Character:按照字符的Unicode值来进行比较
- 数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值大小进行比较
- Date、Time等:后面的日期时间比前面的日期时间大
定制排序:java.util.Compartor
- 强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
- 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序。
经典试题
其中Person 类 重写了 hashCode 和 equal()方法
public class Person {
int id;
String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (id != person.id) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
}
开始
set.remove(p1);
解析:
- person(1001.cc)是存在 开始 penson(1001 ,aa)哈希运算后的位置上的.所以remove操作,是根据person(1001.cc)哈希运算去删除,找不到相对应的元素.
set.add(new Person(1001,“CC”));
解析:
- 第一个person(1001.cc)是存在 开始 penson(1001 ,aa)哈希运算后的位置上的.
- 第二个person(1001.cc)是存在 本身 penson(1001 ,aa)哈希运算的位置上的.
最后结果 是四个值
- Person(1001,“cc”)
- Person(1002,“BB”)
- Person(1001,“cc”)
- Person(1001,“AA”)