Java集合框架面试指南

Java集合框架面试指南

文章目录

  • Java集合框架面试指南
    • ArrayList特点:
    • LinkedList特点:
    • ArrayDeque特点:
    • PriorityQueue特点:
    • HashMap特点:
    • HashSet特点:
    • LinkedHashMap特点
      • LinkedHashMap经典用法
    • TreeMap特点
    • ConcurrentHashMap
      • JDK7中的ConcurrentHashMap是怎么保证并发安全的?
      • JDK7中的ConcurrentHashMap的底层原理
      • JDK8中的ConcurrentHashMap是怎么保证并发安全的?
      • JDK7和JDK8中的ConcurrentHashMap的不同点

人生的艰难困苦无法选择,但是可以自己无坚不摧,星光不问赶路人,时间不复有心人

Java集合类之间的关系

Collection接口

  • 是List,Set,Queue接口的父接口
  • 常用方法:添加对象add(),删除对象remove(),清空容器clear(),判断容易是否为空isEmpty()等

常见的一些集合类

ArrayList特点:

1. 底层是数组,数组里面是object对象,可以通过数组下标随机查重,是顺序容器,允许null元素
2. **非线程安全** ,建议在单线程中使用ArrayList,而在多线程中选择Vector或者CopyOnWriteArrayList
3. 是动态数组,可以实现动态增长,默认初始容量为10,每次扩容都是原来的1.5倍,数组扩容时会将老数组的元素重新拷贝到新的数组中
4. ArrayList没有push_back()方法,对应的方法是add(E e),ArrayList也没有insert()方法,对应的方法是add(int index, E e),set(int index, E element)对数组指定位置赋值。get(int index)获取指定位置的值。
5. remove()方法也有两个版本,一个是remove(int index)删除指定位置的元素,另一个是remove(Object o)删除第一个满足o.equals(elementData[index])的元素。
6. indexOf(Object o)获取元素的第一次出现的index,lastIndexOf(Object o)获取元素的最后一次出现的index

LinkedList特点:

1. 底层结构是**链表**,增删速度快
2. **非线程安全**
3. 是一个**双向**链表,也可以被当作堆栈、队列或双端队列,关于栈和队列首选ArrayDeque
4. 包含一个非常重要的内部类 Node<E>,是双向链表节点所对应的数据结构,其包括的属性有『当前节点所包含的值』、『上一个节点』、『下一个节点』
5. getFrist() 和 getLast() 获取第一个元素和获取最后一个元素
6. removeFrist() ,removeLast(),romove(e) ,remove(index) remove()方法也有两个版本,一个是删除跟指定元素相等的第一个元素remove(Object o),另一个是删除指定下标处的元素remove(int index)。
7. add() 一个是add(E e),该方法在LinkedList的末尾插入元素,因为有last指向链表末尾,在末尾插入元素的花费是常数时间。只需要简单修改几个相关引用即可;另一个是add(int index, E element),该方法是在指定下表处插入元素,需要先通过线性查找找到具体位置,然后修改相关引用完成插入操作。
8. indexOf(Object o)查找第一次出现的index, 如果找不到返回-1;

ArrayDeque特点:

1. ArrayDeque底层是通过循环数组实现的,非线程安全的,该容器不能放入null元素
2. addFirst(E e)的作用是在Deque的首端插入元素,也就是在head的前面插入元素,扩容函数doubleCapacity(),其逻辑是申请一个更大的数组(原数组的两倍),然后将原数组复制过去,addLast(E e)的作用是在Deque的尾端插入元素,也就是在tail的位置插入元素。
3. pollFirst()的作用是删除并返回Deque首端元素,也即是head位置处的元素。pollLast()的作用是删除并返回Deque尾端元素,也即是tail位置前面的那个元素。
4. peekFirst()的作用是返回但不删除Deque首端元素,也即是head位置处的元素,peekLast()的作用是返回但不删除Deque尾端元素,也即是tail位置前面的那个元素。

PriorityQueue特点:

