Java高频面试之集合篇

Java 中常用的容器有哪些?

ArrayList 和 LinkedList 的区别?

ArrayList 是基于数组实现的,LinkedList 是基于链表实现的.
ArrayList实现了RandomAccess接口,可基于下标访问.
LinkedList 实现了Deque /dek/,可以当做双端队列使用.
插入效率对比
如果从头部插入,LinkedList更快
如果从中间插入ArrayList更快
如果从尾部插入,在不扩容的情况下ArrayList略快,扩容次数多时LinkedList更快
get(int index) ArrayList更快
遍历ArrayList更快
ArrayList新循环(迭代器)和普通循环遍历效率差不多
LinkedList 新循环(迭代器)比普通循环效率高很多(每次get(int index)都要找一次)

从头开始插

public static void addFromHeaderTest4ArrayList(int num) {ArrayList<String> list = new ArrayList<>(num);int i = 0;long timeStart = System.currentTimeMillis();while (i < num) {list.add(0, i + "王大锤");i++;}long timeEnd = System.currentTimeMillis();System.out.println("ArrayList从集合头部位置新增元素花费的时间" + (timeEnd - timeStart));
}
public static void addFromHeaderTest4LinkedList(int num) {LinkedList<String> list = new LinkedList<String>();int i = 0;long timeStart = System.currentTimeMillis();while (i < num) {list.addFirst(i + "王大锤");i++;}long timeEnd = System.currentTimeMillis();System.out.println("LinkedList从集合头部位置新增元素花费的时间" + (timeEnd - timeStart));
}
ArrayList从集合头部位置新增元素花费的时间415
LinkedList从集合头部位置新增元素花费的时间12

从中间开始插

public static void addFromMidTest4ArrayList(int num) {ArrayList<String> list = new ArrayList<>(num);int i = 0;long timeStart = System.currentTimeMillis();while (i < num) {int temp = list.size();list.add(temp / 2 + "王大锤");i++;}long timeEnd = System.currentTimeMillis();System.out.println("ArrayList从集合中间位置新增元素花费的时间" + (timeEnd - timeStart));
}
public static void addFromMidTest4LinkedList(int num) {LinkedList<String> list = new LinkedList<>();int i = 0;long timeStart = System.currentTimeMillis();while (i < num) {int temp = list.size();list.add(temp / 2, i + "王大锤");i++;}long timeEnd = System.currentTimeMillis();System.out.println("LinkedList从集合中间位置新增元素花费的时间" + (timeEnd - timeStart));
}
ArrayList从集合中间位置新增元素花费的时间19
LinkedList从集合中间位置新增元素花费的时间28610

从尾部开始插

public static void addFromTailTest4ArrayList(int num) {ArrayList<String> list = new ArrayList<>(num);int i = 0;long timeStart = System.currentTimeMillis();while (i < num) {list.add(i + "王大锤");i++;}long timeEnd = System.currentTimeMillis();System.out.println("ArrayList从集合尾部位置新增元素花费的时间" + (timeEnd - timeStart));
}
public static void addFromTailTest4LinkedList(int num) {LinkedList<String> list = new LinkedList<>();int i = 0;long timeStart = System.currentTimeMillis();while (i < num) {list.add(i + "王大锤");i++;}long timeEnd = System.currentTimeMillis();System.out.println("LinkedList从集合尾部位置新增元素花费的时间" + (timeEnd - timeStart));
}
ArrayList从集合尾部位置新增元素花费的时间17
LinkedList从集合尾部位置新增元素花费的时间13

遍历

/*** 10000000* arrayList遍历耗时:50* linkedList遍历耗时:172*/
private static void extracted7() {int count = 10000000;List<String> arrayList = new ArrayList<>();List<String> linkedList = new LinkedList<>();for (int i = 0; i < count; i++) {arrayList.add("lxw" + i);linkedList.add("lxw" + i);}long startA = System.currentTimeMillis();for (String s : arrayList) {}long endA = System.currentTimeMillis();for (String s : linkedList) {}long endL = System.currentTimeMillis();System.out.println("arrayList通过新循环遍历耗时:" + (endA - startA));System.out.println("linkedList通过新循环遍历耗时:" + (endL - endA));
}

ArrayList添加元素流程

list.add(Object e)

  1. 先判断是否需要扩容,如果需要则扩容
    minCapacity = size+要添加元素的个数 add(e) 为1, addAll(sublist) 为sublist.size()
    minCapacity > elementData.length
  2. 通过索引将元素添加到末尾
    elementData[size++] = e

