Java集合面试题(一)

1. Java 中常用的容器有哪些?

在 Java 中,容器是一种特殊的数据结构,用于存储其他对象。它们可以帮助我们更高效地管理和操作大量的数据。以下是 Java 中常用的几种容器:

  1. List:有序集合(也是动态数组),元素可以重复。List 里的元素都有索引,元素可以重复,允许空元素。常用的实现类有 ArrayListLinkedListVector

    • ArrayList:基于动态数组实现,查询效率高,增删效率低。
    • LinkedList:基于链表实现,增删效率高,查询效率低。
    • Vector:是线程安全的,但性能相对较低。
  2. Set:无序集合,不允许元素重复。Set 中的元素没有索引,元素不可以重复,不允许空元素。常用的实现类有 HashSetLinkedHashSetTreeSet

    • HashSet:基于哈希表实现,性能较好。
    • LinkedHashSet:用链表维护元素的插入顺序,在遍历的时候可以按元素的插入顺序遍历。
    • TreeSet:基于红黑树实现,可以对元素进行排序。
  3. Queue:队列,先进先出(FIFO)。队列中的元素不可以重复,元素是有序的,遵循先入先出的原则。常用的实现类有 LinkedListPriorityQueue 等。

    • LinkedList:可以作为队列使用,提供 addFirstaddLastremoveFirstremoveLast 等方法。
    • PriorityQueue:是一个无界的队列,用于按自然顺序或比较器的顺序进行排序。
  4. Map:存储键值对(key-value pair)的集合,键不可以重复,值可以重复。常用的实现类有 HashMapLinkedHashMapTreeMapHashtable

    • HashMap:基于哈希表实现,性能较好。
    • LinkedHashMap:用链表维护键值对的插入顺序,或者访问顺序(由构造函数的 accessOrder 参数决定)。
    • TreeMap:基于红黑树实现,可以对键进行排序。
    • Hashtable:是线程安全的,但性能相对较低。

这些容器类都提供了丰富的 API,可以方便地进行元素的添加、删除、查找等操作。在选择容器时,应根据具体的需求和场景来选择合适的容器类型。

2. ArrayList 和 LinkedList 的区别?

ArrayList和LinkedList都是Java中的List接口的实现,用于存储元素的动态数组,但它们在内部实现、性能特性以及使用场景上有一些关键的区别。

  1. 内部实现

    • ArrayList:基于动态数组实现。在内存中,ArrayList分配一块连续的内存空间来存储元素。当添加或删除元素时,如果需要,ArrayList会进行数组的扩容或元素的移动。
    • LinkedList:基于双向链表实现。每个元素都包含对前一个元素和后一个元素的引用。因此,LinkedList的元素在内存中不需要连续存储。
  2. 性能特性

    • 访问元素:对于ArrayList,由于元素在内存中连续存储,因此通过索引访问元素(get和set操作)非常快,时间复杂度为O(1)。而LinkedList则需要从头或尾开始遍历,直到找到目标元素,时间复杂度为O(n)。
    • 插入和删除元素:在ArrayList的开头或中间插入或删除元素可能涉及到元素的移动,因此时间复杂度为O(n)。然而,在ArrayList的末尾添加或删除元素通常很快,因为不需要移动其他元素。相反,LinkedList在开头或中间插入或删除元素只需要修改几个引用,时间复杂度为O(1)。但是,在LinkedList的末尾添加或删除元素可能需要遍历到末尾,时间复杂度为O(n)。
  3. 内存使用:LinkedList由于每个元素都包含额外的引用(指向前一个元素和后一个元素),因此相比ArrayList会占用更多的内存。然而,当ArrayList进行扩容时,它可能需要分配更大的连续内存空间,这可能导致一些额外的开销。

  4. 线程安全:ArrayList和LinkedList都不是线程安全的。如果需要在多线程环境中使用,需要额外的同步措施。Java提供了Collections.synchronizedList()方法来创建一个线程安全的列表,或者可以使用CopyOnWriteArrayListConcurrentLinkedDeque等并发集合类。

  5. 使用场景

    • 如果你需要频繁地通过索引访问元素,并且元素的数量可能会变化,那么ArrayList可能是一个更好的选择。
    • 如果你需要频繁地在列表的开头或中间插入或删除元素,那么LinkedList可能更合适。
    • 如果你需要实现一个队列或双端队列,LinkedList是一个很好的选择,因为它提供了在头部和尾部添加和删除元素的高效操作。

