Java Review (二十六、集合----- Set 集合)

HashSet还有一个子类 LinkedHashSet , LinkedHashSet 集合也是根据元素的 hashCode 值来决定元素的存储位置 , 但它同时使用链表维护元素的次序 ,这样使得元素看起来是以插入的顺序保存的 。 也就是说 , 当遍历 LinkedHashSet 集合里的元素时, LinkedHashSet 将会按元素的添加顺序来访问集合里的元素。

LinkedHashSet 需要维护元素 的插入顺序,因此性能略低于 HashSet 的性能,但在迭代访问 Set 里的全部元素时将有很好的性能,因为它以链表来维护内部顺序 。

LinkedHashSetTest.java

public class LinkedHashSetTest

{

public static void main(String[] args)

{

LinkedHashSet books = new LinkedHashSet();

books.add(“疯狂Java讲义”);

books.add(“轻量级Java EE企业应用实战”);

System.out.println(books);

// 删除 疯狂Java讲义

books.remove(“疯狂Java讲义”);

// 重新添加 疯狂Java讲义

books.add(“疯狂Java讲义”);

System.out.println(books);

}

}

虽然 LinkedHashSet 使用了链表记录集合元素的添加顺序,但 LinkedHashSet 依然是HashSet ,因此它依然不九许集合元素重复 。

API:java.util.LinkedHashSet

TreeSet 类

===========================================================================

在这里插入图片描述