list.add(int index, Object e) 性能比较差

  1. 检查插入的位置是否在合理的范围之内
    不合理( index > size || index < 0)则抛异常IndexOutOfBoundsException
  2. 先判断是否需要扩容,如果需要则进行扩容
  3. 把index以及之后的元素复制到index之后
    System.arraycopy(elementData, index, elementData, index + 1,size - index);
  4. 将新元素添加到index位置
    elementData[index] = e

LinkedList 添加元素流程

list.add(Object e)

直接将元素添加到队尾

list.add(int index, Object e)

  1. 检查插入的位置是否在合理的范围之内
index >= 0 && index <= size
  1. 如果插入的位置是队尾,则尾插

  2. 如果不是队尾, 寻找元素 Node node(int index) 如果index < (size >> 1) 从头开始找,否则从末尾往前找(size >> 1 size的一半向下取整)

  3. 将e连接到node前面

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

ArrayList 的扩容机制?

minCapacity=size+要添加元素的个数(add(E e)与addAll(Collection<? extends E> c))

扩容条件 minCapacity>elementData.length

private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;// 扩容1.5倍(向下取整)int newCapacity = oldCapacity + (oldCapacity >> 1);// 扩容后新容量还存不下 list.addAll(subList) 原始容量 10 扩容后newCapacity = 15 minCapacity = 100if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);
}

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

  • Array:申明数组的时候就要初始化并确定长度,长度不可变,而且它只能存储同一类型的数据,(Array可以存储基本类型)只对外提供了一个length属性.

  • ArrayList 是基于Array实现的可以动态扩容的集合类,提供了更丰富的方法.ArrayList 不能存储基本类型(包装类)

  • 当能确定长度并且数据类型一致的时候就可以用数组,其他时候使用ArrayList

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

HashMap 的 put 方法的执行过程?

http://150.158.33.191/myblog/#/BlogArticleDetails?id=83

HashMap 的 get 方法的执行过程?

HashMap 的 resize 方法的执行过程?