3. ArrayList 实现 RandomAccess 接口有何作用?为何 LinkedList 却没实现这个接口?

RandomAccess 接口在 Java 中是一个标记接口,它本身并没有定义任何方法。当一个类实现了 RandomAccess 接口,它仅仅是表明这个类的实例支持快速随机访问其元素。具体来说,如果一个类实现了 RandomAccess 接口,那么使用 get(int index) 方法访问其元素时,通常期望能在常数时间内完成,而不依赖于集合的大小。

对于 ArrayList 来说,由于它基于动态数组实现,可以通过索引直接访问数组中的元素,因此其 get(int index) 方法的时间复杂度是 O(1)。这使得 ArrayList 是一个适合快速随机访问的集合。因此,ArrayList 实现了 RandomAccess 接口,以表明它支持这种高效的随机访问特性。

相反,LinkedList 是基于链表实现的。在链表中,访问特定索引位置的元素需要从头节点开始遍历链表,直到到达目标位置。因此,LinkedListget(int index) 方法的时间复杂度是 O(n),其中 n 是链表的大小。由于 LinkedList 不支持高效的随机访问,所以它没有实现 RandomAccess 接口。

在实现算法或数据结构时,了解一个集合是否实现了 RandomAccess 接口是有用的。例如,在排序算法中,如果知道一个集合支持快速随机访问,那么可能会选择一种不同的排序策略,比如归并排序或快速排序,而不是基于交换元素的排序算法(如冒泡排序或插入排序)。因为对于支持快速随机访问的集合,通过直接访问和交换元素可以更加高效地执行排序操作。

4. ArrayList 的扩容机制?

ArrayList的扩容机制是为了解决在动态添加元素过程中可能出现的空间不足问题。当ArrayList中的元素数量达到当前数组容量时,如果再尝试添加新元素,就会触发扩容操作。以下是ArrayList扩容机制的主要步骤:

  1. 检查容量:当向ArrayList添加新元素时,首先会检查当前元素数量是否已经达到了数组的容量上限。如果未达到上限,则直接添加新元素。如果达到了上限,就进入扩容流程。

  2. 计算新容量:ArrayList扩容时,通常会创建一个新的数组,其大小是原数组容量的1.5倍。这个增长因子(1.5)可以根据具体的实现或JVM版本有所不同,但目的是为了在空间利用和扩容开销之间找到一个平衡。

  3. 复制元素:扩容操作开始后,ArrayList会将原数组中的所有元素复制到新的数组中。这个过程通常使用System.arraycopy()方法完成,这是一种高效的内存块复制操作。

  4. 更新引用:复制完成后,ArrayList会更新其内部的数组引用,使其指向新的、容量更大的数组,并丢弃旧的数组。

  5. 添加新元素:现在,ArrayList拥有了更大的容量,可以将新的元素添加到扩容后的ArrayList中,而不会导致容量不足的问题。

需要注意的是,虽然ArrayList的自动扩容机制使得其可以动态地增长以适应元素数量的变化,但这种扩容操作并不是没有代价的。每次扩容都涉及到内存的申请和已有元素的复制,这在处理大量数据时可能会成为性能瓶颈。因此,在实际使用中,如果预知需要添加大量元素,最好通过构造方法或ensureCapacity()方法预先指定一个较大的初始容量,以减少扩容操作的次数,从而提高性能。

5. Array 和 ArrayList 有何区别?什么时候更适合用 Array?

