java面试题-集合篇

news2024/11/15 11:14:18

Collection

1.Collection有哪些类?

Java集合框架中的Collection接口是所有集合类的基础接口,定义了一些基本的集合操作,如添加元素、删除元素、判断是否包含某个元素等。常见的集合类包括List、Set和Queue。

List

List接口定义了按照索引访问和操作元素的方法。它允许元素重复,并且有序。在List中可以使用get()和set()方法访问指定位置的元素,使用add()和remove()方法添加和删除元素。

常见的List实现类有:

  • ArrayList:ArrayList 是一个基于动态数组的实现,支持随机访问,插入和删除操作效率低。

  • LinkedList:底层使用双向链表实现,插入和删除操作效率高,但随机访问效率低。

  • Vector:与ArrayList类似,但是线程安全,效率较低。

Set

Set接口表示一个不允许有重复元素的集合,实现类必须重写equals()方法和hashCode()方法。常见的Set实现类有:

  • HashSet:底层使用哈希表实现,无序,元素唯一。

  • LinkedHashSet:底层使用哈希表和链表实现,有序,元素唯一。

  • TreeSet:底层使用红黑树实现,有序,元素唯一。

Queue

Queue接口表示一个先进先出(FIFO)的队列。常见的Queue实现类有:

  • LinkedList:底层使用链表实现,效率较高,LinkedList实现了Queue接口,它支持在队列的头部和尾部进行元素的添加和删除操作,因此可以被用作栈、队列和双端队列。。

  • PriorityQueue:是一种基于优先级堆的Queue,它保证了每次取出的元素都是队列中优先级最高的元素。。

需要注意的是,这些集合类都是基于Object的,如果需要在集合中存储特定类型的元素,需要使用泛型。例如,List<String>表示一个只包含字符串元素的List。

2.讲一下ArrayList的底层实现?

ArrayList 的底层实现基于数组,它继承了 AbstractList 抽象类并实现了 List 接口。下面是一些关于 ArrayList 的底层实现的细节:

  1. 数组:ArrayList 的内部实现是一个数组,使用数组实现可以方便地进行随机访问,根据索引直接访问指定位置的元素。

  1. 自动扩容:ArrayList 可以自动扩容以适应动态变化的容量需求,每次扩容会增加 50% 的容量。

  1. 元素的添加:ArrayList 中的 add(E e) 方法会在末尾添加一个元素,如果当前容量不足,则会进行扩容。

  1. 元素的删除:ArrayList 中的 remove(int index) 方法会删除指定索引位置的元素,将该位置后面的元素向前移动一位。

3.ArrayList自动扩容的具体实现?

当调用ArrayListadd方法时,如果当前列表中的元素数量已经达到容量的极限,那么就需要自动扩容。扩容的过程就是创建一个新的数组,并将原来数组中的元素复制到新数组中。

默认情况下,ArrayList的容量是10。当第一个元素被添加时,内部数组会被初始化为长度为10的数组。当添加第11个元素时,原始数组将会被复制到一个新的长度为15的数组中,容量增加了50%。如果再添加元素,当超过了15个元素时,内部数组将再次扩容到新的长度为22的数组中。

当使用ensureCapacity方法增加数组容量时,ArrayList使用给定参数的最大值和当前容量的大小来决定新的容量大小。

private void ensureCapacityInternal(int minCapacity) {
    // 判断是否需要扩容
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // 判断是否需要扩容
    if (minCapacity - elementData.length > 0) {
        grow(minCapacity);
    }
}

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        newCapacity = hugeCapacity(minCapacity);
    }
    elementData = Arrays.copyOf(elementData, newCapacity);
}

扩容操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。

4.ArrayList的Fail-Fast机制?

在 Java 中,如果使用集合类的迭代器来遍历集合元素,而同时修改了集合中的元素,就有可能会发生 ConcurrentModificationException 异常。这是因为 Java 集合类的迭代器是快速失败(fail-fast)机制,如果在迭代集合时集合发生了结构性变化(例如添加或删除元素),迭代器就会立即抛出异常,而不是等到迭代完成再抛出异常。