/*** 1.计算新容量* 2.计算新阈值* 3.根据新容量创建新数组,并将新数组赋值给table* 4.将老数组中的元素转移到新数组*/
final HashMap.Node<K, V>[] resize() {HashMap.Node<K, V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {// 不再扩容,修改阈值threshold = Integer.MAX_VALUE;return oldTab;} else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold} else if (oldThr > 0) // initial capacity was placed in threshold 初始化容量被临时存储在阈值里面了// 指定容量的情况 会初始化阈值 临时将容量存到阈值里, 否则没法区分是初始化扩容还是初始化后的扩容newCap = oldThr;else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) {float ft = (float) newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?(int) ft : Integer.MAX_VALUE);}// 给新阈值赋值threshold = newThr;@SuppressWarnings({"rawtypes", "unchecked"})// 创建新数组HashMap.Node<K, V>[] newTab = (HashMap.Node<K, V>[]) new HashMap.Node[newCap];// 重新给table赋值table = newTab;// 将oldTab里面的元素赋值给tabif (oldTab != null) {for (int j = 0; j < oldCap; ++j) {HashMap.Node<K, V> e;if ((e = oldTab[j]) != null) {// 转移 类似于剪贴oldTab[j] = null;// hash桶中只有一个元素if (e.next == null)// 相当于rehashnewTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)((HashMap.TreeNode<K, V>) e).split(this, newTab, j, oldCap);else { // preserve order// loHead 低位的头元素 loTail低位的为元素HashMap.Node<K, V> loHead = null, loTail = null;// hiHead高位的头元素 hiTail高位的尾元素HashMap.Node<K, V> hiHead = null, hiTail = null;// HashMap.Node<K, V> next;// 将原链表拆成2个子链表do {// 为了在while条件中用next = e.next;// (e.hash & oldCap) == 0 计算高低位 if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;} else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;// 将低位链表复制给新数组的j位置newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;// 将高位链表赋值给新数组的j+oldCapnewTab[j + oldCap] = hiHead;}}}}}return newTab;
}

HashMap 的 size 为什么必须是 2 的整数次方?

为了利用&运算计算节点在数组中的下标

HashMap 多线程死循环问题?

HashMap 的 get 方法能否判断某个元素是否在 map 中?

不能,HashMap允许value为null,返回null无法区分是不存在还是value为null

HashMap 与 HashTable 的区别是什么?

1. HashTable 继承自 Dictionary 类实现了Map接口, HashMap 继承自AbstractMap实现了Map接口。
2. HashTable 是线程安全的,HashMap 不是。
3. HashMap允许将 null key和null value,而 Hashtable 不允许null key和null value。
4. HashMap 只有containsKey(Object key)和containsValue(Object value), Hashtable 额外提供了contains(Object value)( contains 方法容易让人引起误解)。

HashMap 与 ConcurrentHashMap 的区别是什么?

1. HashMap 线程不安全,ConcurrentHashMap 线程安全.
2. HashMap允许null key和null value,ConcurrentHashMap不允许null key和null value

HashTable 和 ConcurrentHashMap 的区别?

1. 底层数据结构: JDK1.7 的 ConcurrentHashMap 底层采用 分段数组+链表 实现,而 JDK1.8 的 ConcurrentHashMap 实现跟 HashMap1.8 的数据结构一样,都是 数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似,都是采用 数组+链表 的形式。数组是 HashMap 的主体,链表则是为了解决哈希冲突而存在的;
2. 实现线程安全的方式: ① 在 JDK1.7 的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段( Segment ),每一把锁只锁容器其中的一部分数据,这样多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高了并发访问率。 到了 JDK1.8,摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作,(JDK1.6 以后对 synchronized 锁做了很多的优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在 JDK1.8 中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② Hashtable (同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。一个线程访问同步方法时,当其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程就不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈,效率就越低。

ConcurrentHashMap 的实现原理是什么?

  1. HashSet 的实现原理?

HashSet 怎么保证元素不重复的?

HashSet 保证元素不重复是利用HashMap 的put 方法实现的,在存储之前先根据key 的hashCode 和equals 判断是否已存在,如果存在就不在重复插入了,这样就保证了元素的不重复。

LinkedHashMap 的实现原理?

LinkedHashMap 继承自HashMap,重写了newNode,afterNodeAccess,afterNodeInsertion,afterNodeRemoval等方法.
在newNode方法中维护了双向链表
默认是插入属性,也可以通过LinkedHashMap(int initialCapacity, float loadFactor,boolean accessOrder)的accessOrder指定访问属性.
LinkedHashMap里并没有重写put方法,只是重写了put中调用的部分方法(模板方法模式)

Iterator 怎么使用?有什么特点?

Iterator 和 ListIterator 有什么区别?

ListIterator 继承自Iterator.
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。 
Iterator对集合只能是单向移动前向遍历,ListIterator既可以前向也可以后向。
ListIterator 在迭代过程中不光可以删除元素,还能新增元素,修改元素.Iterator 只能删除元素
public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("1");list.add("2");list.add("3");ListIterator<String> listIterator = list.listIterator();while (listIterator.hasPrevious()){String previous = listIterator.previous();System.out.println("previous = " + previous);}System.out.println("------------");while (listIterator.hasNext()){String next = listIterator.next();if ("1".equals(next)) {listIterator.set("11");}if ("2".equals(next)) {listIterator.remove();}if ("3".equals(next)) {listIterator.add("4");}System.out.println("next = " + next);}System.out.println("====================");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String next = iterator.next();System.out.println("next = " + next);}
}
  1. Iterator 和 Enumeration 接口的区别?

  2. fail-fast 与 fail-safe 有什么区别?

Collection 和 Collections 有什么区别?

Collection 是集合接口
Collections 是集合的工具类

https://zhuanlan.zhihu.com/p/92481037

  1. table 的初始化时机是什么时候,初始化的 table.length 是多少、阀值(threshold)是多少,实际能容下多少元素

    new HashMap<>()
    初始化时机是第一次put元素的时候,table.length是16,threshold是12,时机能容下12个元素
  2. 什么时候触发扩容,扩容之后的 table.length、阀值各是多少?

    第一次添加元素的时候(tab == null)tab.length<64 && 链表长度>8++size > threshold
    
  3. table 的 length 为什么是 2 的 n 次幂

    为了利用&运算求key在数组中的下标
    
  4. 求索引的时候为什么是:h&(length-1),而不是 h&length,更不是 h%length

    h%length 效率不如位运算快
    h&length hash碰撞多,会导致 table 的空间得不到利用(有的桶里放了很多,有的桶里空着)、降低 table 的操作效率
    h&(length-1) hash碰撞少   
    &右边为1或者连续的1时&左边的和=右边的相同(只要左边相对分散(由hash控制),结果就相对分散)
    1&1=1;
    0&1=0;
    1&0=0;
    0&0=0;
    1010&1111=1010; => 10&15=10;
    1011&1111=1011; => 11&15=11;
    01010&10000=00000; => 10&16=0;
    01011&10000=00000; => 11&16=0;
    2^n-1 低位为连续的1,length刚好为2^n
  5. Map map = new HashMap(1000); 当我们存入多少个元素时会触发map的扩容; Map map1 = new HashMap(10000); 我们存入第 10001个元素时会触发 map1 扩容吗

    Map map = new HashMap(1000); 当我们存入多少个元素时会触发map的扩容
    此时的 table.length = 2^10 = 1024; threshold = 1024 * 0.75 = 768; 所以存入第 768 个元素时进行扩容Map map1 = new HashMap(10000); 我们存入第 10001个元素时会触发 map1 扩容吗
    此时的 table.length = 2^14 = 16384; threshold = 16384 * 0.75 = 12288; 所以存入第 10001 个元素时不会进行扩容
    
  6. 为什么加载因子的默认值是 0.75,并且不推荐我们修改

    为什么加载因子的默认值是 0.75,并且不推荐我们修改
    如果loadFactor太小,那么map中的table需要不断的扩容,扩容是个耗时的过程
    如果loadFactor太大,那么map中table放满了也不不会扩容,导致冲突越来越多,解决冲突而起的链表越来越长,效率越来越低
    而 0.75 这是一个折中的值,是一个比较理想的值
    

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

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

相关文章

Unity的滑动控制相机跟随和第三人称视角三

Unity的相机跟随和第三人称视角三 第三人称相机优化介绍讲解拖动事件相机逻辑人物移动逻辑总结 第三人称相机优化 Unity第三人称相机视角一 Unity第三人称相机视角二 介绍 之前相机视角讲过了两篇文章了&#xff0c;但是都是自动旋转视角&#xff0c;今天来了新需求&#xf…

支部管理系统微信小程序(管理端+用户端)flask+vue+mysql+微信小程序

系统架构如图所示 高校D支部管理系统 由web端和微信小程序端组成&#xff0c;由web端负责管理&#xff0c;能够收缴费用、发布信息、发布问卷、发布通知等功能 部分功能页面如图所示 微信小程序端 包含所有源码和远程部署&#xff0c;可作为毕设课设

网络中UDP编程

网络 1.recvfrom ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 功能: 从套接字中接收数据 参数: sockfd:套接字文件描述符 buf:存放数据空间首…

Java 数据结构之链表

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA null || headB null) return null;ListNode pA headA, pB headB;while (pA ! pB) {pA pA null ? headB : pA.next;pB pB null ? headA : pB.next;}return pA;} public ListNode rev…

