JAVA语言疯狂讲义-- JAVA语言集合入门
小标 2018-12-19 来源 : 阅读 790 评论 0

摘要:本文主要向大家介绍了JAVA语言疯狂讲义-- JAVA语言集合入门,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言疯狂讲义-- JAVA语言集合入门,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。


一,Java集合


Java的集合类只要又两个接口派生而出:Collection和Tree,Collection和Tree是Java集合框架的根接口,这两个接口又包含了一些子接口的实现类。



Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和Queue集合。


Iterator接口也是Java集合接口框架的成员,但它与Collection系列、Tree系列的集合不一样:Collection系列集合、Tree系列集合只要用于盛装其他对象,而Iterator则主要用于遍历(即迭代访问)Collection集合中的元素,Iterator对象也称为迭代器。



二,各Set实现类的性能分析


HashSet 和 TreeSet 是Set的两个典型实现,到底如何选择HashSet和TreeSet呢?HashSet 的性能总是比TreeSet好(特别是最常用的添加,查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。


HashSet还有一个子类:linkedHashSet,对于普通的插入、删除操作,linkedHashSet比HashSet要略微慢一点,这时由于维护链表所带来的额外开销造成的,但由于有了链表,遍历linkedHashSet会更快。


HashSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。


必须指出的是,Set的三个实现类HashSet、TreeSet和HashSet都是线程不安全的。如果有多个线程同时访问一个Set集合,并且有超过一个线程修改了该Set集合,则必须手动保证该Set集合的同步性。通常可以通过collection工具类的synchronizedSortedSet方法来包装该Set集合。此操作最好在创建时进行,以防止对Set集合的意外非同步访问。例如


?

1

   

SortedSet Set = Collections.synchronizedNavigableSet(new TreeSet<>());

   



三,各种线性表的性能分析


ArrayList和vector是List类的两个典型实现,都是基于数据实现的List类。

vector类是一个古老的集合,具有很多缺点,通常尽量少用vector实现类。

ArrayList和vector的显著区别是:ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,如果有超过一个线程修改了ArrayList集合,则程序必须手动保证集合的同步性,但vector集合则是线程安全的,无须保证该集合的同步性。因为vector是线程安全的,所以vector的性能比ArrayList的性能要低。


PriorityQueue是一个比较标准的队列实现类,而不是绝对标准的队列实现,因为PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序的。


Java提供的list就是一个线性表接口,而ArrayList、linkedlist又是线性表的两种典型实现,基于数组的线性表和基于链表的线性表。queue代表了队列,deque代表了双端队列(既可作为队列使用,也可作为栈使用),接下来对各种实现类的性能进行分析。


一般来说,由于数组以一块连续内存区来保存所有的数组元素,所以数组在随机访问时性能最好,所有的内部以数组作为底层实现的集合在随机访问时性能都比较好,而内部以链表作为底层实现的集合在执行插入、删除操作时有较好的性能。但总体来说,ArrayList的性能比linkedlist的性能要好,因此大部分时候都应该考虑使用ArrayList。


关于使用list集合的建议:

1,如果需要遍历list集合,对于ArrayList、vector集合,应该使用随机访问法(get)来遍历集合元素,这样性能更好,对于linkedlist集合,则应该采用迭代器(iterator)来遍历集合元素。

2,如果需要经常执行插入、删除操作来改变包含大量数据的list集合的大小,可考虑使用linkedlist集合。使用ArrayList、vector集合可能需要经常分配内部数组的大小,效果可能比较差。

3,如果多个线程需要同时访问list集合的元素,开发者可考虑使用Collections将集合包装成线程安全的集合。



四,各Tree实现类的性能分析


从Java源码来看,Java是先实现了Tree,然后通过包装一个所有value都为null的Tree就实现了Set集合。


Properties类是Hashtable类的子类,正如它的名字所暗示的,该对象在处理属性文件时特别方便。


WeekHashTree与HashTree的用法基本类似。与HashTree的区别在于,HashTree的key保留了对实际对象的强引用,这意味着只要该HashTree对象不被销毁,该HashTree的所有key所引用的对象就不会被垃圾回收,HashTree也不会自动删除这些key所对应的kev-value对,但WeekHashTree的key只保留了对实际对象的弱引用,这意味着如果WeekHashTree对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,WeekHashTree也可能自动删除这些key所对应的key-value对。


HashTree实现类是一个与枚举一起使用的Tree实现,HashTree中的所有key都必须是单个枚举类的枚举值。


对于Tree的常用实现类而言,虽然HashTree与Hashtable的实现机制几乎一样,但由于Hashtable是一个古老的、线程安全的集合,因此HashTree通常比Hashtable要快。


treeTree通常比HashTree、Hashtable要慢(尤其在插入、删除key-value对时更慢),因为treeTree底层采用红黑树来管理key-value对(红黑树的每个节点就是一个key-value对)。


使用treeTree有一个好处:treeTree中的key-value对总是处于有序状态,无须专门进行排序操作。当treeTree被填充之后,就可以调用keySet(),取得由key组成的Set,然后使用toArray()方法生成key的数组,接下来使用Arrays的binarySearch()方法在已排序的数组中快速地查询对象。


对于一般的应用场景,程序应该多考虑使用HashTree,因为HashTree正是为快速查询设计的(HashTree底层其实也是采用数组来存储key-value对)。但如果程序需要一个总是排好序的Tree时,则可以考虑使用treeTree。


linkedHashTree比HashTree慢一点,因为它需要维护链表来保持Tree中key-value的添加顺序。IdentityHashTree性能没有特别出色之处,因为它采用与HashTree基本相似的实现,只是它使用==而不是equals()方法来判断元素相等。HashTree的性能最好,但它只能使用同一个枚举类的枚举值作为key。



五,collections工具类同步控制


collections类中提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。


Java中常用的集合框架中的实现类HashSet、treeSet、ArrayList、ArrayDeque、linkedlist、HashTree和treeTree都是线程不安全的。如果有多个线程访问它们,而且有超过一个的线程试图修改它们,则存在线程安全问题。collections提供了多个类方法可以把它们包装成线程安全的集合。


?

1

2

3

4

5

6

7

8

9

10

11

12

   

public class SynchronizedTest

{

    public static void main(String[] args)

    {

        // 下面程序创建了四个线程安全的集合对象

        Collection c = Collections

            .synchronizedCollection(new ArrayList());

        List list = Collections.synchronizedList(new ArrayList());

        Set s = Collections.synchronizedSet(new HashSet());

        Tree m = Collections.synchronizedTree(new HashTree());

    }

}

   


在上面示例程序中,直接将新创建的集合对象传给了collections的synchronizedXxx()方法,这样就可以直接获取list、Set和Map的线程安全实现版本。



六,线程安全的集合类


在java.util.concurrent包下提供了大量支持并发访问的集合接口和实现类。


这些线程安全的集合类可分为如下两类:

1,以Concurrent开头的集合类,如ConcurrentHashMap。Concurrentskiplistmap、Concurrentskiplistset、Concurrentlinkedqueue和Concurrentlinkeddeque。

2,以CopyOnWrite开头的集合类,如CopyOnWriteArrayList、CopyOnWritearrayset。


其中以Concurrent开头的集合类代表了支持并发访问的集合,它们可以支持多个线程并发写入访问,这些写入线程的所有操作都是线程安全的,但读取操作不必锁定。以Concurrent开头的集合类采用了更复杂的算法来保证永远不会锁住整个集合,因此在并发写入时有较好的性能。


当多个线程共享一个公共结合时,ConcurrentLinkedQueue时一个恰当的选择。ConcurrentLinkedQueue不允许使用null元素。ConcurrentLinkedQueue实现了多线程的高效访问,多个线程访问ConcurrentLinkedQueue集合时无须等待。


在默认情况下,ConcurrentHashMap支持16个线程并发写入,当超过16个线程并发向该map中写入数据时,可能有一些线程需要等待。实际上,程序通过设置concurrentLevel构造参数(默认值16)来支持更多的并发写入线程。


与前面介绍的HashMap和普通集合不同的是,因为Concurrentlinkedqueue和ConcurrentHashMap支持多线程并发访问,所以当使用迭代器来遍历元素时,该迭代器可能不能反映出迭代器之后所做的修改,但程序不会抛出异常。


使用非concurrent包下的collection作为集合对象时,如果该集合对象创建迭代器后集合元素发生改变,则会引发ConcurrentModificationException异常。


由于CopyOnWritearrayset的底层封装了CopyOnWriteArrayList,因此它的实现机制完全类似于CopyOnWriteArrayList集合。


对于CopyOnWriteArrayList集合,正如它的名字所暗示的,它采用复制底层数组的方式来实现写操作。


当线程对CopyOnWriteArrayList集合执行读取操作时,线程将会直接读取集合本身,无须加锁与阻塞。当线程对CopyOnWriteArrayList集合执行写入操作时(包括调用add()、remove()、set()等方法),该集合会在底层复制一份新的数组,接下来对新的数组执行写入操作。由于对CopyOnWriteArrayList集合的写入操作都是对数组的副本执行操作,因此它是线程安全的。


需要指出的是,由于CopyOnWriteArrayList执行写入操作时需要频繁地复制数组,性能比较差,但由于读操作与写操作不是操作同一个数组,而且读操作也不需要加锁,因此读操作就很快、很安全。由此可见,CopyOnWriteArrayList适合用在读取操作远远大于写入操作的场景中,例如缓存等。


          

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程