ArrayList 是一个支持随机访问的序列容器,底层使用数组实现,所以在对 ArrayList 进行并发操作时,可能会出现不同步的问题,因此 ArrayList 也使用了快速失败机制来保证线程安全。

具体来说,如果在对 ArrayList 进行迭代操作的同时,对其进行增删改操作,会导致 ArrayList 的 modCount(修改次数)和迭代器的 expectedModCount(预期的修改次数)不一致,迭代器会立即抛出 ConcurrentModificationException 异常。

以下是一个简单的示例代码,用来演示 ArrayList 快速失败机制的工作原理:

List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    if (element == 2) {
        list.remove(element);
    }
}

在上面的示例代码中,我们在迭代过程中删除了元素 2,这会导致 ConcurrentModificationException 异常的抛出。为了避免出现这种情况,我们可以使用 Iterator 的 remove() 方法来进行元素的删除,或者使用线程安全的集合类,例如 CopyOnWriteArrayList。


MAP

1.Map有哪些实现类?

Java中的Map接口定义了一个键值对映射的数据结构,可以通过给定的键快速查找对应的值。Map接口有很多实现类,常见的有以下几种:

  1. HashMap:基于哈希表实现的Map,支持null键和null值,非线程安全的。

  1. LinkedHashMap:基于哈希表和双向链表实现的Map,可以按照插入顺序或者访问顺序遍历键值对,非线程安全的。

  1. TreeMap:基于红黑树实现的Map,键值对按照自然顺序或者自定义顺序排序,非线程安全的。

  1. ConcurrentHashMap:线程安全的HashMap,使用分离锁来控制并发访问,支持高并发,可以通过一定的控制减小锁的竞争。

  1. Hashtable:早期Java版本中提供的线程安全的哈希表,支持null键和null值,但是效率较低,已经被ConcurrentHashMap取代。

  1. Properties:Hashtable的子类,用来读取和写入属性文件,通常用于读取配置文件。

除了以上这些常见的实现类,还有一些其他的实现类,比如WeakHashMap、IdentityHashMap、EnumMap等,不过它们使用的较少,一般只在特定场景下使用。

2.HashMap的底层实现(jdk7&jdk8)?

JDK7 的底层实现

在 JDK7 中,HashMap 是通过数组和链表的结合来实现的。其基本思路是:将 key 通过哈希函数映射为数组下标,将 value 存储在对应的数组元素中。如果不同的 key 映射到了同一个数组下标,就会以链表的形式存储在该数组元素中。

HashMap 在 JDK7 中的底层结构主要由两部分组成:一个 Entry 数组和一个链表。其中,Entry 是 HashMap 的基本单元,它包含了 key、value 和指向下一个 Entry 的指针。当使用 put() 方法向 HashMap 中添加元素时,会根据 key 的哈希值计算出在数组中的位置,然后将 Entry 添加到该位置的链表中。如果两个不同的 key 哈希值相同,那么它们会被放到同一个链表中,形成一个链表结构。这就是 JDK7 中 HashMap 的基本实现原理。

然而,这种实现方式有一个严重的问题:当链表过长时,查询效率会大大降低,因为需要遍历整个链表才能找到对应的元素。在极端情况下,当所有的元素都映射到了同一个数组下标,HashMap 的时间复杂度就会退化到 O(n),这就是所谓的哈希冲突问题。

JDK8 的底层实现

JDK8 中的 HashMap 对 JDK7 中的实现进行了优化,主要是通过引入红黑树来解决链表过长的问题。当链表长度超过一定阈值时(默认为 8),链表就会转换为红黑树。这样,在查询时,如果在链表中需要遍历的节点数量超过了阈值,就会使用红黑树进行快速查找,从而提高了查询的效率。