Pytorch将数据(张量)写入视频

Pytorch将数据写入视频 1. 安装与报错解决2. torch.io程序3. cv2 img文件转视频参考资料 1. 安装与报错解决 安装 pip install PyAV报错问题1的解决 报错信息如下&#xff1a; ...tf2_py38\lib\site-packages\torchvision\io\video.py", line 41, in _check_av_availab…

解决Git中fatal: refusing to merge unrelated histories

我们在git新建项目的时候一般是先建一个远程仓库&#xff0c;然后本地与之关联&#xff0c;再将本地代码推到远程仓库就可以了&#xff0c;不过有可能返回以下错误&#xff1a; fatal: refusing to merge unrelated histories 这时需要在命令后增加以下命令即可 --allow-unr…

【Unity】【VR开发】如何避免按键冲突

【背景】 VR开发过程中,控制器按键冲突是常有的问题。比如握住手枪时发射子弹用的Trigger,瞬移用的也是Trigger,如何解决这种冲突呢? 【分析】 为了让同一个Trigger按键在不同场景下分别触发合适的动作,需增加判断逻辑来区分场景。具体到当前的问题,需要追加判断逻辑区…

数据库系列之:什么是 SAP HANA?

数据库系列之&#xff1a;什么是 SAP HANA&#xff1f; 一、什么是 SAP HANA&#xff1f;二、什么是内存数据库&#xff1f;三、SAP HANA 有多快&#xff1f;四、SAP HANA 的十大优势五、SAP HANA 架构六、数据库设计七、数据库管理八、应用开发九、高级分析十、数据虚拟化 一、…