1. 优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素)。
2. <font style="color:rgb(44, 62, 80);">不</font>允许放入null元素;其通过堆实现,具体说是通过完全二叉树(complete binary tree)实现的小顶堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值),**通过数组来作为PriorityQueue的底层实现。**
3. leftNo = parentNo*2+1	rightNo = parentNo*2+2	parentNo = (nodeNo-1)/2
4. PriorityQueue的peek()和element操作是常数时间,add(), offer(), 无参数的remove()以及poll()方法的时间复杂度都是log(N)。
5. add(E e)和offer(E e)的语义相同,都是向优先队列中插入元素,只是Queue接口规定二者对插入失败时的处理不同,前者在插入失败时抛出异常,后则则会返回false,新加入的元素x可能会破坏小顶堆的性质,因此需要进行调整。调整的过程为: 从k指定的位置开始,将x逐层与当前点的parent进行比较并交换,直到满足x >= queue[parent]为止。
6. element()和peek()的语义完全相同,都是获取但不删除队首元素,也就是队列中权值最小的那个元素,二者唯一的区别是当方法失败时前者抛出异常,后者返回null。根据小顶堆的性质,堆顶那个元素就是全局最小的那个;由于堆用数组表示,根据下标关系,0下标处的那个元素既是堆顶元素。**所以直接返回数组0下标处的那个元素即可。**
7. remove()和poll()方法的语义也完全相同,都是获取并删除队首元素,区别是当方法失败时前者抛出异常,后者返回null。由于删除操作会改变队列的结构,为维护小顶堆的性质,需要进行必要的调整,**从k指定的位置开始,将x逐层向下与当前点的左右孩子中较小的那个交换,直到x小于或等于左右孩子中的任何一个为止。**

HashMap特点:

1. 实现了Map、Cloneable(能被克隆)、Serializable(支持序列化)接口,**无序,**非线程安全,允许存在一个**为null的key和任意个为null的value**
2. 采用**链表和红黑树**实现,初始容量为16,填充因子默认是0.75 , 扩容时是当前容量翻倍,当链表长度大于8会树化,当树的节点小于6会进行链表化
3. get(object key) 首先通过hash() 函数得到对应的bucket的下标,然后通过key.equals(k) 方法判断是否是想要找的那个entry
4. put(K key, V value) 该方法首先会对map做一次查找,看是否包含该元组,如果已经包含则直接覆盖,如果是树节点则调用树的插入,否则遍历链表,**尾插法**,之后判断链表长度是否大于8,大于8则直接树化
5. 数组扩容,resize() 方法用于初始化数组或数组扩容,每次扩容后,容量为原来的 2 倍,并进行数据迁移。

HashSet特点:

1. HashSet是基于HasMap实现的,**是一个没有重复元素的集合,不保证元素的顺序,而且HashSet允许使用null元素,非同步的**
2. Set只使用到了HashMap的key,所以定义一个静态的常量Object类,来充当HashMap的value 
3. add(E e) 添加指定元素,clear() 移除set中所有的元素 clone() 返回HashSet实例的浅表副本,并没有复制这些元素本身  contains(Object o) 若此set包含指定元素则返回true  isEmpty() set若为空则返回true iterator() set中元素的迭代器  remove(Object o) 移除指定元素  size() 返回set元素的数量

LinkedHashMap特点

1. LinkedHashMap实现了Map接口,即允许放入key为null的元素,也允许插入value为null的元素。从名字上可以看出该容器是linked list和HashMap的混合体
2. 直接继承HashMap,唯一的区别是在HashMap的基础上,采用双向链表,将所有的entry连接起来了,保证元素的迭代顺序就是entry的插入顺序
3. put(K key,V value) 1. 从table的角度看,新的entry需要插入到对应的bucket里,当有哈希冲突时,采用头插法将新的entry插入到冲突链表的头部。2. 从header的角度看,新的entry需要插入到双向链表的尾部。
4. romove()1. 从table的角度看,需要将该entry从对应的bucket里删除,如果对应的冲突链表不空,需要修改冲突链表的相应引用。2. 从header的角度来看,需要将该entry从双向链表中删除,同时修改链表中前面以及后面元素的相应引用。

LinkedHashMap经典用法

LinkedHashMap除了可以保证迭代顺序外,还有一个非常有用的用法: 可以轻松实现一个采用了LRUCache替换策略的缓存。具体说来,LinkedHashMap有一个子类方法protected boolean removeEldestEntry(Map.Entry<K,V> eldest),该方法的作用是告诉Map是否要删除“最老”的Entry,所谓最老就是当前Map中最早插入的Entry,如果该方法返回true,最老的那个元素就会被删除。在每次插入新元素的之后LinkedHashMap会自动询问removeEldestEntry()是否要删除最老的元素。这样只需要在子类中重载该方法,当元素个数超过一定数量时让removeEldestEntry()返回true,就能够实现一个固定大小的LRUCache策略的缓存。示例代码如下:

