Java-常见面试题收集(三)

八 集合

1 List,Set,Map 是否继承自 Collection 接口

  List,Set 接口继承于 Collection 接口
  Map 没有继承于 Collection 接口

2 List,Set,Map 三者的区别

  ① List、Set 都是继承自 Collection 接口,Map 则不是

  ② List 特点:元素有放入顺序,元素可重复 ,Set 特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在 set 中的位置是有该元素的 HashCode 决定的,其位置其实是固定的,加入 Set 的 Object 必须定义 equals()方法 ,另外 list 支持 for 循环,也就是通过下标来遍历,也可以用迭代器,但是 set 只能用迭代,因为他无序,无法用下标来取得想要的值。)

  ③ Set 和 List 对比:
  Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
  List:和数组类似,List 可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
  ④ Map 适合储存键值对的数据

  ⑤ 线程安全集合类与非线程安全集合类: LinkedList、ArrayList、HashSet 是非线程安全的,Vector 是线程安全的;HashMap 是非线程安全的,HashTable 是线程安全的;

3 Array 和 ArrayList 的区别

  即数组和 ArrayList 的区别
  ①数组的长度是固定的,集合的长度是可变的(自动拓展容量)。
  ②使用 Java 类封装出一个个容器类,开发者只需要直接调用即可,不用程序员再手写容器类。

4 ArrayList 和 Vector 的区别

  这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的

  ①同步性:
  Vector 是线程安全的,也就是说是它的方法之间是线程同步的,而 ArrayList 是线程序不安全的,它的
方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用 ArrayList,因为它不考虑线
程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用 Vector,因为不需要我们自己再去考
虑和编写线程安全的代码。

  ②数据增长:
  ArrayList 与 Vector 都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要
增加 ArrayList 与 Vector 的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多
个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。
即 Vector 增长原来的一倍,ArrayList

5 Arraylist 与 LinkedList 区别

  ① 是否保证线程安全:ArrayList和 LinkedList 都是不同步的,也就是不保证线程安全;
  ② 底层数据结构: Arraylist底层使用的是 Object 数组;LinkedList 底层使用的是双向链表数据结构
  ③ 是否支持快速随机访问:LinkedList不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index) 方法)。
  ④ 内存空间占用: ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而
LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

6 数组和 List 之间的转换

  数组转 List:使用 Arrays. asList(array) 进行转换。
  List 转数组:使用 List 自带的 toArray() 方法。

7 为什么重写 equals 时必须重写hashCode 方法

  在 Java 中,当你重写 equals() 方法时,通常也需要重写 hashCode() 方法。这是因为 equals() 和 hashCode() 方法在 Java 的一些集合类(如 HashMap、HashSet 等)中起着至关重要的作用,它们共同维护着对象在这些集合中的唯一性和一致性。

  ① 合同要求:Java 规范中对于 equals() 和 hashCode() 的关系有一个明确的合同要求,即如果两个对象根据 equals(Object) 方法是相等的,那么调用这两个对象的 hashCode 方法必须产生相同的整数结果。

  ② 性能考虑:在哈希集合中,对象是根据其哈希码分配到不同的桶(bucket)中的。如果两个对象相等(即 equals() 返回 true)但哈希码不同,那么它们可能会被分配到不同的桶中,导致在集合中无法正确地检索或删除这些对象。

  ③ 一致性:如果在对象的状态改变时,equals() 方法的比较逻辑也随之改变,那么 hashCode() 方法也应该相应地改变,以确保哈希集合的正确性。

  因此,为了避免潜在的问题和错误,当你重写 equals() 方法时,通常也需要重写 hashCode() 方法,以确保它们满足上述合同要求,并且在哈希集合中正确地工作。

  一个常见的 hashCode() 实现方式是使用对象的字段来计算哈希码,这些字段在 equals() 方法中也被用于比较。这样可以确保两个相等的对象具有相同的哈希码。

  需要注意的是,虽然重写 equals() 时通常需要重写 hashCode(),但反过来并不总是成立。有些情况下,你可能只需要重写 hashCode() 而不需要重写 equals(),但这通常较少见。

