目录
概览说说 List, Set, Queue, Map 四者的区别?
LinkedList 为什么不能实现 RandomAccess 接口?
ConcurrentHashMap 和 Hashtable 的区别
ConcurrentHashMap 线程安全的具体实现方式/
底层具体实现JDK 1.7 和 JDK 1.8 的 ConcurrentHashMap 实现有什么不同?
ConcurrentHashMap 为什么 key 和 value 不能为 null?
ConcurrentHashMap 能保证复合操作的原子性吗?
集合
概述Java 集合
Java 集合, 也叫作容器,主要是由两大接口派生而来:
collection: list set queue
map
概览说说 List, Set, Queue, Map 四者的区别?
list有序可重复
set无序不可重复
queue先进先出的队列
map k-v数据结构的键值对
集合框架底层数据结构总结
list
arraylist:object[]数组;
vector:object[]数组;
linkedlist:双向链表
set
hashset:基于hashmap实现的,底层采用hashmap来保存元素;
linkedhashset:是hashset的子类,并且其内部是通过linkedhashmap来实现的
treeset:(有序且唯一)红黑树。
queue
priorityqueue:object[]数组实现二叉堆;
arrayqueue:object[]数组 + 双指针
map
hashmap:1.8前采用数组 + 链表组成,数组是hashmap的主体,链表是为了解决hash冲突而存在(拉链法)。1.8后对于哈希冲突有了较大的变化,当链表长度大于8数组长度大于64时会将链表转换为红黑树,以减少搜索时间。
linkedhashmap: 继承自hashmap,底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成,另外linkedhashmap增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。
hashtable:数组+链表组成,数组为主体,链表解决hash冲突。
treemap:红黑树
为什么要使用集合?
我们需要存储一组类型相同的数据时,数组是最常用且最基本的容器之一。但是,使用数组存储对象存在一些不足之处,因为在实际开发中,存储的数据类型多种多样且数量不确定。这时,Java 集合就派上用场了。与数组相比,Java 集合提供了更灵活、更有效的方法来存储多个数据对象。Java 集合框架中的各种集合类和接口可以存储不同类型和数量的对象,同时还具有多样化的操作方式。相较于数组,Java 集合的优势在于它们的大小可变、支持泛型、具有内建算法等。总的来说,Java 集合提高了数据的存储和处理灵活性,可以更好地适应现代软件开发中多样化的数据需求,并支持高质量的代码编写。
List
ArrayList 和 Array(数组)的区别?
arraylist为基于object动态数组,array为静态数组。
arraylist会根据实际存储的元素动态的扩容和缩容,而array被创建之后就不能改变他的长度。
arraylist有序使用泛型来确保类型安全,array则不可以。
arraylist只能存储对象,array既可以存储对象,也可以存储基本数据结构。
arraylist支持插入、删除、遍历等常见操作,并且提供了丰富的api操作方法,array只是一个固定长度的数组,只能按照下标访问其中的元素,不具备动态添加、删除元素的能力。
arraylist创建时不需要指定大小、而array创建时必须指定大小。
ArrayList 和 Vector 的区别?(了解即可)
后者为线程安全的数组
Vector 和 Stack 的区别?(了解即可)
二者都是线程安全的,都是使用了synchronized关键字进行同步处理。
stack继承自vector,是一个后进先出的栈,而vector是一个列表。
ArrayList 可以添加 null 值吗?
可以,不过不建议向arraylist中添加null值,因为null值并没有意义,只会让代码更难以维护,比如忘记做判空处理就会导致空指针异常。
ArrayList 插入和删除元素的时间复杂度?
插入
如果在空间满足的情况下在数组末尾插入则为0(1)
如果在空间满足的情况下在队首插入,由于arraylist采用索引的方式存储,则其后所有数据都需要改变索引,所以为0(n)
如果在空间满足的情况下插入队中,则需改变从插入出起的后半部分所以,所以为0(n)
删除
头部删除:由于需要将所有元素依次向前移动一个位置,因此时间复杂度是 O(n)。
尾部删除:当删除的元素位于列表末尾时,时间复杂度为 O(1)。
指定位置删除:需要将目标元素之后的所有元素向前移动一个位置以填补被删除的空白位置,因此需要移动平均 n/2 个元素,时间复杂度为 O(n)。
LinkedList 插入和删除元素的时间复杂度?
由于linkedlist采用的是链表的方式进行数据存储所以插入和删除的效率相比arraylist要高很多
如果操作位置为队首或队尾则为0(1)
如果为队中则须断开两边链表再进行操作时,需要先移动到指定位置,再进行修改,所以时间复杂度为0(n)
LinkedList 为什么不能实现 RandomAccess 接口?
randomaccess为随机访问,由于其底层为链表,顾在内存空间上是不满足连续的只能通过指针来定位,所以想要访问只能依次遍历,
arraylist采用索引存储,在内存空间上为连续的,所以支持randomaccess
ArrayList 与 LinkedList 区别?
二者都为非线程安全的数据集合
arraylist底层使用的是object数组,linkedlist底层使用的是双向链表数据结构
arraylist采用数组存储,所以插入和删除元素时的时间复杂度受元素位置的影响
linkedlist采用链表存储,所以再头尾插入或者删除元素不受元素位置的影响
arraylist支持随机访问(randomAccess)linkedlist则不支持
arraylist的空间浪费体现在预留的空间,linkedlist则是需要空间存储前驱和后继的数据。
说一说 ArrayList 的扩容机制吧
arraylist在创建是容量为0,初始化后得到一个容量为10的数组,当空间不足时,会触发扩容,按照1.5的倍数进行扩容。
arraylist的扩容本质上是将新数据和旧数据进行复制,然后将其粘贴到新生成的数组内,所以是代价很大的一种扩容方式,一般情况下,为了保证效率和安全性,会在数组定义时,手动按照需要存储的数据大概容量对arraylist进行提前定义容量。
arraylist同样支持缩容,不过他不会被自动触发,就算容量为1000对数组只存住一个值也不会,需要手动调用方法。
Set
无序性和不可重复性的含义是什么
无序性不等于随机性 ,无序性是指存储的数据在底层数组中并非按照数组索引的顺序添加 ,而是根据数据的哈希值决定的
不可重复性是指添加的元素按照 equals()
判断时 ,返回 false,需要同时重写 equals()
方法和 hashCode()
方法
Queue
Queue 与 Deque 的区别
queue为先进先出的队列,只能从一端插入元素,另一端删除元素,实现上一般遵循 先进先出(FIFO) 规则
deque为双端队列,可以在队列的两边进行存入和取出
ArrayDeque 与 LinkedList 的区别
ArrayDeque
是基于可变长的数组和双指针来实现,而 LinkedList
则通过链表来实现。
ArrayDeque
不支持存储 NULL
数据,但 LinkedList
支持。
ArrayDeque
是在 JDK1.6 才被引入的,而LinkedList
早在 JDK1.2 时就已经存在。
ArrayDeque
插入时可能存在扩容过程, 不过均摊后的插入操作依然为 O(1)。虽然 LinkedList
不需要扩容,但是每次插入数据时均需要申请新的堆空间,均摊性能相比更慢。
从性能的角度上,选用 ArrayDeque
来实现队列要比 LinkedList
更好。此外,ArrayDeque
也可以用于实现栈
说一说 PriorityQueue
元素出队顺序是与优先级相关的,即总是优先级最高的元素先出队。
PriorityQueue
利用了二叉堆的数据结构来实现的,底层使用可变长的数组来存储数据
PriorityQueue
通过堆元素的上浮和下沉,实现了在 O(logn) 的时间复杂度内插入元素和删除堆顶元素。
PriorityQueue
是非线程安全的,且不支持存储 NULL
和 non-comparable
的对象。
PriorityQueue
默认是小顶堆,但可以接收一个 Comparator
作为构造参数,从而来自定义元素优先级的先后。
什么是 BlockingQueue?
BlockingQueue
(阻塞队列)是一个接口,继承自 Queue
。BlockingQueue
阻塞的原因是其支持当队列没有元素时一直阻塞,直到有元素;还支持如果队列已满,一直等到队列可以放入新元素时再放入。
Map(重要)
HashMap 和 Hashtable 的区别
区别在于高并发情况下的线程安全性hashtable通过synchronized保证了线程安全
因为线程安全问题,hashmap要比hashtable效率高
hashmap可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个
hashtable则不允许有null间和null值。
HashMap 和 HashSet 区别
hashset实际上是对hashmap的简单包装,底层调用的大部分还是hashmap的方法
除了:clone()
、writeObject()
、readObject()
HashSet 如何检查重复?
首先通过hashcode进行哈西码的查询,如果相同则调用equals方法进行判断
当你把对象加入HashSet
时,HashSet
会先计算对象的hashcode
值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode
值作比较,如果没有相符的 hashcode
,HashSet
会假设对象没有重复出现。但是如果发现有相同 hashcode
值的对象,这时会调用equals()
方法来检查 hashcode
相等的对象是否真的相同。如果两者相同,HashSet
就不会让加入操作成功。
HashMap 的底层实现
hashmap底层为动态数组+链表(java1.8之后引入红黑树,以缩短查询时间)
HashMap 多线程操作导致死循环问题
JDK1.7 及之前版本的 HashMap
在多线程环境下扩容操作可能存在死循环问题,这是由于当一个桶位中有多个元素需要进行扩容时,多个线程同时对链表进行操作,头插法可能会导致链表中的节点指向错误的位置,从而形成一个环形链表,进而使得查询元素的操作陷入死循环无法结束。
HashMap 为什么线程不安全?
JDK 1.8 后,在 HashMap
中,多个键值对可能会被分配到同一个桶(bucket),并以链表或红黑树的形式存储。多个线程对 HashMap
的 put
操作会导致线程不安全,具体来说会有数据覆盖的风险。
ConcurrentHashMap 和 Hashtable 的区别
二者都是线程安全的数据结构
但是相比于hashtable这种古老的、官方不再推荐的数据结构,concurrenthashmap拥有更好的性能,并且concurrenthashmap通过锁定头节点这种更小的粒度,实现了牺牲小部分性能的情况下保证了线程安全。
ConcurrentHashMap 线程安全的具体实现方式/
1.8之前采用分段锁
1.8后采用对头节点的锁定
底层具体实现JDK 1.7 和 JDK 1.8 的 ConcurrentHashMap 实现有什么不同?
1.7为数组+链表
ConcurrentHashMap
是由 Segment
数组结构和 HashEntry
数组结构组成。
1.8为数组+链表+红黑树
ConcurrentHashMap
取消了 Segment
分段锁,采用 Node + CAS + synchronized
来保证并发安全。数据结构跟 HashMap
1.8 的结构类似,数组+链表/红黑二叉树。Java 8 在链表长度超过一定阈值(8)时将链表转换为红黑树
ConcurrentHashMap 为什么 key 和 value 不能为 null?
为了排除二义性,查询出的null值无法判断意义
可能当前key没有值
也可能表示当前key的值就是null
ConcurrentHashMap 能保证复合操作的原子性吗?
ConcurrentHashMap
提供了一些原子性的复合操作,如 putIfAbsent
、compute
、computeIfAbsent
、computeIfPresent
、merge
等。这些方法都可以接受一个函数作为参数,根据给定的 key 和 value 来计算一个新的 value,并且将其更新到 map 中。
排序操作查找
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)//用新元素替换旧元素
声明:本文只作博主本人复习总结用,如有错误欢迎指出,