Array和ArrayList在Java中都是用于存储数据的集合类型,但它们之间存在一些重要的区别,这些区别决定了在特定情况下应该选择使用哪一种。

  1. 大小和可变性

    • Array的大小在声明时就已经确定,并且之后无法改变。这意味着一旦你创建了一个固定大小的Array,你就不能增加或减少它的容量。
    • ArrayList的大小则是可变的。它基于动态数组实现,因此可以根据需要增长或缩小。这使得ArrayList在处理大小不确定或需要动态调整大小的数据集时更为灵活。
  2. 数据类型

    • Array可以是任何类型的对象,包括基本数据类型(如int、char等)的数组。
    • ArrayList则只能存储引用类型的对象。它不能直接存储基本数据类型,但可以存储它们的包装类(如Integer、Character等)。
  3. 数据访问

    • Array和ArrayList都支持通过索引直接访问元素。Array可以直接使用索引访问,而ArrayList则提供了get(int index)方法来获取指定索引处的元素。
  4. 内存管理

    • Array直接存储在内存中,其地址是连续的。这种连续的内存布局使得数组在查询操作时效率较高。
    • ArrayList则基于动态数组实现,其底层仍然是一个数组,但根据需要可以动态调整大小。因此,ArrayList需要额外的空间来存储对象本身以及维护元素的新增和删除操作所需的额外开销。
  5. 性能

    • 对于已知大小且不会改变的数据集,Array通常具有更好的性能,因为它的内存布局是连续的,且没有额外的空间开销。
    • ArrayList在动态调整大小时可能会涉及到数据的复制和重新分配内存,这可能会带来一定的性能开销。

基于上述区别,以下是一些建议的使用场景:

  • 适合使用Array的情况

    • 当你知道数据集的大小是固定的,并且不会改变时。
    • 当需要高效地进行连续内存访问时,例如进行大量的数学运算或图形处理。
    • 当处理基本数据类型且不希望引入额外的包装和解包开销时。
  • 适合使用ArrayList的情况

    • 当数据集的大小可能会动态变化时。
    • 当需要灵活地添加、删除或修改元素时。
    • 当处理的对象是引用类型时。

6. HashMap 的实现原理/底层数据结构?JDK1.7 和 JDK1.8

JDK1.7中的HashMap

  • 底层数据结构:主要基于数组和链表。
  • 实现原理
    • 创建一个Entry类型的一维数组,默认初始化长度为16。
    • 当执行put操作时,会计算key的哈希值,并根据哈希值和数组长度确定元素在数组中的存储索引位置。
    • 如果该位置为空,则直接存储键值对;如果已存在元素(哈希冲突),则形成链表。

JDK1.8中的HashMap

  • 底层数据结构:数组、链表和红黑树。
  • 实现原理
    • 与JDK1.7类似,首先根据key的哈希值和数组长度确定元素在数组中的存储位置。
    • 当链表长度达到一定阈值(默认为8)时,链表会转化为红黑树,以提高查询效率。
    • 当红黑树的节点数量少于一定阈值(默认为6)时,会退化为链表。
    • 当HashMap中的元素数量超过负载因子(默认为0.75)乘以数组长度时,会触发扩容机制,数组长度加倍,并重新计算元素的存储位置。

此外,无论在哪个版本的JDK中,HashMap都不保证映射的顺序,特别是它不保证该顺序恒久不变。因此,如果需要有序的映射,可以考虑使用LinkedHashMap等其他数据结构。

7. HashMap 的 put 方法的执行过程?

HashMap 是 Java 中常用的一个类,它实现了 Map 接口,用于存储键值对。put 方法是 HashMap 中用于添加或更新键值对的方法。以下是 HashMapput 方法的大致执行过程:

  1. 计算键的哈希值
    当调用 put(K key, V value) 方法时,首先会计算键(key)的哈希值。这通常是通过调用键对象的 hashCode() 方法来完成的。哈希值是一个整数,用于确定键在哈希表中的存储位置。

  2. 定位桶的位置
    根据计算出的哈希值和哈希表的容量,通过一定的算法(通常是哈希值与表容量减一的按位与运算)确定键应该存储在哪个桶(bucket)中。这里的桶实际上就是哈希表中的一个位置或索引。

  3. 处理冲突
    如果计算出的桶位置已经有键值对存在(即发生了哈希冲突),那么 HashMap 会采用链表或红黑树(当链表长度超过一定阈值时)的形式来处理这些冲突的键值对。新插入的键值对会被添加到链表或红黑树的末尾。

  4. 插入或更新键值对
    如果桶位置为空,或者桶位置的键值对中的键与新键不同,那么就在该桶位置插入新的键值对。如果桶位置已经有与新键相同的键值对存在,那么就用新值替换旧值。

  5. 调整表的大小(如果需要)
    如果哈希表的当前大小不足以容纳所有的键值对,或者哈希表的大小超过了某个阈值(通常是加载因子与当前容量的乘积),那么 HashMap 会进行表格扩展(即重新分配一个更大的数组,并重新计算所有键值对的桶位置)。这个过程称为哈希表的重新哈希(rehashing)。

  6. 返回旧值(如果存在)
    put 方法会返回之前与该键关联的值(如果存在的话),否则返回 null