在 JDK8 中,HashMap 的底层结构主要由三部分组成:一个数组、一个链表和一个红黑树。当使用 put() 方法向 HashMap 中添加元素时,如果对应数组下标上已经存在元素,就会进行以下操作:

  • 如果该元素是一个链表,就将新元素追加到链表的末尾。

  • 如果该元素是一个红黑树,就在树中查找 key 对应的节点,然后将节点的 value 替换成新的 value。如果树中不存在对应的节点,就将新元素添加到树中。

  • 如果该元素为 null,就直接在该数组的位置插入新的 Entry。

在 JDK8 中,HashMap 的 get() 方法的实现方式也发生了变化。在查询时,先根据 key 的哈希值计算出在数组中的位置,然后判断该位置上的元素是否为 null。如果为 null,则返回 null;如果不为 null,则判断该元素是链表还是红黑树。如果是链表,则遍历链表寻找对应的元素;如果是红黑树,则在树中进行查找。

JDK8 中 HashMap 的优化主要体现在两个方面:

  1. 引入红黑树,解决链表过长的问题,提高了查询效率。当链表长度超过一定阈值时,将链表转换为红黑树,避免了链表过长时查询效率下降的问题。

  1. 除了对链表和红黑树的优化之外,JDK 8 还对哈希函数进行了改进。在 JDK 8 中,对于 key 的 hash 值,不再采用传统的取模运算(%)计算哈希桶的索引,而是采用了一种新的方式,使用 key 的 hash 值高位和低位进行异或运算,以此来增加哈希桶的分布性。这种新的方式能够更好地抵抗哈希冲突,从而提高了 HashMap 的性能。

  1. HashMap 将插入元素时使用的方式从头插法改为了尾插法,更好地支持并发操作。在多线程环境下,头插法容易导致多线程竞争同一个桶位,从而导致链表成环。成环后会导致链表转换成红黑树的操作失败,进而影响整个 HashMap 的性能。而尾插法不会导致链表成环,因此在多线程环境下更为安全。

总的来说,JDK8 中 HashMap 的底层实现相比于 JDK7 发生了较大的变化,通过引入红黑树和优化哈希算法,提高了 HashMap 的性能和稳定性。

3.HashSet的底层实现?

HashSet 是基于 HashMap 实现的,底层是一个 HashMap 对象。在 HashSet 中,所有元素都是存储在一个 HashMap 的键上,而这个键的值则是一个静态的 Object 常量(通常是一个 dummy Object)。因此,HashSet 的实现过程可以简单概括为将所有元素作为 HashMap 的 key 存储,而 value 为一个静态的 Object 对象。

具体来说,HashSet 就是在 HashMap 的基础上去掉了 value,只保留了 key。在使用 HashSet 时,我们只需要调用 HashMap 的 put() 方法,把元素作为 key 插入 HashMap 中,value 则使用一个常量对象(例如 private static final Object PRESENT = new Object())来占位即可。

相比于 HashMap,HashSet 的实现过程更为简单,因为它只需要存储键而不需要存储值。因此,HashSet 在大多数情况下比 HashMap 更加高效。同时,由于 HashSet 也是基于 HashMap 实现的,因此它们的底层实现也非常相似,可以复用 HashMap 的很多特性。

以下是 HashSet 的部分源码:

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    // HashSet 底层就是一个 HashMap,所有元素作为 key 存储在 HashMap 中
    private transient HashMap<E,Object> map;

    // 常量对象,用于占位
    private static final Object PRESENT = new Object();

    public HashSet() {
        map = new HashMap<>();
    }

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
}

可以看到,在 HashSet 中,我们只需要调用 HashMap 的 put() 方法来将元素插入到 HashMap 中。这样做的好处是可以节省很多重复代码,而且可以复用 HashMap 的很多特性。同时,由于 HashSet 只存储键而不存储值,因此在大多数情况下比 HashMap 更加高效。

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

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

相关文章

Module理解及使用

