学了一天关于集合方面的知识,纯属个人理解,总结一下
Collection接口:List、Set
List---ArrayList、LinkedList、Vector、Stack
Set---HashSet、TreeSet
Map接口:HashMap、TreeMap
Map不是集合
关于List和Set
List可通过下标进行对元素的访问,但插入元素时把后面的数据往后挪一位,比较困难
Set不能随机访问元素,但由于是链表结构,插入数据较为方便
Array和ArrayList
Array只能放入对象,ArrayList可以放入对象和基本数据类型。但Array对存储对象的方法进行封装能更加高效,所以我们在选择 集合类型时 先考虑 数据类型是什么。
Array是固定大小的,ArrayList有动态大小的,有自动扩容的方法
当指定大小时,建议使用Array
ArrayList和LinkedList和Vector
LinkedList 在插入和删除数据时效率更高,ArrayList 在查找某个 index 的数据时效率更高
LinkedList 比 ArrayList 需要更多的内存
Vector和ArrayList差不多相同,但Vector是线程安全的
TreeSet的不可重复性
public boolean add(E e) { return map.put(e, PRESENT)==null;// 调用HashMap的put方法,PRESENT是一个至始至终都相同的虚值 }
TreeSet的add()方法,底层实际上就是一个HashMap,但他的每个Value值都是一样的虚值(没有意义),因此如果Key相同的话,则会覆盖原来的。
当一个集合被作为参数传递给一个函数时,如何才可以确保函数不能修改它?
在作为参数传递之前,使用Collections.unmodifiableCollection(Collection c)方法创建一个只读集合,这将确保改变集合的任何操作都会抛出UnsupportedOperationException。
集合的转换
Collections静态类,用于操作 一个集合。如 集合的排序 Collection.sort()
Arrays静态类,用于操作一个数组。如 将数组 排序成集合 Arrays.sort()
fail-fast和fail-safe
迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量,如果数据结构发生了变化,将modCount变为expectedmodCount,
在下次遍历next()时,如果modCount != expectedmodCount,则会抛出异常,中止遍历。否则继续遍历
在遍历过程中,所有涉及到改变modCount值得地方全部加上synchronized, 或使用CopyOnWriteArrayList来替换ArrayList,则可以避免fail-fast机制
一般使用concurrent的集合都是fail-safe的,可在多线程下并发使用,并发修改
原理是,使用 集合时,并不是使用原来的集合,而是复制一个新的集合,用新的进行遍历,因此不会检测到对 原来集合数据的修改
HashMap的一些理解
使用HashMap初始化大小时,根据参数的二进制和负载因子进行扩容。
例如:
如果初始大小是10000时,传入10000条数据,HashMap是否会扩容?
答:并不会,如果初始化大小是10000,那根据底层的实现tableSizeFor,容量会变成2 ^14 * 0.75的大小,还有剩;
但如果是1000,则会是2^10*0.75,还不够1000;
因此我们初始大小时,应该选择 大小*0.75的容量来进行初始化