通信(四)gRPC

gRPC 1. 简介2. C2.1 编译.proto生成.pb.h和.pb.cc服务器示例客户端示例 3. C#3.1 导入NuGet程序包编译.proto生成.cs和Grpc.cs示例 1. 简介 2. C 2.1 编译.proto生成.pb.h和.pb.cc grpc_cpp_plugin.exe生成编译时.proto自动生成 服务器示例 客户端示例 3. C# 3.1 导入N…

Linux添加虚拟卷

Linux添加虚拟卷 1.在服务器上安装lvm 使用LVM&#xff08;Logical Volume Manager&#xff09;来将多个物理磁盘挂载到同一个逻辑卷 2.创建 用pvcreate将设备初始化为物理卷 sudo pvcreate /dev/${目标设备} #例如 sudo pvcreate /dev/sdb sudo pvcreate /dev/sdc sudo p…

1-安装rabbitmq

rabbitmq官网&#xff1a; https://www.rabbitmq.com/docs/download 本机环境&#xff1a;mac&#xff0c;使用orbstack提供的docker 使用docker部署rabbitmq docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management 然后报错&#xf…

信息安全服务规范包括哪些方面

信息安全服务规范是确保信息系统安全稳定运行的重要指导原则和操作准则。在信息化高速发展的今天&#xff0c;信息安全已经成为国家、企业乃至个人不可忽视的重要议题。因此&#xff0c;制定和执行信息安全服务规范对于保障信息安全、维护社会秩序具有重要意义。 信息安全服务规…

[C/C++]_[初级]_[关于输出double浮点数精度的方法]

场景 在开发WTL/Win32程序时&#xff0c;绘制进度百分比&#xff0c;需要指定这个百分比包括小数点不超过5个字符&#xff0c;也就是除了小数点之外的数字个数最多只显示4个。已知可能出现的数值有1.1,1.1145,22.14… 用什么方式可以获取需要的字符串呢&#xff1f; 说明 绘…

VScode+Zotero+Latex文献引用联动

一、VScodeLatex联动 1、VScode的安装 2、texlive.iso安装 可以参考以下&#xff0c;也可以忽略所有直接一步一步默认安装 https://zhuanlan.zhihu.com/p/442308176 3、Vscode的插件安装&#xff1a;【latex workshop】 4、打开设置&#xff0c;搜索json&#xff0c;然后点击…

MIT 6.S081---Lab: Multithreading

Uthread: switching between threads (moderate) 修改uthread.c&#xff0c;在thread中新增context字段&#xff1a; 修改uthread.c&#xff0c;在thread_create函数中新增以下逻辑&#xff1a; 修改uthread.c中的thread_switch函数定义&#xff1a; 修改uthread.c中的th…

你不得不知道的Python AI库

Python是人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;领域中使用最广泛的编程语言之一&#xff0c;拥有丰富的库支持各种AI和ML任务。本文介绍一些经典的Python AI库。 1. NumPy 简介&#xff1a;NumPy&#xff08;Numerical Python&#xff09;…

Meta AI移动设备上部署LLM的新框架MobileLLM

Meta AI 研究团队推出的 MobileLLM 标志着大语言模型(LLMs)朝着模拟人类理解和生成自然语言迈出了革命性的一步。LLMs 在处理和分析大量数据集方面的能力已经显著影响了自动化客户服务、语言翻译和内容创作等多个领域。然而,由于传统 LLMs 在计算和存储资源方面的需求庞大,…

centos7中python3.10找不到openssl解决方案

如果有用其他方法安装了其他版本openssl&#xff0c;记得卸载其他的openssl&#xff0c;删除其他的openssl相关文件。 yum remove openssl* rm -rf ***下载最新版的openssl文件 按照官网安装方法安装openssl 官方安装地址https://docs.python.org/3/using/unix.html#on-linu…

代码随想录算法训练营第13天

239. 滑动窗口最大值 &#xff08;一刷至少需要理解思路&#xff09; 方法&#xff1a;暴力法 &#xff08;时间超出限制&#xff09; 注意&#xff1a; 代码&#xff1a; class Solution { public:vector<int> maxSlidingWindow(vector<int>& nums, int k…

目标检测YOLO实战应用案例100讲-基于数据融合的SAR图像中海上目标检测与跟踪

目录 前言 国内外研究现状 船舰目标监视资源 光学卫星 SAR卫星