8 Comparable 和 Comparator 的区别

  Comparable 和 Comparator 都是 Java 中用于排序的接口,但它们之间存在明显的区别。以下是它们之间的主要差异:

  Comparable
  定义:Comparable 是一个接口,它允许对象之间进行比较。实现 Comparable 接口的类必须重写 compareTo(Object o) 方法,以定义对象之间的自然排序规则。
  使用场景:当类的自然顺序就是排序顺序时,使用 Comparable。例如,Integer、String 等类都实现了 Comparable 接口,因此可以直接对它们进行排序。
  限制:每个类只能有一个自然排序规则。如果需要对同一个类的实例进行多种不同的排序,那么 Comparable 可能不是最佳选择。

  Comparator
  定义:Comparator 是一个接口,它允许对象按照不同的规则进行比较。实现 Comparator 接口的类必须重写 compare(Object o1, Object o2) 方法,以定义比较逻辑。
  使用场景:当需要为对象定义多种排序规则时,使用 Comparator。你可以为同一个类创建多个不同的 Comparator 实现,每个实现定义一种不同的排序规则。
  灵活性:Comparator 比 Comparable 更灵活,因为它允许你在运行时选择排序规则,而不仅仅是依赖于类的自然顺序。

  示例
  假设有一个 Person 类,包含 name 和 age 两个字段。

  如果你想按照 age 对 Person 对象进行排序,并且这种排序是 Person 类的自然顺序,那么可以让 Person 类实现 Comparable 接口,并在 compareTo 方法中定义基于 age 的比较逻辑。
但是,如果你还想按照 name 对 Person 对象进行排序,或者想同时考虑 name 和 age 进行排序,那么可以创建一个或多个 Comparator 实现,每个实现定义一种不同的排序规则。

  总结
  Comparable 用于定义类的自然排序规则。
  Comparator 用于定义多种不同的排序规则。
  当类的自然顺序就是排序顺序时,使用 Comparable。
  当需要为对象定义多种排序规则时,使用 Comparator。

9 HashMap 和 Hashtable 的区别

  HashMap 是 Hashtable 的轻量级实现(非线程安全的实现),他们都完成了 Map 接口,主要区别在于HashMap 允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable

九 源码分析

1 ArrayList 的拓容机制

  ArrayList的扩容机制是其动态数组实现中的关键部分,当向ArrayList中添加元素而其当前大小不足以容纳新元素时,就会触发扩容操作。以下是ArrayList扩容机制的主要步骤:

  检查容量:当尝试向ArrayList添加新元素时,首先会检查当前ArrayList的容量是否足够。如果当前元素数量已经达到数组的容量上限,就需要进行扩容。

  计算新容量:ArrayList的扩容操作通常会将当前容量增加到原来的1.5倍,这个增长因子可以根据具体的实现有所不同。这种扩容策略可以平衡内存使用和性能,避免频繁扩容带来的性能开销。

  创建新数组:一旦确定了新的容量,ArrayList就会创建一个新的数组,其大小等于计算出的新容量。

  复制元素:接下来,ArrayList会将原数组中的所有元素复制到新数组中。这个复制操作通常使用System.arraycopy()方法或者类似的机制来完成,以确保复制过程的效率。

  更新引用:复制完成后,ArrayList会更新其内部的数组引用,使其指向新的数组,并丢弃旧的数组。这样,ArrayList现在就可以使用具有更大容量的新数组来存储元素了。

  添加新元素:最后,新的元素可以被添加到扩容后的ArrayList中,而不会出现容量不足的问题。

  值得注意的是,ArrayList的初始容量可以通过构造函数来指定。如果没有指定初始容量,那么ArrayList会使用一个默认的初始容量(通常是10)。在后续的扩容过程中,容量会按照上述的扩容策略进行增长。

  另外,需要注意的是,ArrayList的扩容操作可能会导致性能开销,特别是在大量添加元素的情况下。因此,如果可能的话,最好在创建ArrayList时就指定一个合适的初始容量,以减少后续扩容的次数。同时,由于ArrayList在扩容时需要创建新的数组并复制元素,所以这个过程并不是线程安全的。在多线程环境下使用ArrayList时,需要特别注意线程安全问题。