class LRUCache extends LinkedHashMap {private int cap;public LRUCache(int capacity) {super(capacity,0.75f,true);cap = capacity;}public int get(int key) {return (int) super.getOrDefault(key,-1);}public void put(int key, int value) {super.put(key,value);}@Overrideprotected boolean removeEldestEntry(Map.Entry eldest) {return size()>cap;}
}

TreeMap特点

  • 是SortedMap接口的实现类
  • 有序,根据key对节点进行排序
  • 支持两种排序方法:自然排序定制排序。前者所有key必须实现Comparable接口且所有key应该是一个类的对象;后者通过传入一个Comparator接口对象负责对多有key进行排序。
  • 非线程安全
  • 采用红黑树的数据结构

ConcurrentHashMap

JDK7中的ConcurrentHashMap是怎么保证并发安全的?

主要利用Unsafe操作+ReentrantLock+分段思想。主要使用了Unsafe操作中的:

  1. compareAndSwapObject: 通过cas的方式修改对象的属性2 . putOrderedObject:并发安全的给数组的某个位置赋值3 . getObjectVolatile:并发安全的获取数组某个位置的元素分段思想是为了提高ConcurrentHashMap的并发量,

分段数越高则支持的最大并发量越高,程序员可以通过concurrencyLevel参数来指定并发量ConcurrentHashMap的内部类Segment就是用来表示某一个段的。每个Segment就是一个小型的HashMap的, 当调用ConcurrentHashMap的put方法是,最终会调用到Segment的put方法, 而Segment类继承了ReentrantLock, 所以Segment自带可重入锁, 当调用到Segment的put方法时,会先利用可重入锁加锁, 加锁成功后再将待插入的key ,value插入到小型HashMap中,插入完成后解锁。

JDK7中的ConcurrentHashMap的底层原理

ConcurrentHashMap底层是由两层嵌套数组来实现的:1. ConcurrentHashMap对象中有一个属性segments, 类型为Segment[];

2 . Segment对象中有一个属性table, 类型为HashEntry[];

当调用ConcurrentHashMap的put方法时,先根据key计算出对应的Segment[]的数组下标j,确定好当前key ,value应该插入到哪个Segment对象中,如果segments[j]为空,则利用自旋锁的方式在j位置生成一个 Segment对象。然后调用Segment对象的put方法。

Segment对象的put方法会先加锁, 然后也根据key计算出对应的HashEntry[]的数组下标i,然后将 key ,value封装为HashEntry对象放入该位置, 此过程和JDK7的HashMap的put方法一样, 然后解锁。 在加锁的过程中逻辑比较复杂, 先通过自旋加锁, 如果超过一 定次数就会直接阻塞等等加锁。

JDK8中的ConcurrentHashMap是怎么保证并发安全的?

主要利用Unsafe操作+synchronized关键字。Unsafe操作的使用仍然和JDK7中的类似,主要负责并发安全的修改对象的属性或数组某个位置的值。

synchronized主要负责在需要操作某个位置时进行加锁 (该位置不为空) , 比如向某个位置的链表进行插入结点,向某个位置的红黑树插入结点。JDK8中其实仍然有分段锁的思想,只不过JDK7中段数是可以控制的,而JDK8中是数组的每一个位置都有 一把锁。

当向ConcurrentHashMap中put一 个key ,value时,

1.首先根据key计算对应的数组下标i, 如果该位置没有元素, 则通过自旋的方法去向该位置赋值。

2.如果该位置有元素, 则synchronized会加锁

3.加锁成功之后, 在判断该元素的类型a. 如果是链表节点则进行添加节点到链表中b. 如果是红黑树则添加节点到红黑树

4.添加成功后,判断是否需要进行树化

5.addCount,这个方法的意思是ConcurrentHashMap的元素个数加1, 但是这个操作也是需要并发安全的,并且元素个数加1成功后,会继续判断是否要进行扩容, 如果需要,则会进行扩容,所以这个方法很重要。

