原文链接:https://javaguide.cn

List
Set
Queue
Map
当我们需要保存一组类型相同的数据的时候,由于数据的类型是多种多样的,所以我们使用了集合。集合提高了数据存储的灵活性,还可以保存具有映射关系的数据。
(1)是否保证线程安全: ArrayList和LinkedList都是不同步的、线程不安全的。
(2)底层实现: ArrayList底层使用Object[]数组实现,LinkedList底层使用双向链表实现。(jdk1.6之前是双向循环链表,1.6之后取消了循环)。
(3)是否支持快速随机访问: ArrayList底层使用数组实现,支持快速访问。而LinkedList底层是链表实现,不支持快速访问。
(4)空间内存占用:ArrayList的空间浪费主要体现在list列表结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素存储都需要比ArrayList消耗更多的空间(因为节点要放置前驱和后继)。
通过查看源码我们可以知道,Arrays.copyOf()方法内部调用了System.arraycopy()方法。
arraycopy()方法需要目标数组,将原数组拷贝到自定义的数组中或者是原数组,并且可以选择拷贝的起点以及放入新的数组中的位置。
copyOf()时系统自动在内部新建一个数组,并返回该数组。
拓展:
ArrayList实现了RandomAccess接口,只是作为一个标识,说明ArrayList支持快速随机访问功能。
ensureCapacity()方法并不是在ArrayList内部调用的,而是提供给用户来使用的,在向ArrayList里面添加大量元素之前最好先使用ensureCapacity方法,以减少增量重新分配的次数,提高效率。
当使用无参构造方法直接创建ArrayList集合时,初始化的数组是一个空的数组,只有在add第一个元素时,数组的大小才会初始化为10,直到添加第11个元素时,ArrayList才会进行扩容操作。
当我们需要对一个集合进行自定义排序后,我们就要重写CompareTo或者是compare方法。
(1)无序性?无序性不等于随机性,无序性是指存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
(2)不可重复性?不可重复性是指添加的元素按照equals判断时,返回false,需要同时重写equals方法和hashCode方法。
Queue是单端队列,只能从一端插入元素,另一端删除元素,遵循FIFO的规则。
Queue根据容量问题而导致操作失败后的处理方式不同。一种抛出异常,一种返回null。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zzn588QN-1642240006941)(F:笔记深入java基础面试题截图Queue接口.png)]
Deque是双端队列,在队列的两端均可以插入或者删除元素。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RQh5h1Ya-1642240006942)(F:笔记深入java基础面试题截图Deque方法.png)]
实际上,Deque还提供了其他的方法,可以当作栈来使用。
ArrayDeque和LinkedList都实现了Deque接口,两者都可以当作队列来使用。
从性能上来说,ArrayDeque来实现队列要比LinkedList要好一点,另外,ArrayDeque也可以直接用作栈。
PriorityQueue是在jdk1.5被引入的,与Queue得区别在于元素出队顺序是与优先级相关得,即总是优先级高的元素出队列。
HashSet底层就是基于HashMap实现的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C4FWRos7-1642240006943)(F:笔记深入java基础面试题截图HashMap和HashSet的区别.png)]
HashMap和TreeMap都继承于AbstractMap,但是TreeMap还实现了NavigableMap接口和SortedMap接口。
相比来说,TreeMap多了对集合中元素根据键排序的功能和对集合元素进行搜索的能力。
当把对象加入到HashSet中时,HashSet会先计算对象的hashCode值来判断对象加入的下标位置,同时也会与其他的对象的hashCode进行比较,如果没有相同的,就直接插入数据;如果有相同的,就进一步使用equals来进行比较对象是否相同,如果相同,就不会加入成功。
(1)如果两个对象相等,则hashCode也一定是相等的
(2)两个对象相等,对两个进行equals也会返回true
(3)两个对象有相同的hashCode,他们也不一定是相等的
(4)hashCode和equals方法都必须被同时覆盖
对于基本类型来说,==比较的是值是否相等;
对于引用类型来说,equals比较的是两个引用是否指向同一个对象地址
对于引用类型(包括包装类型)来说,如果equals没有被重写,对比他们的地址是否相同,如果被重写,则比较的是地址里的内容是否相同。
为了能让HashMap存取高效,尽量减少哈希碰撞,尽量把数据均匀分配。
主要原因是并发下的Rehash会造成元素之间形成一个循环链表。多线程下HashMap会存在数据丢失的问题,并发环境下推荐使用Concurrent Hash Map、
ConcurrentHashmap和Hashtable的区别主要体现在实现线程安全的方式上不同
会产生哈希碰撞。
如果Hash码相同,则会通过equals方法进行比较key是否相同:
如果key值相同,则使用新的value代替旧的value;
如果key值不相同,则会在该节点的链表结构上新增一个节点(如果链表长度>=8并且数组节点数>=64 ,链表结构就会转化成红黑树)。
只要通过hash函数计算所得到的两个元素的hash值相同就会产生hash碰撞。
HashMap在jdk8之前采用链表解决哈希碰撞,jdk8之后采用链表+红黑树解决哈希碰撞。
当hashCode值相同时,就会进一步使用equals方法进行比较key是否相同。
如果key相同,那么新值覆盖旧值;
如果key不相同,则将新的key-value添加到HashMap中。
当通过put方法不断进行数据添加时,如果元素个数超过了当前阈值就会进行扩容,默认扩容大小是原来的2倍,扩容之后会将原来的数据复制到新的数组中。
应该设置初始容量为 n/0.75 + 1 取整即可减少resize导致的性能损失。
常用方法:
void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面
int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target)
boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素
集合判断是否为空,使用isEmpty()的方法,而不是size==0的方式
因为isEmpty()方法的可读性好,而且时间复杂度为O(1)
使用Java.util.stream.Collectors类的toMap()方法转化为map集合时,要注释当value为null时会抛出NPE异常。
不要在foreach里面进行元素的remove/add操作,remove请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
fail-fast 机制 :多个线程对 fail-fast 集合进行修改的时候,可能会抛出ConcurrentModificationException。
可以利用Set元素唯一的特性,进行集合去重,避免使用List的contains进行遍历去重或者判断包含操作
使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的类型完全一致,长度为0的空数组。
toArray(T[] array) 方法的参数是一个泛型数组,如果 toArray 方法中没有传递任何参数的话返回的是 Object类 型数组。
使用工具类Arrays.asList()把数组转化成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。