2 HashMap 和 ConcurrentHashMap 的区别

  ① HashMap 不是线程安全的,而 ConcurrentHashMap 是线程安全的。
  ② ConcurrentHashMap 采用锁分段技术,将整个 Hash 桶进行了分段 segment,也就是将这个大的
数组分成了几个小的片段 segment,而且每个小的片段 segment 上面都有锁存在,那么在插入元素的时
候就需要先找到应该插入到哪一个片段 segment,然后再在这个片段上面进行插入,而且这里还需要获取
segment 锁。
  ③ ConcurrentHashMap 让锁的粒度更精细一些,并发性能更好

3 HashMap 的底层原理

  基于 hashing 的原理,jdk8 后采用数组+链表+红黑树的数据结构。我们通过 put和 get 存储和获取对象。当我们给 put()方法传递键和值时,先对键做一个hashCode()的计算来得到它在 bucket 数组中的位置来存储 Entry 对象。当获取对象时,通过 get 获取到 bucket 的位置,再通过键对象的 equals()方法找到正确的键值对,然后在返回值对象。

4 HashMap 中 put 是如何实现的

1.计算关于 key 的 hashcode 值(与 Key.hashCode 的高 16 位做异或运算)
2.如果散列表为空时,调用 resize()初始化散列表
3.如果没有发生碰撞,直接添加元素到散列表中去
4.如果发生了碰撞(hashCode 值相同),进行三种判断
4.1:若 key 地址相同或者 equals 后内容相同,则替换旧值
4.2:如果是红黑树结构,就调用树的插入方法
4.3:链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入之
后判断链表个数是否到达变成红黑树的阙值 8;也可以遍历到有节点与插入元素
的哈希值和内容相同,进行覆盖。
5.如果桶满了大于阀值,则 resize 进行扩容

5 HashMap 中什么时候需要进行扩容,扩容 resize()又是如何实现的

  HashMap在以下情况下需要进行扩容:

  当HashMap中的元素数量超过了其容量的0.75倍时,会触发扩容操作。这个阈值通常是HashMap容量的0.75倍,也就是说,当HashMap中的元素数量超过了容量的75%时,就会触发扩容操作。扩容的目的是为了减少哈希冲突,提高查询效率。当HashMap中的元素数量过多时,哈希冲突的概率就会增大,导致查询效率降低。通过扩容,可以使元素分布到更多的桶中,降低哈希冲突的概率,从而提高查询效率。

  当HashMap在put元素时,如果当前数组的容量小于树化的容量的最小值(默认是64),也会触发扩容条件,从而调用resize方法。

  HashMap的扩容操作是通过resize()方法实现的。该方法在初始化或者扩容的时候会调用,主要包含扩容(扩大容量capacity和threshold)及迁移元素两个过程。在扩容时,会创建一个新的数组,其容量是原数组的两倍,然后重新计算原数组中所有元素的哈希值,并将它们放入新的数组。这个过程需要一定的计算资源,因此扩容是一个相对耗时的操作。

  在迁移元素时,会先分裂链表或者红黑树,然后批量迁移。具体的迁移过程可能因Java版本和具体实现而有所不同,但大体上都是将原数组中的元素按照新的哈希值重新分布到新的数组中。

  总之,HashMap的扩容机制是为了保证其在存储大量元素时仍能保持良好的性能。通过合理地调整容量和重新分布元素,HashMap可以在保持高效查询的同时,避免过多的哈希冲突和内存浪费。

