当前位置: 首页 > news >正文

Java Collection

Collection——狭义上的集合

接口Collection派生了三大类集合,分别是List、Set、Queue/Deque。

  • List,有序集合,提供了访问、插入、删除等操作。
  • Set,不允许重复元素的,这是和List最明显的区别,不存在两个对象equals返回true。在开发中有很多需要保证元素唯一性的场合可以使用。
  • Queue/Deque,Java提供的标准队列结构的实现,除了集合的基本功能,它还支持类似先入先出(FIFO, First-in-First-Out)或者后入先出(LIFO,Last-In-First-Out)等特定行为。

Vector是Java早期提供的线程安全的动态数组,如果不需要线程安全,并不建议选择,同步是有额外开销的。Vector内部是使用对象数组来保存数据,可以根据需要自动的增加容量,当数组已满时,会创建新的数组,并拷贝原有数组数据。

ArrayList是应用动态数组实现,它本身不是线程安全的,性能要好很多。与Vector类似,ArrayList也是可以根据需要调整容量,不过两者的调整逻辑有所区别,Vector在扩容时会提高1倍,而ArrayList则是增加50%。

LinkedList是双向链表,它不需要像上面两种那样调整容量,它也不是线程安全的。这些集合不是完全孤立的,比如LinkedList,既是List,也是Deque。

TreeSet支持自然顺序访问,但添加、删除、包含等操作要相对低效(log(n))。

HashSet利用哈希算法,理想情况下哈希散列正常,可以提供常数时间的添加、删除、包含等操作,但是它不保证有序。

LinkedHashSet内部构建了一个记录插入顺序的双向链表,因此提供了按照插入顺序遍历的能力,也保证了常数时间的添加、删除、包含等操作,这些操作性能略低于HashSet,因为需要维护链表的开销。

注:

  • 每种集合的通用逻辑,都被抽象到相应的抽象类中,比如AbstractList就集中了各种List操作的通用部分。
  • Vector和ArrayList非常适合随机访问的场合。LinkedList进行节点插入、删除却要高效得多,但是随机访问性能则要比动态数组慢。
  • TreeSet代码里实际默认是利用TreeMap实现的,Java类库创建了一个Dummy对象“PRESENT”作为value,然后所有插入的元素其实是以键的形式放入了TreeMap里面;同理,HashSet也是以HashMap为基础实现的,它们是Map类的马甲。
  • 在遍历元素时,HashSet性能受自身容量影响,所以初始化时最好不要将HashMap容量设置过大。而对于LinkedHashSet,由于其内部链表提供的方便,遍历性能只和元素多少有关系。

Java 8中支持了Lambda和Stream,相应的Java集合框架也进行了大范围的增强,支持类似为集合创建相应stream或者parallelStream的方法实现,方便地实现函数式代码。源码中这些API的设计和实现比较独特,它们不是实现在抽象类里面,而是以默认方法的形式实现在Collection这样的接口里。这是Java 8在语言层面的新特性,Java面向对象基本机制的演进允许接口实现默认方法,原来实现在类似Collections这种工具类中的方法,大多可以转换到相应的接口上。

Java 9中提供了一系列的静态工厂方法,比如List.of()、Set.of(),大大简化了构建小容器实例的代码量。业界实践发现相当一部分集合实例容量都非常有限,且在生命周期中并不会进行修改。通过各种of静态工厂方法创建的实例,还应用了一些最佳实践,比如它是不可变的,符合我们对线程安全的需求;它不需要考虑扩容,空间上更加紧凑等。阅读of方法的源码发现,Java已经支持可变参数(varargs),但官方类库还是提供了一系列特定参数长度的方法,看起来似乎非常不优雅,为什么呢?这其实是为了最优的性能,JVM在处理变长参数时会有明显的额外开销,如果你需要实现性能敏感的API,也可以参考。

Collection集合类都不是线程安全的,但并不代表这些集合完全不能支持并发编程的场景,在Collections工具类中提供了一系列的synchronized方法,但平常不推荐使用,同步控制粒度太大,不适合对性能要求较高的场景。

// 定义
static <T> List<T> synchronizedList(List<T> list)// 使用
List list = Collections.synchronizedList(new ArrayList());

实现原理是将方法(比如get、set、add等)通过synchronized添加同步原语。注意这些方法创建的线程安全集合,都符合迭代时fail-fast行为,当发生意外的并发修改时,尽早抛出ConcurrentModificationException异常,以避免不可预计的行为。

集合排序

集合具体使用哪种排序算法,需要区分是Arrays.sort()还是Collections.sort() (底层是调用Arrays.sort());什么数据类型;多大的数据集(太小的数据集,复杂排序是没必要的,Java会直接进行二分插入排序)等。

  • 对于原始数据类型,目前使用的是所谓双轴快速排序(Dual-Pivot QuickSort,java/util/DualPivotQuicksort.java),早期版本是相对传统的快速排序。
  • 对于对象数据类型,目前则是使用TimSort,它是一种归并和二分插入排序(binarySort)结合的优化排序算法(java/util/TimSort.java)。TimSort并不是Java独创的,它的思路是查找数据集中已经排好序的分区,然后合并这些分区来达到排序的目的。
  • Java 8引入了并行排序算法(直接使用parallelSort方法),这是为了充分利用现代多核处理器的计算能力,底层实现基于fork-join框架,当处理的数据集比较小的时候,差距不明显,甚至还表现差一点;当数据集增长到数万或百万以上时,会有很大的提升,具体取决于处理器和系统环境。

Have Fun


http://www.mrgr.cn/news/78608.html

相关文章:

  • uniapp连接mqtt频繁断开原因和解决方法
  • 【组成原理】计算机硬件设计——ALU
  • Maven 配置
  • yolov8的深度学习环境安装(cuda12.4、ubuntu22.04)
  • Spring Boot使用JDK 21虚拟线程
  • 在shardingsphere执行存储过程
  • 机器学习实战:泰坦尼克号乘客生存率预测(数据处理+特征工程+建模预测)
  • vulnhub靶场之hackableⅡ
  • 【C语言】字符串左旋的三种解题方法详细分析
  • Jmeter进阶篇(29)AI+性能测试领域场景落地
  • Linux系统 进程
  • 三十二:网络爬虫的工作原理与应对方式
  • 记录学习《手动学习深度学习》这本书的笔记(一)
  • (Python)前缀和
  • OPTEE v4.4.0 FVP环境搭建(支持hafnium)
  • 【北京迅为】iTOP-4412全能版使用手册-第二十章 搭建和测试NFS服务器
  • Spring源码学习
  • Python字典的用法(定义、增加、删除、修改、查询、遍历)
  • Spring中每次访问数据库都要创建SqlSession吗?
  • LearnOpenGL 学习(入门--三角形,着色器,纹理)