ES6的模块化设计思想是静态化&#xff0c;也就是说&#xff0c;在编译的时候确定模块的依赖关系&#xff0c;以及输出输出入的变量。而CommonJS和AMD模块都是在运行时确定的。ES6的模块不是对象&#xff0c;而是通过export显示指定输出的代码&#xff0c;再通过import命令输入。…

ES6新特性详解

文章目录1. let和const1.1 let声明变量1.2 const声明常量2. 模板字符串3. 解构赋值3.1 数组的解构赋值3.2 对象的解构赋值4. 函数扩展4.1 参数默认值4.2 剩余参数4.3 箭头函数5. 对象扩展5.1 对象简写5.2 属性名表达式5.3 扩展运算符6. Symbol7. Iterator和Generator7.1 Iterat…

k8s自动化安装脚本(kubeadm-1.23.7)

文章目录介绍软件架构版本介绍更新内容2023-02-192023-02-152023-02-142023-02-102022-10-202022-08-06准备部署包操作步骤环境准备结构备注解压部署包修改host文件脚本使用方式初始化环境验证ansible配置安装k8s集群登录master的节点添加node节点master节点状态检查组件安装安…

高精度算法一

目录 1. 基础知识 2. 大整数 大整数 3. 大整数 - 大整数 1. 基础知识 利用计算机进行数值计算&#xff0c;有时会遇到这样的问题&#xff1a;有些计算要求精度高&#xff0c;希望计算的数的位数可达几十位甚至几百位&#xff0c;虽然计算机的计算精度也算较高了&#xff0c…

大数据之---Nifi-处理器类别_详细了解各种处理器的含义用途---大数据之Nifi工作笔记0007

处理器有很多类这里主要分了,数据转换的可以看到上面的这些是数据转换常用的 然后: 这里这个controlrate,用来控制流的速率,来保护作用,防止崩掉 distributeLoad用来负载均衡,减轻压力 这里的路由是,根据是否满足对应的条件,来执行是否把数据路由到某个地方. 然后来看数据库…

数据库技术-函数依赖、键与约束、范式

一、函数依赖 给定一个x&#xff0c;能唯一确定一个Y&#xff0c;就称x确定Y&#xff0c;或者说Y依赖于x&#xff0c;例如YX*X函数。 函数依赖又可扩展以下两种规则: 部分函数依赖:A可确定C&#xff0c;(A,B)也可确定C,(A,B)中的一部分&#xff08;即A&#xff09;可以确定C&a…

Spring Cloud Nacos实战(一)- 下载和安装

Spring Cloud Alibaba Nacos下载和安装 Nacos介绍 ​ Nacos&#xff08;Naming Configuration Service&#xff09; 是一个易于使用的动态服务发现、配置和服务管理平台&#xff0c;用于构建云原生应用程序 ​ 服务发现是微服务架构中的关键组件之一。Nacos 致力于帮助您发现…

使用nginx单独部署Vben应用

前言 本文主要介绍Vben使用nginx单独部署的方式&#xff0c;其实前端发展到现在已经不是当年的jsp&#xff0c;asp必须要和后端一起部署了。单独部署调试的工具也很多&#xff0c;比如vue-cli-service 和 Vben中用到的vite &#xff0c;当然这些我们一般用在开发的工程中。正式…

SpringCloud(三)Hystrix断路器服务降级、服务熔断、服务监控案例详解

七、Hystrix断路器 7.1 简介 分布式系统面临的问题 复杂分布式体系结构中的应用程序有数十个依赖关系&#xff0c;每个依赖关系在某些时候将不可避免地失败。 多个微服务之间调用的时候&#xff0c;假设微服务A调用微服务B和微服务C&#xff0c;微服务B和微服务C又调用其它的微…

C# chart绘图 鼠标响应

1、图形自动滚动设置 chart1.ChartAreas[0].AxisX.Maximum 横坐标显示区域最大值 chart1.ChartAreas[0].AxisX.Minimum 横坐标显示区域最小值 显示宽度 chart1.ChartAreas[0].AxisX.Maximum - chart1.ChartAreas[0].AxisX.Minimum chart1.ChartAreas[0].AxisX.Maximum x_d…