需要注意的是,HashMap 的性能在很大程度上取决于哈希函数的质量和哈希表的加载因子。一个好的哈希函数应该能够均匀分布键到各个桶中,从而减少哈希冲突并提高性能。加载因子则是用于控制哈希表何时进行扩展的一个参数,它影响了空间利用率和性能之间的权衡。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot如何替换启动图标

SpringBoot项目在启动时会出现一个默认的启动图案 . ____ _ __ _ _/\\ / ____ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | _ | _| | _ \/ _ | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) ) |____| .__|_| |_|_| |_\__, | / / / /|_||___//_/_/_/::…

Qt:使用ctrl+z快捷键取消文本框修改

1、使用ctrlz快捷键取消文本框修改 #include <QApplication> #include <QLineEdit> #include <QUndoStack> #include <QVBoxLayout>int main(int argc, char *argv[]) {QApplication a(argc, argv);QWidget window;QVBoxLayout layout(&window);/…

软件架构和基于架构的软件开发方法知识总结

一、软件架构定义 软件架构为软件系统提供了一个结构、行为和属性的高级抽象 软件架构是一种表达&#xff0c;使软件工程师能够&#xff1a; &#xff08;1&#xff09;分析设计在满足所规定的需求方面的有效性 &#xff08;2&#xff09;在设计变更相对容易的阶段&#xff0c;…

题目:异或森林(蓝桥OJ 3400)

问题描述&#xff1a; 解题思路&#xff1a; 一个数也可以看作是一段区间&#xff0c;当该区间的异或和为完全平方数时则符合题意。 我们需要注意枚举的完全平方的上限。 异或前缀和减小时间复杂度。 题解&#xff1a; #include <bits/stdc.h> using namespace std; usi…

vi\vim编辑器详解

vi\vim编辑器介绍 vi\vim是visual interface的简称, 是Linux中最经典的文本编辑器 同图形化界面中的 文本编辑器一样&#xff0c;vi是命令行下对文本文件进行编辑的绝佳选择。 vim 是 vi 的加强版本&#xff0c;兼容 vi 的所有指令&#xff0c;不仅能编辑文本&#xff0c;而…

鸿蒙一次开发,多端部署(九)应用市场首页

本小节将以应用市场首页为例&#xff0c;介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口。 页面设计 一个典型的应用市场首页的UX设计如下所示。 观察应用市场首页的页面设计&#xff0c;不同断点下的页面设计有较多相似的地方。 据此&#xff0c;我们可以将页…

实现不同数据类型的处理——数据类型转换和数据溢出

1.数据类型转换 当需要将整数型数据转换为浮点型数据&#xff0c;比如12转换为12.00&#xff0c;字符串“123”转换为整数类型123&#xff0c;可以通过类型转换来实现。 数据类型转换就是将某一数据类型转换为其他类型的数据。 有些数据转换不需要人工操作&#xff0c;编写相…

python的BBS论坛系统flask-django-nodejs-php

为了更好地发挥本系统的技术优势&#xff0c;根据BBS论坛系统的需求&#xff0c;本文尝试以B/S架构设计模式中的django/flask框架&#xff0c;python语言为基础&#xff0c;通过必要的编码处理、BBS论坛系统整体框架、功能服务多样化和有效性的高级经验和技术实现方法&#xff…

WebClient上载文件——实现将本地文件同步到远端服务器上

问题描述 用户上传产品示例图片到服务器端上&#xff0c;客户端在请求图片资源时&#xff0c;当服务端架设了多个节点的情况下&#xff0c;由于没有负载均衡请求到保存图片资源的服务器&#xff0c;出现图片访问404的问题。 这里保存上传文件时&#xff0c;同时需要将该文件保…

【话题】AI大模型学习