6 HashMap 中 get 是如何实现的

  对 key 的 hashCode 进行 hashing,与运算计算下标获取 bucket 位置,如果在桶的首位上就可以找到就直接返回,否则在树中找或者链表中遍历找,如果有hash 冲突,则利用 equals 方法去遍历链表查找节点。

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

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

相关文章

【C#】Conventions(惯例)最佳实践和准则

在C#中,Conventions(惯例)是指编写代码时的一套最佳实践和准则。这些惯例旨在提高代码的可读性、一致性和可维护性。虽然这些惯例不是语言的强制规则,但遵循它们可以使你的代码更加清晰和专业。 以下是一些常见的C#编码惯例: 命名约定: 使用有意义的、描述性的名称。类名和公…

P8711 [蓝桥杯 2020 省 B1] 整除序列 存疑解决篇 Python

[蓝桥杯 2020 省 B1] 整除序列 题目描述 有一个序列,序列的第一个数是 n n n,后面的每个数是前一个数整除 2 2 2,请输出这个序列中值为正数的项。 输入格式 输入一行包含一个整数 n n n。 输出格式 输出一行,包含多个整数…

Zinx框架的高级用法

一、使用框架提供的实用类 zinx框架已经提供了常用的IO通道类-TCP。 阅读Tcp相关类的使用文档,将之前的3个案例用TCP的方式实现。 步骤: 创建Tcp数据通道类继承ZinxTcpData,重写GetInputNextStage函数,内容跟之前标准输入通道类…

Mysql2-sql语句

一、MySQL数据库表操作 MySQL表的基本概念 在windows中有个程序叫做excel. 而Excel文件中存在了如sheet1、sheet2、sheet3的表, 所有的sheet都存储在这个Excel文件中, 在某个sheet中有相应的数据. 回到数据库和表的关系上来说, 这个Excel文件就是一个数据库, 所有的sheet就是…

基于SpringBoot和Vue的大学生租房系统的设计与实现

今天要和大家聊的是一款今天要和大家聊的是一款基于SpringBoot和Vue的大学生租房系统的设计与实现。 !!! 有需要的小伙伴可以通过文章末尾名片咨询我哦!!! 💕💕作者:李同…

【C++】弥补C语言的不足(②有默认参数的函数)

&#x1f33b;缺省参数 我们先来看一个简单地例子&#xff0c;对于在函数的定义中三个形参都给定默认值&#xff1a; #include <iostream> using namespace std; void fun(int a 10, int b 20, int c 30) {cout << "a " << a << endl;…

【考研数学】跟张宇,一看就会,一做就废,怎么办?

刚开始考研的时候都是这种情况&#xff0c;建议降低习题难度 刚开始就做1000题的都是勇士 1000题适合在强化阶段做&#xff0c;因为1000题中的题目&#xff0c;综合度高&#xff0c;需要做题者掌握比较多的解题技巧&#xff0c;而且对于计算能力要求也比较高。初学者肯定是不…

C语言经典面试题目(二十六)

1、解释一下C语言中的函数原型及其作用。 函数原型是指在函数定义之前声明函数的参数类型、返回类型和函数名称的一种声明方式。函数原型的作用包括&#xff1a; 编译器检查&#xff1a;函数原型能够告诉编译器函数的返回类型和参数类型&#xff0c;从而能够在编译阶段检查函…

【OJ】动归练习一

个人主页 &#xff1a; zxctscl 如有转载请先通知 题目 1. 前言2. 1137第 N 个泰波那契数2.1 分析2.2 代码 3. 面试题 08.01. 三步问题3.1 分析3.2 代码 4. 746使用最小花费爬楼梯4.1 分析4.1.1 以i位置为终点4.1.2 以i位置为起点 4.2 代码4.2.1以i位置为终点4.2.2以i位置为起点…

