Vector、ArrayList、LinkedList有何区别 以及 集合框架的知识梳理
文章目录
一、考点分析
- Java集合框架的设计结构,至少要有一个整体印象
- Java提供的主要容器(集合和Map)类型,了解或掌握对应的数据结构,算法,思考具体技术选择。
- 将问题扩展到性能,并发等领域。
- 集合框架的演进和发展。
- 数据结构和算法是基本功
- 内部排序:归并排序,交换(冒泡,快排)排序,选择排序,插入排序
- 外部排序:掌握利用内存和外部存储处理超大数据集,至少要理解过程和思路
- 算法稳定性分析
二、集合框架
1、Vector、ArrayList、LinkedList的区别
这三者都是实现集合框架中的List, 也就是所谓的有序集合, 因此具体功能也比较近似,比如都 提供按照位置进行定位、添加或者删除的操作,都提供迭代器以遍历其内容等. 但因为具体的设 计区别,在行为、性能、线程安全等方面,表现又有很大不同.
Vector:
- Vector是Java早期提供的线程安全的动态数组,如果不需要线程安全,并不建议选择,毕竟同 步是有额外开销的。Vector内部是使用对象数组来保存数据 ,可以根据需要自动的增加容量, 当数组已满时,会创建新的数组,并拷贝原有数组数据。
ArrayList:
- ArrayList是应用更加广泛的动态数组实现,它本身不是线程安全的,所以性能要好很多。与 Vector近似,ArrayList也是可以根据需要调整容量,不过两者的调整逻辑有所区别,Vector 扩容时会提高1倍,而ArrayList则是增加50%。
LinkedList:
- LinkedList顾名思义是Java提供的双向链表,所以它不需要像上面两种那样调整容量,它也不 是线程安全的。
2、集合框架的整体设计
List :
- 是有序集合,它提供了方便的访问、 插入、删除等操作。
Set:
-
Set是不允许重复元素的,这是和List最明显的区别,也就是不存在两个对象equals 返回true,我们在日常开发中有很多需要保证元素唯一的场合。
-
TreeSet支持自然顺序访问,但是添加、删除、包含等操作要相对低效(log(n)时间)。
-
HashSet则是利用哈希算法,理想情况下,如果哈希散列正常,可源供常数时间的添加、 删除、包含等操作,但是它不保证有序。
-
LinkedHashSet,内部构建了一个记录插入顺序的双向链表,因此提供了按照插入顺序遍历 的能力, 与此同时,也保证了常数时间的添加、删除、包含等操作,这些操作性能略低于 HashSet,因为需要维护链表的开销。
Queue/Deque:
-
则是Java提供的标准队列结构的实现,除了集合的基本功能,它还支持类 似先入先出(FIFO , First-in-First-Out)或者后入先出(LIFO , Last-In-First-Out) 行为。这里不包括BlockingQueue ,因为通常是并发编程场合,所以被放置在并发包里。
Map:(不属于集合框架)
-
TreeSet代码里实际默认是利用TreeMap实现的, Java类库创建了一个Dummy对象“PRESENT”作为value ,然后所有插入的元素其实是以键 的形式放入了 TreeMap里面;同理,HashSet其实也是以HashMap为基础实现的,原来他 们只是Map类的马甲!
-
hashMap:
- HashMap:HashMap是基于散列函数,以数组和链表的方式来存储key/value。线程非安全,允许null作为key和value,key不可以重复,value允许重复。
- 使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()
- HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。
- HashMap默认初始容量16,加载因子0.75,扩容为旧容量乘2,查找元素快,如果key一样则比较value,如果value不一样,则按照链表结构存储value,就是一个key后面有多个value;
TreeMap:
- 基于红黑二叉树的NavigableMap的实现,线程非安全。
- key不可以重复,value允许重复,存入TreeMap的元素应当实现Comparable接口或者实现Comparator接口,会按照排序后的顺序迭代元素,两个相比较的key不得抛出classCastException。
- 主要用于存入元素的时候对元素进行自动排序,迭代输出的时候就按排序顺序输出。
备注:
-
每种集合的通用逻辑,都被抽象到相应的抽象类之中,比如AbstractList就集中了各种List操 作的通用部分。
-
这些集合不是完全孤立的,比如,LinkedList本身,既是List, 也是Deque 哦。
3、如何使集合线程安全
-
除了java.util.concurrent里面的线程安全容 器,在 Collections工具类中,提供了一系列的synchronized方法,比如
static <T> List<T> synchronizedList(List<T> list)
List list = Collections.synchronizedList(new ArrayList());
-
它的实现,基本就是将每个基本方法,比如get、set、add之类,都通过synchronizd添加基本的同步支持,非常简单粗暴,但也非常实用。注意这些方法创建的线程安全集合,都符合迭代 时fail-fast行为(**fail-fast 机制是java Collection中的一种错误机制。**当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。),当发生意外的并发修改时,尽早抛出ConcurrentModificationException异 常,以避免不可预计的行为。
3、集合框架的演进
排序算法:
- Java 8引入了并行排序算法(直接使用parallelSort方法),这是为了充分利用现代多核多处理器的计算能力,底层实现基于fork-join框架, 当处理的数据集比较小的时候,差距不明显, 甚至表现差一点;但是,当数据集增长 数万或百万以上时,提高就非常大了,具体还是取决于处理器和系统环境。
集合框架演进:
-
在Java 8之中,Java平台支持了 Lambda和Stream ,相应的Java集合框架也进行了大范围 的增强,以支持类似为集合创建相应stream或者parallelstream的方法实现,我们可以非常 方便的实现函数式代码。链式编程
-
在Java 9中,Java标准类库提供了一系列的静态工厂方法,比如,List.of()、Set.of()、大大简 化了构建小的容器实例的代码量。根据业界实践经验,我们发现相当一部分集合实例都是容量非 常有限的,而且在生命周期中并不会进行修改。但是,在原有的Java类库中,我们可能不得不写成:
ArrayList<String> list = new ArrayList<>(); list.add("Hello"); list.add("World");
List<String> simpleList = List.of("Hello","world");
而利用新的容器静态工厂方法,一句代码就够了 ,并且保证了不可变性,线程安全。又因为不需要考虑扩容,所以空间上更加紧凑。
三、课后题
**Q:**比如 你需要实现一个云计算任务调度系统,希望可以保证VIP客户的任务被优先处理,你可以利用 哪些 结构或者标准的集合类型呢?类似场景大多是基于什么 结构呢?
A:在这个题目下,自然就会想到优先级队列(堆)了,但还需要额外考虑vip再分级,即同等级vip的 平权的问题,所以应该考虑除了直接的和vip等级相关的优先级队列优先级规则问题,还得考 虑同等级多个客户互相不被单一客户大量任务阻塞的问题,数据结构确实是基础,即便这个 思考题考虑的这个场景,待调度数据估计会放在redis里面吧
四、参考文档
- 极客时间《Java核心技术36讲》第8讲