6 . 同时一个线程在put时如果发现当前ConcurrentHashMap正在进行扩容则会去帮助扩容。

JDK7和JDK8中的ConcurrentHashMap的不同点

这两个的不同点太多了 . . ., 既包括了HashMap中的不同点, 也有其他不同点, 比如:

  1. JDK8中没有分段锁了, 而是使用synchronized来进行控制

2 . JDK8中的扩容性能更高, 支持多线程同时扩容, 实际上JDK7中也支持多线程扩容, 因为JDK7中的扩 容是针对每个Segment的, 所以也可能多线程扩容, 但是性能没有JDK8高, 因为JDK8中对于任意一个线程都可以去帮助扩容

3 . JDK8中的元素个数统计的实现也不一 样了, JDK8中增加了CounterCell来帮助计数, 而JDK7中没有, JDK7中是put的时候每个Segment内部计数, 统计的时候是遍历每个Segment对象加锁统计。

推荐阅读:Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

整理一下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/963.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

QEMU学习之路(4)— Xilinx开源项目systemctlm-cosim-demo安装与使用

QEMU学习之路&#xff08;4&#xff09;— Xilinx开源项目systemctlm-cosim-demo安装与使用 一、前言 项目说明&#xff1a;https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/862421112/Co-simulation 操作系统&#xff1a;Ubuntu 20.04.6 LTS gcc版本&#xff1a;9.4…

【Java知识】高性能网络应用框架Netty应知应会

文章目录 概述线程模型IO模型代码示例服务端代码示例客户端代码示例代码说明&#xff1a; 自定义协议实现自定义协议格式自定义编码器&#xff08;Encoder&#xff09;自定义解码器&#xff08;Decoder&#xff09;业务处理器&#xff08;Handler&#xff09;在Netty服务器管道…

AUTOSAR 规范中的设计模式:传感器执行器模式

在 AUTOSAR Adaptive Platform (AP) 规范中,传感器执行器模式是一种典型的设计模式,主要用于实时控制系统中,用来实现传感器数据的获取和执行器指令的发送。该模式通过分离传感器和执行器的实现,使其独立运行并且能够通过某种通信机制进行数据交换,以确保数据的实时性和系…

Linux:编辑器Vim和Makefile

✨✨所属专栏&#xff1a;Linux✨✨ ✨✨作者主页&#xff1a;嶔某✨✨ vim的三种常用模式 分别是命令模式&#xff08;command mode&#xff09;、插入模式&#xff08;Insert mode&#xff09;和底行模式&#xff08;last line mode&#xff09; 各模式的功能区分如下&…

【Linux】【进程控制】API汇总整理

进程控制是操作系统中一个非常重要的概念&#xff0c;它涉及到创建、管理和终止进程的能力。进程控制包括一系列操作&#xff0c;如创建新进程、等待进程结束、发送信号给进程等。下面是进程控制中一些常见的操作及其相关API&#xff1a; 进程控制概述 进程控制是指操作系统提…

HTML练习题:彼岸的花(web)

展示效果: 代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>彼岸の花</title><style…

【安全性分析】正式安全分析与非正式安全分析

安全性分析-系列文章目录 第一章 【安全性分析】正式安全分析与非正式安全分析 第二章 【安全性分析】BAN逻辑 (BAN Logic) 文章目录 安全性分析-系列文章目录前言一、正式安全分析1. 理想化模型(如随机预言机模型)2. 标准模型(Standard Model)3. 形式化验证4. 数学证明二…

HTML小阶段二维表和思维导图

下面是对标签、元素、属性的对比二维表&#xff0c;通过对比3w1h&#xff08;what是什么、where用在哪、why为什么要用、how如何用&#xff09;来学习区分学习标签、元素、属性 标签 元素 属性 what &#xff08;Tags&#xff09;标签是用来标记内容块或标明元素内容意义 …

NIO 核心知识总结

在传统的 Java I/O 模型&#xff08;BIO&#xff09;中&#xff0c;I/O 操作是以阻塞的方式进行的。也就是说&#xff0c;当一个线程执行一个 I/O 操作时&#xff0c;它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈&#xff0c;因为需要为每个连…

助力风力发电风机设备智能化巡检,基于YOLOv7全系列【tiny/l/x】参数模型开发构建无人机巡检场景下风机叶片缺陷问题智能化检测预警模型