方向一&#xff1a;AI大模型学习的理论基础 AI大模型学习&#xff0c;即大规模机器学习&#xff0c;是建立在深厚的数学基础之上的。它主要涉及到线性代数、概率论、统计学、优化理论等基础数学知识。在算法原理方面&#xff0c;常见的有梯度下降法、反向传播算法、卷积神经网…

数学建模(层次分析法 python代码 案例)

目录 介绍&#xff1a; 模板&#xff1a; 例题&#xff1a;从景色、花费、饮食&#xff0c;男女比例四个方面去选取目的地 准则重要性矩阵&#xff1a; 每个准则的方案矩阵&#xff1a;​ 一致性检验&#xff1a; 特征值法求权值&#xff1a; 完整代码&#xff1a; 运行结…

1.6 学Python能干什么,Python的应用领域有哪些

Python能干什么&#xff0c;Python的应用领域 Python 作为一种功能强大的编程语言&#xff0c;因其简单易学而受到很多开发者的青睐。那么&#xff0c;Python 的应用领域有哪些呢&#xff1f; Python 有着非广泛的应用&#xff0c;几乎所有大中型互联网公司都在使用 Python&a…

基于甘特图的资源调度优化策略

资源在项目管理中是一个永恒的话题。无论人力、物力还是财力资源,总是捉襟见肘,都希望用最少的资源完成最大的工作。这就要求我们在资源调度方面果断精准,做到最优化。而甘特图作为项目时间规划的重要工具,恰恰能为资源调度提供绝佳帮助。 甘特图能反映出任务之间的制约关系,有…

Flutter 项目架构技术指南

Flutter 项目架构技术指南 视频 https://www.bilibili.com/video/BV1rx4y127kN/ 前言 原文 https://ducafecat.com/blog/flutter-clean-architecture-guide 探讨Flutter项目代码组织架构的关键方面和建议。了解设计原则SOLID、Clean Architecture&#xff0c;以及架构模式MVC…

(MATLAB)第二十一章 Simulink仿真设计初步

Simulink是MATLAB的重要组成部分&#xff0c;可以非常容易地实现可视化建模&#xff0c;并把理论研究和工程实践有机地结合在一起&#xff0c;不需要书写大量程序&#xff0c;只需要使用鼠标和键盘对已有模块进行简单的操作和设置。 21.1 Simulink简介 Simulink是MATLAB软件的…

linux热键,man手册介绍

目录 热键 tab ctrl c ctrl r man 区段 快捷键 热键 tab 可以看到以输入的内容为开头的指令,但无法选择: 当输入的内容匹配到的内容只有一个时,可以自动补全 可以用于输入路径时,自动补全文件名 ctrl c 让当前的程序停掉,可以在 程序或指令出问题而自己无法停止时 使用…

LeetCode---126双周赛

题目列表 3079. 求出加密整数的和 3080. 执行操作标记数组中的元素 3081. 替换字符串中的问号使分数最小 3082. 求出所有子序列的能量和 一、求出加密整数的和 按照题目要求&#xff0c;直接模拟即可&#xff0c;代码如下 class Solution { public:int sumOfEncryptedInt…

设计模式 适配器模式

1.背景 适配器模式&#xff0c;这个模式也很简单&#xff0c;你笔记本上的那个拖在外面的黑盒子就是个适配器&#xff0c;一般你在中国能用&#xff0c;在日本也能用&#xff0c;虽然两个国家的的电源电压不同&#xff0c;中国是 220V&#xff0c;日本是 110V&#xff0c;但是这…

2024.3.22 ARM

实现三个按键的中断 main.c &#xff1a;主函数初始化 #include "key_inc.h" #include "uart4.h" //封装延时函数 void delay(int ms) {int i, j;for (i 0; i < ms; i){for (j 0; j < 2000; j);} } int main() {char *s "hello world"…

ES的集群节点发现故障排除指南(2)

本文是ES官方文档关于集群节点发现与互联互通的问题排查指南内容&#xff0c;第二部分。 原文参考及相关内容&#xff1a; 英文原文&#xff08;官网&#xff09; 第一部分-&#xff08;1&#xff09; 已选出主节点但状态不稳定&#xff1f; 当一个节点赢得主节点选举时&…