前沿
Set接口
Set 接口概述
Set接口是Collection的子接口,set接口没有提供额外的方法 ,存储无顺序的,不可重复的数据---->高中的集合
- HashSet: 作为Set接口的主要实现类,线程不安全,可以储存null值
- LinkedHashSet: HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
- TreeSet:可以按照添加的元素,进行排序。
Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。
Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法
Set:
- 无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是通过数据的hash值来判断
- 不可重复性:保证添加相同元素按照equals判断时不能返回true
- 添加数据的过程:以HashSet为例:
下面介绍Set的几个实现类
实现类1:HashSet
实现类之一:HashSet
Collection子接口之二:Set接口 Set实现类之一:HashSet
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除 性能。
HashSet 具有以下特点:
不能保证元素的排列顺序
HashSet 不是线程安全的
集合元素可以是 null
存储形式既包括数组又包括链表,通过Hash算法比较哈希值存放至数组,若不同的对象hash值相同,则调用equals方法,返回false,则在hash值相同的数组元素基础之上以链表的形式存储
HashSet 集合判断两个元素相等的标准:
两个对象通过 hashCode() 方法比较相 等,并且两个对象的 equals() 方法返回值也相等。
对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。
即:“相等的对象必须具有相等的散列码”。
重写hashCode注意事项
在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
当两个对象的 equals() 方法比较返回 true 时,这
两个对象的 hashCode() 方法的返回值也应相等。
对象中用作 equals() 方法比较的 Field,都应该用来
计算 hashCode 值
重写equals注意事项
以自定义的Customer类为例,何时需要重写equals()?
当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,
总是 要改写hashCode(),根据一个类的equals方法(改写后),
两个截然不 同的实例有可能在逻辑上是相等的,但是,
根据Object.hashCode()方法, 它们仅仅是两个对象。
因此,违反了“相等的对象必须具有相等的散列码”。
结论:复写equals方法的时候一般都需要同时复写hashCode方法。
通 常参与计算hashCode的对象的属性也应该参与到equals()中进行计算
向HashSet中添加元素的过程理解
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法 来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定出该对象 在 HashSet 底层数组中的存储位置。
(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)
如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果 为true,添加失败; 如果为false,那么会保存该元素,但是该数组的位置已经有元素了, 那么会通过链表的方式继续链接。
如果两个元素的 equals() 方法返回 true**,但它们的 hashCode() 返回值不相 等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功**。
实现类2:LinkedHashSet
LinkedHashSet实现类
HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历,(但存储时是无序的)
LinkedHashSet 是 HashSet 的子类
LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置, 但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入 顺序保存的。
LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全 部元素时有很好的性能。
LinkedHashSet 不允许集合元素重复。
对于频繁的遍历LinkedHashSet效率更高
实现类3:TreeSet
TreeSet实现类
TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
TreeSet底层使用红黑树结构存储数据
新增的方法如下: (了解)
Comparator comparator()
Object first() Object last()
Object lower(Object e)
Object higher(Object e)
SortedSet subSet(fromElement, toElement)
SortedSet headSet(toElement)
SortedSet tailSet(fromElement)
TreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。
排 序—自然排序
- 自然排序:TreeSet 会默认调用集合元素的 compareTo(Object obj) 方法来比较元 素之间的大小关系,然后将集合元素按升序(默认情况)排列
(由源码分析compareTo(Object o)方法,并实现)
- 如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口。
- TreeSet在遍历时会自动排序(自然排序或定制排序),但一般自然排序无法处理自定义类的对象(往往是因为有很多自定义的属性),需要在自定义类中重写方法
(由源码分析compareTo(Object o)方法,并实现)
@Override
public int compareTo(Object o) {
if (o instanceof User){
User user=(User) o;
//定义只比较name属性
int compare=-this.name.compareTo(user.name);
//等于-1为小于,等于0为等于,等于1为大于
if (compare!=0){
return compare;
}else {
//年龄从小到大排列
return Integer.compare(this.age,user.age);
}
}else {
throw new RuntimeException("输入的类型不匹配");
}
}
(TreeSet里面比较相同使用的是compareTo,而不用equals())
排 序—定制排序
TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没 有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照 其它属性大小进行排序,则考虑使用定制排序。定制排序,通过Comparator接口来 实现。需要重写compare(T o1,T o2)方法。
利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表 示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构 造器。
此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异 常。
使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0
//TreeSet:不能添加不同类的对象
@Test
public void test5(){
Comparator com=new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User&& o2 instanceof User){
User user1=(User) o1;
User user2=(User) o2;
//从小到大排序
return Integer.compare(user1.getAge(), user2.getAge());
}else {
throw new RuntimeException("不匹配");
}
}
};//重写了方法则创建了接口对应实现类!!!!!
TreeSet set =new TreeSet(com);
set.add(new User("Tom",13));
set.add(new User("Tam",22));
set.add(new User("Tcm",43));
set.add(new User("Tcm",13));
set.add(new User("Tdm",34));
set.add(new User("Tem",51));
Iterator iterator =set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
特殊声明:本人并不是大佬,只是说一说自己在学习Java过程中的理解,文章有不正确的地方欢迎大家批评指正。
温馨提示:道路千万条,学习第一条。平时不学习,招聘两行泪!!!