八股文 java八股面试 java集合篇

目录

集合

概述Java 集合

概览说说 List, Set, Queue, Map 四者的区别?

集合框架底层数据结构总结

list

set

queue

map

为什么要使用集合?

List

ArrayList 和 Array(数组)的区别?

ArrayList 和 Vector 的区别?(了解即可)

Vector 和 Stack 的区别?(了解即可)

ArrayList 可以添加 null 值吗?

ArrayList 插入和删除元素的时间复杂度?

LinkedList 插入和删除元素的时间复杂度?

LinkedList 为什么不能实现 RandomAccess 接口?

ArrayList 与 LinkedList 区别?

说一说 ArrayList 的扩容机制吧

Set

无序性和不可重复性的含义是什么

Queue

Queue 与 Deque 的区别

ArrayDeque 与 LinkedList 的区别

说一说 PriorityQueue

什么是 BlockingQueue?

Map(重要)

HashMap 和 Hashtable 的区别

HashMap 和 HashSet 区别

HashSet 如何检查重复?

HashMap 的底层实现

HashMap 多线程操作导致死循环问题

HashMap 为什么线程不安全?

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 是非线程安全的,且不支持存储 NULLnon-comparable 的对象。

PriorityQueue 默认是小顶堆,但可以接收一个 Comparator 作为构造参数,从而来自定义元素优先级的先后。

什么是 BlockingQueue?

BlockingQueue (阻塞队列)是一个接口,继承自 QueueBlockingQueue阻塞的原因是其支持当队列没有元素时一直阻塞,直到有元素;还支持如果队列已满,一直等到队列可以放入新元素时再放入。

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 值作比较,如果没有相符的 hashcodeHashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让加入操作成功。

HashMap 的底层实现

hashmap底层为动态数组+链表(java1.8之后引入红黑树,以缩短查询时间)

HashMap 多线程操作导致死循环问题

JDK1.7 及之前版本的 HashMap 在多线程环境下扩容操作可能存在死循环问题,这是由于当一个桶位中有多个元素需要进行扩容时,多个线程同时对链表进行操作,头插法可能会导致链表中的节点指向错误的位置,从而形成一个环形链表,进而使得查询元素的操作陷入死循环无法结束。

HashMap 为什么线程不安全?

JDK 1.8 后,在 HashMap 中,多个键值对可能会被分配到同一个桶(bucket),并以链表或红黑树的形式存储。多个线程对 HashMapput 操作会导致线程不安全,具体来说会有数据覆盖的风险。

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 提供了一些原子性的复合操作,如 putIfAbsentcomputecomputeIfAbsentcomputeIfPresentmerge等。这些方法都可以接受一个函数作为参数,根据给定的 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)//用新元素替换旧元素

声明:本文只作博主本人复习总结用,如有错误欢迎指出,

部分资料来源参考Java集合常见面试题总结(上) | JavaGuide(Java面试 + 学习指南)

Java集合常见面试题总结(下) | JavaGuide(Java面试 + 学习指南)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值