在全球能源转型的大潮中&#xff0c;清洁环境能源的发展已成为各国关注的焦点。风力发电作为其中的佼佼者&#xff0c;以其可再生、无污染的特点&#xff0c;受到了广泛的青睐。然而&#xff0c;风力发电设施大多建于人迹罕至的地区&#xff0c;设备庞大且复杂&#xff0c;其维…

Apache POI(java操作Miscrosoft Office)

Apache POI 1.1 介绍 Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是&#xff0c;我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。 一般情况下&#xff0c;POI 都是用于操作 Excel 文件。 Apache POI 的应用场景&a…

C++ | Leetcode C++题解之第537题复数乘法

题目&#xff1a; 题解&#xff1a; class Solution { public:string complexNumberMultiply(string num1, string num2) {regex re("\\|i"); vector<string> complex1(sregex_token_iterator(num1.begin(), num1.end(), re, -1), std::sregex_token_iterator…

Java ==> String类(字符串)

文章目录 一、认识String类1、创建String对象2、不可变的String3、字符串常量池 二、字符串常用操作1、字符串比较1.1 用“”比较1.2 用equals()方法比较1.3用compareTo()方法进行比较 2、获取字符串长度3、字符串查找4、字符串转换4.1valueOf()数值转换为字符串4.2字母大小写转…

Qt中的Model与View 4:QStandardItemModel与QTableView

目录 QStandardItemModel API QTableView 导航 视觉外观 坐标系统 API 样例&#xff1a;解析一个表格txt文件 QStandardItemModel QStandardItemModel 可用作标准 Qt 数据类型的存储库。它是模型/视图类之一&#xff0c;是 Qt 模型/视图框架的一部分。它提供了一种基于…

【SpringMVC】传递json,获取url参数,上传文件

【传递json数据】 【json概念】 一种轻量级数据交互格式&#xff0c;有自己的格式和语法&#xff0c;使用文本表示一个对象或数组的信息&#xff0c;其本质上是字符串&#xff0c;负责在不同的语言中数据传递与交换 json数据以字符串的形式体现 【json字符串与Java对象互转…

Java JUC(四) 自定义线程池实现与原理分析

目录 一. 阻塞队列 BlockingQue 二. 拒绝策略 RejectPolicy 三. 线程池 ThreadPool 四. 模拟运行 在 Java基础&#xff08;二&#xff09; 多线程编程 中&#xff0c;我们简单介绍了线程池 ThreadPoolExecutor 的核心概念与基本使用。在本文中&#xff0c;我们将基于前面学…

go-logger v0.27.0 - 并发性能为官方库 10 倍

go-logger是一个高性能的 golang 日志库&#xff0c;旨在提供快速、轻量级的日志记录功能 Github 使用文档 v0.27.0 更新内容 优化内存分配优化写数据性能增加日志属性自定义函数增加各个日志级别格式化打印函数 说明 性能优化是该版本最重要的更新内容。性能优化的结果&…

【华为HCIP实战课程31(完整版)】中间到中间系统协议IS-IS路由汇总详解,网络工程师

一、IS-IS的汇总 1、可以有效减少在LSP中发布的路由条目,减小对系统资源的占用。 2、会减少LSP报文的扩散,接收到该LSP报文的其他设备路由表中只会出现一条聚合路由。 3、可以避免网络中的路由震荡,提高了网络的稳定性。 4、被聚合的路由可以是IS-IS路由,也可以是被引入…

邮件发送excel带预览excel功能

excel 打开后的内容: 思路&#xff1a; 1、邮件发送excel 是作为附件发送出去的&#xff1b; 2、excel 预览是&#xff0c;必须另外点击预览按钮&#xff0c;并不能直接预览邮件内容然后在邮件主体内容展示出来 根据以上两点基本没法实现 邮件发送后邮件自带 预览功能。 伪方法…

HCIA(DHCP服务)

第三节 开启DHCP服务 创建地址池 调用全局服务 [R1]dhcp enable 开启DHCP服务 [R1]ip pool AA 创建地址池 [R1-ip-pool-AA]network 192.168.1.0 mask 24 写入网段 [R1-ip-pool-AA]gateway-list 192.168.1.1 写入网关 [R1-ip-pool-AA]dns-list 8.8.8.8 114.1…