本篇是我的Java专栏第四篇,看过我前三篇专栏的同学一定知道我讲的东西是别人没讲过且很实用的内容,比起那些看起来又冗长又浪费时间的文章而言,希望你可以从我的专栏中学到受益的技能和受到思维的启发。如果喜欢我的内容,请继续关注我的专栏,本专栏从Java基础、集合、内存GC、IO、多线程、JVM以及性能优化7个方面剖析Java,旨在帮助有一定Java开发经验的程序员提高自己。如有问题,可在我的专栏底部给我留言,我会尽我的能力给你解答。
今天我给大家讲讲不一样的Java集合类,Java集合大致可分为Set、List、Map、Queue四种体系。而集合类主要又由Collection和Map两个接口派生。
集合类继承树
Map继承树
List用来存储有序的Collection。
ArrayList、Vector、LinkedList均继承自AbstractList,AbstractList继承自AbstractCollection,并实现List接口。
ArrayList超过数组预定义最大限度时,需要对数组进行扩容,扩容过程会执行大量复制操作。而LinkedList使用链表结构,无需扩容。
另外List还有个专用目的的实现叫CopyOnWriteArrayList,由写时复制新数组实现,适用于遍历较多,变化不多的情况。
List/Collections有几个有趣的方法:
vector时Java早期版本的类,具有同步性质,是线程安全的,它会对所有操作进行加锁,所以一般情况下不要使用。
尽管如此,平时在开发过程中也尽量不要使用Vector和HashTable,用Collections.synchronizedList(list)。
优势就是 读取 和 搜索 速度很快,删除数据开销很大,数组需要重排。
优势就是 插入 数据很快,这里要注意的是LinkedList用的是链表结构,因此每次获取数据都要从第一个元素开始遍历,因此数据越多性能越慢。但是即便是这样其 删除 数据也比ArrayListy要快。
LinkedList list; for(int n = 0; n < list.size; n++) { // 不要这样使用LinkedList // 否则每次get都是从第一个元素开始遍历 list.get(n); } // 推荐这样遍历 Iterator iterator = list.iterator(); while(iterator.hasNext())
因此当数据增删不是很频繁的情况下,尽量优先使用ArrayList;反之当数据需要频繁增删,查询较少,很少用到get(index)的时候,应该使用LinkedList。这里需要结合自身业务需求,而不是盲目使用ArrayList。
ArrayList和Vector底层用数组实现,ArrayList是线程不安全的,LinkedList是采用的双向链表实现。
用List的subList()方法返回的是源数据的直接引用,只能用作暂时的对象,否则会报错。
对原数据任意位置进行增删操作之后,再次调用已经创建的subList引用会报ConcurrentModificationException。
而对subList创建的引用进行增删操作后,会对原数据对应位置进行增删操作。
RandomAccess是一个接口,它本身并没有提供任何方法,只是用来标识哪些类可以支持快速随机访问。
任何基于数组的List都实现了RandomAccess接口,而基于链表结构的都没有。因为只有数组才能快速随机访问,而链表只能通过遍历的方式访问。因此可以根据是否支持RandomAccess来优化程序性能。
if(list instanceof RandomAccess) { for(int n = 0; n < list.size; n++); } else { Iterator iterator = list.iterator(); while(iterator.hasNext()); }
foreach是一种高效的迭代器,其与iterator的区别就是foreach不支持迭代过程中remove对象。因此无论对象是否支持RandomAccess,用foreach迭代效率是最高的。
java中常见的Map有以下四种:
速度快,没有顺序,非同步
默认按照Key升序,不允许Null值,非同步
按照插入顺序排序,非同步。LinkedHashMap继承自HashMap,它在其基础上又在内部增加了一个链表,用以存放元素的插入顺序。