ChatGPT从业影响之个人浅见

文章目录前言ChatGPT简介AI简介ChatGPT对从业者的影响参考资料前言 ChatGRT最近很火&#xff0c;网上有一些观点&#xff0c;他会取代很多职业&#xff0c;让产业产生颠覆性的变化。今天聊聊自己的想法&#xff0c;他的出现&#xff0c;是否会让我&#xff08;程序员&#xff…

qmt api 接口文档

http://qmt.ptradeapi.com 用户自行安装 Python 三方库 对于有经验的 Python 开发者来说&#xff0c;平台提供了自行安装第三方库的方式。为了引入额外的第三方库&#xff0c;用户需要做如下一些操作&#xff1a; 安装前注意事项&#xff1a; 三方库的安装有可能会引起系统错误…

JVM学习篇对象创建与内存分配机制深度剖析

1. 对象的创建 对象创建的主要流程: 1.1 类加载检查 虚拟机遇到一条new指令时&#xff0c;首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用&#xff0c;并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有&#xff0c;那必须先执行相…

【Java 面试合集】内存中为什么要区分栈和堆

内存中为什么要区分栈和堆 1. 概述 嗨&#xff0c;大家好Java 面试合集又来了&#xff0c;今天我们分享的主题很大众化以及普通。无论是Java 语言本身还是别的语言都会有所涉及&#xff0c;但是今天我们从Java角度来讲下 2. 分析 今天我们会从多个方向来分享这个话题 2.1 栈是…

ETCD本地多成员集群部署

目录安装部署etcdctl 操作etcd使用http请求操作etcd本地多成员集群搭建python获取成员信息参考安装部署 按照官网文档&#xff0c;安装release版本 https://etcd.io/docs/v3.4/install/ [rootVM-33-162-centos /usr/local/bin]# etcd --version etcd Version: 3.4.16 Git SH…

点云配准方法原理(NDT、ICP)

配准是点云处理中的一个基础问题&#xff0c;众多学者此问题进行了广泛而深入的研究&#xff0c;也出现了一系列优秀成熟的算法&#xff0c;在三维建模、自动驾驶等领域发挥着重要的作用。 本文主要介绍粗配准NDT (Normal Distribution Transform) 与 精配准ICP (Iterative Cl…

最新最全中小微企业研究数据:海量创业公司信息与获取投资信息(1985-2021年)

一、企业获取投资名单&资方信息 数据来源&#xff1a;搜企网、企查查、天眼查 时间跨度&#xff1a;1985年8月-2021年9月 区域范围&#xff1a;全国范围 数据字段&#xff1a;企业名称、时间、获得投资金额以及投资方信息 部分数据&#xff1a; DateCompany_nameUnit…

摄影师没了?!生成式人工智能即将降维打击摄影行业

本文是Mixlab无界社区成员的投稿&#xff1a;滚石deepfacelab和deepfacelive项目组成员摄影师失业了&#xff1f;&#xff1f;怎么说&#xff1f;##你还以为AI绘画影响的只是插画师行业吗&#xff1f;错了&#xff0c;摄影行业也即将面临技术洗牌。话不多说&#xff0c;先看一下…

java并发编程原理2 (AQS, ReentrantLock,线程池)

一、AQS&#xff1a; 1.1 AQS是什么&#xff1f; AQS就是一个抽象队列同步器&#xff0c;abstract queued sychronizer&#xff0c;本质就是一个抽象类。 AQS中有一个核心属性state&#xff0c;其次还有一个双向链表以及一个单项链表。 首先state是基于volatile修饰&#x…

分享113个HTML艺术时尚模板,总有一款适合您

分享113个HTML艺术时尚模板&#xff0c;总有一款适合您 113个HTML艺术时尚模板下载链接&#xff1a;https://pan.baidu.com/s/1ReoPNIRjkYov-SjsPo0vhg?pwdjk4a 提取码&#xff1a;jk4a Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 女性化妆用品网页模板 粉…