深浅拷贝与初始化列表

一、深拷贝与浅拷贝 浅拷贝&#xff1a;简单的赋值拷贝操作 深拷贝&#xff1a;在堆区重新申请空间 由于栈上的数据先进后出&#xff0c;所以p2后释放&#xff0c;在执行析构代码时&#xff0c;新建的堆区数据就被释放&#xff0c;再当p1进行释放时&#xff0c;由于堆区数据…

【力扣每日一题】lc1793. 好子数组的最大分数(单调栈)

LC1793. 好子数组的最大分数 题目描述 给你一个整数数组 nums &#xff08;下标从 0 开始&#xff09;和一个整数 k 。 一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i1], ..., nums[j]) * (j - i 1) 。 一个 好 子数组的两个端点下标需要满足 i < k < j 。 请…

【每日一题】13. 罗马数字转整数

13. 罗马数字转整数 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例 1&#xff1a; 输入&#xff1a;s “Hello World” 输出&…

【leetcode】67.二进制求和

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 示例&#xff1a; 输入&#xff1a;a "1010", b "1011" 输出&#xff1a;"10101"思路1&#xff1a; …

【Nginx】反向代理解决跨域问题

电脑A写前端代码&#xff0c;电脑B写后端代码&#xff0c;电脑A用Nginx解决跨域问题&#xff0c;从而调用后端的接口。 为什么nginx反向代理可以实现跨域请求&#xff1f; 因为浏览器的同源策略&#xff08;Same-Origin Policy&#xff09;。 在同源策略下&#xff0c;无法向…

一命通关广度优先遍历

前言 在这篇文章之前&#xff0c;已对非线性结构遍历的另一种方法——深度优先遍历进行了讲解&#xff0c;其中很多概念词都是共用的。为了更好的阅读体验&#xff0c;最好先在掌握或起码了解dfs的基础上&#xff0c;再来阅读本文章&#xff0c;否则因为会有很多概念词看不明白…

nodejs的中雪花算法(Snowflake)

介绍 雪花算法&#xff08;Snowflake&#xff09;是Twitter开发的一种分布式唯一ID生成算法&#xff0c;用于生成全局唯一的ID。雪花算法的核心思想是利用时间戳和机器ID来生成唯一的ID&#xff0c;确保在分布式环境下生成的ID不会重复。 雪花算法生成的ID是一个64位的整数&a…

如何修复WordPress网站媒体库上传文件失败的问题

公司最近推出了一系列新产品&#xff0c;为了更新网站的视频和图片&#xff0c;我们需要将它们上传至网站媒体库。然而&#xff0c;在上传视频时&#xff0c;我们却遇到了一些问题。系统提示说&#xff0c;我们尝试上传的视频文件大小超出了站点的最大上传限制。尽管我们的视频…

计算机网络:性能指标

计算机网络&#xff1a;性能指标 速率带宽吞吐量时延时延带宽积往返时间利用率丢包率 本博客介绍计算机网络的性能指标&#xff0c;我们可以从不同的方面来度量计算机网络的性能。常用的计算机网络性能指标有以下 8 个&#xff0c;他们是&#xff1a;速率、带宽、吞吐量、时延、…

P1481 魔族密码

P1481 魔族密码 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 字典树 在插入字符串 s s s时&#xff0c;不断记录 s 0... k s_{0...k} s0...k​的个数取最大即可。 #include <bits/stdc.h> using namespace std; const int N 1e5 21; int cnt[N], tr[N][30], idx,…

NVIDIA NCCL 源码学习(十三)- IB SHARP

背景 之前我们看到了基于ring和tree的两种allreduce算法&#xff0c;对于ring allreduce&#xff0c;一块数据在reduce scatter阶段需要经过所有的rank&#xff0c;allgather阶段又需要经过所有rank&#xff1b;对于tree allreduce&#xff0c;一块数据数据在reduce阶段要上行…