TreeSet 是 SortedSet 接口的实现类,正如 SortedSet 名字所暗示的, TreeSet 可以确保集合元素处于排序状态。与 HashSet 集合相比, TreeSet 还提供了如下几个额外的方法 :

  • Comparator comparator(): 如 果 TreeSet 采用了定制排序,则该方法返回定制排序所使用的Comparator; 如 果 TreeSet 采用了自然排序,则返回 null 。

  • Object ftrst(): 返回集合中的第一个元素。

  • Object last(): 返回集合中的最后一个元素 。

  • Object lower(Object e): 返回集合中位于指定元素之前的元素( 即小于指定元素的最大元素 ,参考元素不需要是 TreeSet 集合里的元素)。

  • Object higher (Object e): 返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素不需要是 TreeSet 集合里的元素) 。

  • SortedSet subSet(Object fromElement, Object toElement): 返回此 Set 的子集合,范围从 fromElement(包含〉到 toElement (不包含)。

  • SortedSet headSet(Object toElement): 返回此 Set 的子集 ,由小于toElement 的元素组成 。

  • SortedSet tailSet(Object 仕omElement): 返回此Set 的子集 , 由大于或等于企omElement 的元素组成 。

下面程序测试了 TreeSet 的通用用法:

TreeSetTest.java

public class TreeSetTest

{

public static void main(String[] args)

{

TreeSet nums = new TreeSet();

// 向TreeSet中添加四个Integer对象

nums.add(5);

nums.add(2);

nums.add(10);

nums.add(-9);

// 输出集合元素,看到集合元素已经处于排序状态

System.out.println(nums);

// 输出集合里的第一个元素

System.out.println(nums.first()); // 输出-9

// 输出集合里的最后一个元素

System.out.println(nums.last()); // 输出10

// 返回小于4的子集,不包含4

System.out.println(nums.headSet(4)); // 输出[-9, 2]

// 返回大于5的子集,如果Set中包含5,子集中还包含5

System.out.println(nums.tailSet(5)); // 输出 [5, 10]

// 返回大于等于-3,小于4的子集。

System.out.println(nums.subSet(-3 , 4)); // 输出[2]

}

}

运行结果:

在这里插入图片描述

与 HashSet 集合采用 hash 算法来决定元素 的存储位置不同, TreeSet 采用红黑树的数据结构来存储集合元素。TreeSet 支持两种排序方法 : 自然排序和定制排序。在默认情况下, TreeSet 采用自然排序。

要使用树集, 必须能够比较元素。这些元素必须实现 Comparable 接口, 或者构造集时必须提供一个 Comparator 。

自然排序


TreeSet 会调用集合元素的 compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式就是自然排序 。

Java 提供了 一个 Comparable 接口,该接口里定义了一个 compareTo(Object obj )方法,该方法返回一个整数值,实现该接口的类必须实现该方法 , 实现了 该接口的类的对象就可以比较大小。当一个对象调用该方法与另一个对象进行 比较时,例如 obj 1.compareTo(obj2) ,如果该方法返回 0 ,则表明这两个对象

相等 :如果该方法返回一个正整数, 则表明 objl 大于 obj2; 如果该方法返回一个负整数, 则表明 objl小于 obj2 。

Java 的 一些常用类已经实现了 Comparable 接口,并提供了比较大小的标准。

下面是实现了Comparable 接口的常用类:

  • BigDecimal 、 BigInteger 以及所有的数值型对应的包装类 : 按它们对应的数值大小进行比较。

  • Character: 按字符的 UNICODE 值进行比较。

  • Boolean: true 对应的包装类实例大于 false 对应的包装类实例。

  • String: 按字符串中字符的UNICODE 值进行 比较。

  • Date 、 Time: 后面的时间、日期比前面的时间、日期大。

如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口,否则程序将会抛出异常。如下程序示范了这个错误:

TreeSetTestError.java

class Err{}

public class TreeSetErrorTest

{

public static void main(String[] args)

{

TreeSet ts = new TreeSet();

// 向TreeSet集合中添加Err对象

// 自然排序时,Err没实现Comparable接口将会引发错误

ts.add(new Err());

}

}

向 TreeSet 中添加的应该是同一个类的对象,否则也会引发ClassCastException 异常,因为大部分类在实现 compareTo(Object obj)方法时,都需要将被比较对象 obj 强制类型转换成相同类型。

如下程序示范了这个错误 :

TreeSetErrorTest2.java

public class TreeSetErrorTest2

{

public static void main(String[] args)

{

TreeSet ts = new TreeSet();

// 向TreeSet集合中添加两个对象

ts.add(new String(“疯狂Java讲义”));

ts.add(new Date()); // ①

}

}

当把一个对象加入 TreeSet 集合中时, TreeSet 调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树结构找到它的存储位置 。 如果两个对象通过 compareTo(Object obj)方法比较相等,新对象将无法添加到 TreeSet 集合中 。

TreeSetTest2.java

class Z implements Comparable

{

int age;

public Z(int age)

{

this.age = age;

}

// 重写equals()方法,总是返回true

public boolean equals(Object obj)

{

return true;

}

// 重写了compareTo(Object obj)方法,总是返回1

public int compareTo(Object obj)

{

return 1;

}

}

public class TreeSetTest2

{

public static void main(String[] args)

{

TreeSet set = new TreeSet();

Z z1 = new Z(6);

set.add(z1);

// 第二次添加同一个对象,输出true,表明添加成功

System.out.println(set.add(z1)); //①

// 下面输出set集合,将看到有两个元素

System.out.println(set);

// 修改set集合的第一个元素的age变量

((Z)(set.first())).age = 9;

// 输出set集合的最后一个元素的age变量,将看到也变成了9

System.out.println(((Z)(set.last())).age);

}

}

运行结果:

在这里插入图片描述

程序中①代码行把同 一个对象再次添加到 TreeSet 集合中,因为 zl 对象的

ompareTo(Object obj)方法总是返回 1, 虽然它的 equalsO方法总是返回 true ,但 TreeSet会认为 z1对 象和它自己也不相等,因此TreeSet 可以添加两个 z1 对 象。

TreeSet 及 Z 对象在内存中的存储示意图

在这里插入图片描述

由此应该注意一个问题 : 当需要把一个对象放入 TreeSet中, 重写该对象对应类的 equals()方法时,应保证该方法与 compareTo(Object obj)方法有一致的结果,其规则 是 : 如果两个对象通过 equals()方法比较返回 true 时,这两个对象通过 compareTo(Object obj)方法比较应返回 0 。

定制排序


TreeSet 的自 然排序是根据集合元素 的大小, TreeSet 将它们以升序排列 。 如果需要实现定制排序,例如以降序排列, 则可以通过 Comparator 接口 的帮助 。该接口里包含一个 int compare(T 01 , T 02)方法,该方法用于比较 01 和 02 的大小:如果该方法返 回正整数 ,则表明 01 大于 02; 如果该方法返回 0 ,则表明 01 等于 02; 如果该方法返回负整数, 则表 明 01 小于 02 。

如果需要实现定制排序,则需要在创建 TreeSet 集合对象时,提供一个Comparator 对象与该TreeSet集合关联 , 由该 Comparator 对象负责集合元素 的排序逻辑。由于 Comparator 是一个函数式接口,因此可使用 Lambda 表达式来代替 Comparator 对象 。

TreeSetTest4.java

class M

{

int age;

public M(int age)

{

this.age = age;

}

public String toString()

{

return “M [age:” + age + “]”;

}

}

public class TreeSetTest4

{

public static void main(String[] args)

{

// 此处Lambda表达式的目标类型是Comparator

TreeSet ts = new TreeSet((o1 , o2) ->

{

M m1 = (M)o1;

M m2 = (M)o2;

// 根据M对象的age属性来决定大小,age越大,M对象反而越小

return m1.age > m2.age ? -1
m1.age < m2.age ? 1 : 0;

});

ts.add(new M(5));

ts.add(new M(-3));

ts.add(new M(9));

System.out.println(ts);

}

}

运行结果:

在这里插入图片描述

API:java.util.TreeSet

EnumSet 类

===========================================================================

EnumSet 是一个专为枚举类设计的集合类, EnumSet 中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建 EnumSet 时显式或隐式地指定。

EnumSet 的集合元素也是有序的, EnumSet 以枚举值在 Enum 类内的定义顺序来决定集合元素的顺序。

EnumSet 在内部以位向 量 的形式存储,这种存储形式非常紧凑 、 高效 ,因此 EnumSet 对象占用内存很小,而且运行效率很好。尤其是进行批量操作(如调用 containsAll() 和 retainAll()方法〉时,如果其参数也是 EnumSet 集合,则该批量操作的执行速度也非常快。

EnumSet 集合不允许加入 null 元素,如果试图插入 null 元素, EnumSet 将抛出 NullPointerException异常。如果只是想判断 EnumSet 是否包含 null 元素或试图删除 null 元素都不会抛出异常,只是删除操作将返回 false,因为没有任何 null 元素被删除。

EnumSet 类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的类方法来创建 EnumSet对象 。 EnumSet 类它提供了如下常用的类方法来创建 EnumSet 对象 :

  • EnumSet allOf(Class elementType): 创建一个包含指定枚举类里所有枚举值的 EnumSet 集合 。

  • EnumSet complementOf(EnumSet s): 创建一个其元素类型与指定 EnumSet 里元素类型相同的

*EnumSet 集合,新 EnumSet 集合包含原 EnumSet 集合所不包含的 、此枚举类剩下的枚举值(即新EnumSet 集合和原 EnumSet 集合的集合元素加起来就是该枚举类的所有枚举值)。

  • EnumSet copyOf(Collection c): 使用 一个普通集合来创建 EnumSet 集合 。

  • EnumSet copyOf(EnumSet s): 创建一个与指定 EnumSet 具有相同元素类型、相同集合元素的EnumSet 集合 。

  • EnumSet noneOf(Class elementType): 创建一个元素类型为指定枚举类型的空 EnumSet 。

  • EnumSet of(E first, E… rest): 创建一个包含一个或多个枚举值 的 EnumSet 集合,传入的多个枚举值必须属于同一个枚举类。

  • EnumSet range(E from, E to): 创建一个包含从 from 枚举值到 to 枚举值范围内所有枚举值的EnumSet集合。

下面程序示范了 如何使用 EnumSet来保存枚举类的多个枚举值 :

EnumSetTest.java

enum Season

{

SPRING,SUMMER,FALL,WINTER

最后我们该如何学习?

1、看视频进行系统学习

这几年的Crud经历,让我明白自己真的算是菜鸡中的战斗机,也正因为Crud,导致自己技术比较零散,也不够深入不够系统,所以重新进行学习是很有必要的。我差的是系统知识,差的结构框架和思路,所以通过视频来学习,效果更好,也更全面。关于视频学习,个人可以推荐去B站进行学习,B站上有很多学习视频,唯一的缺点就是免费的容易过时。

另外,我自己也珍藏了好几套视频资料躺在网盘里,有需要的我也可以分享给你:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

2、读源码,看实战笔记,学习大神思路

“编程语言是程序员的表达的方式,而架构是程序员对世界的认知”。所以,程序员要想快速认知并学习架构,读源码是必不可少的。阅读源码,是解决问题 + 理解事物,更重要的:看到源码背后的想法;程序员说:读万行源码,行万种实践。

Spring源码深度解析:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

Mybatis 3源码深度解析:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

Redis学习笔记:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

Spring Boot核心技术-笔记:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

3、面试前夕,刷题冲刺

面试的前一周时间内,就可以开始刷题冲刺了。请记住,刷题的时候,技术的优先,算法的看些基本的,比如排序等即可,而智力题,除非是校招,否则一般不怎么会问。

关于面试刷题,我个人也准备了一套系统的面试题,帮助你举一反三:

1年半经验,2本学历,Curd背景,竟给30K,我的美团Offer终于来了

只有技术过硬,在哪儿都不愁就业,“万般带不去,唯有业随身”学习本来就不是在课堂那几年说了算,而是在人生的旅途中不间断的事情。

人生短暂,别稀里糊涂的活一辈子,不要将就。

的方式,而架构是程序员对世界的认知”。所以,程序员要想快速认知并学习架构,读源码是必不可少的。阅读源码,是解决问题 + 理解事物,更重要的:看到源码背后的想法;程序员说:读万行源码,行万种实践。

Spring源码深度解析:

[外链图片转存中…(img-BXw8ymyi-1714306261938)]

Mybatis 3源码深度解析:

[外链图片转存中…(img-uChWSVX7-1714306261938)]

Redis学习笔记:

[外链图片转存中…(img-z1jYEGNU-1714306261939)]

Spring Boot核心技术-笔记:

[外链图片转存中…(img-z7GlQWFb-1714306261939)]

3、面试前夕,刷题冲刺

面试的前一周时间内,就可以开始刷题冲刺了。请记住,刷题的时候,技术的优先,算法的看些基本的,比如排序等即可,而智力题,除非是校招,否则一般不怎么会问。

关于面试刷题,我个人也准备了一套系统的面试题,帮助你举一反三:

[外链图片转存中…(img-FzI8CFW3-1714306261939)]

只有技术过硬,在哪儿都不愁就业,“万般带不去,唯有业随身”学习本来就不是在课堂那几年说了算,而是在人生的旅途中不间断的事情。

人生短暂,别稀里糊涂的活一辈子,不要将就。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 16
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值