【复习】Java集合

集合概念

集合与数组

数组是固定长度;集合是动态长度的数据结构,需要动态增加或删除元素

数组可以包含基本数据类型和对象;集合只能包含对象

数组可以直接访问元素;集合需要通过迭代器访问元素

线程安全的集合?

java.util包:

  1. Vector:线程安全的动态数组,内部方法大部分都经过synchronized修饰
  2. Hashtable:线程安全的哈希表,内部方法大部分都经过synchronized修饰,这样被所著的就是整个Table对象(底层是数组+链表)

并发Map:

  1. ConcurrentHashMap:
  • JDK7,ConcurrentMap加的是分段锁(Segment锁),每个分段锁含有整个table的一部分,不同分段之间的并发互不影响【每个Segment都类似一个小的HashMap,对于插入、更新、删除操作时,需要先定位到具体的Segment,再在Segment上加锁】
  • JDK8,取消了Segment字段,直接在table元素上枷锁,实现对每一行进行加锁,进一步减少了并发冲突。主要通过volatile + CAS(乐观锁)或 synchronized(悲观锁)来实现的线程安全。

添加元素时,先判断容器是否为空:

  • 为空:直接使用volatile + CAS来初始化
  • 不为空:根据存储的元素计算该位置是否为空
    • 如果存储的元素计算结果为空,利用CAS设置该节点
    • 如果存储的元素计算结果不为空,使用synchronized,然后遍历桶中的元素,并替换或新增节点到桶中,再判断是否需要转成红黑树。(当发生hash碰撞时说明容量不够用或已经有大量线程访问,因此使用synchronized来处理hash比CAS效率高)
  1. ConcurrentSkipListMap:实现了一个基于跳表(SkipList)算法的可排序的并发集合,通过维护多个指向其他元素的跳跃链接来实现高效查询

并发Set:

  1. ConcurrentSkipListSet:线程安全的有序集合,底层是使用ConcurrentSkipListMap实现
  2. CopyOnWriteArraySet:线程安全的HashSet

并发List:

  1. CopyOnWriteArrayList:线程安全的ArrayList,底层通过一个数组保存数据,使用volatile关键字修饰数组,保证当前线程对数组对象重新赋值后,其他线程可以感知到;在写入新元素时,会将原来的数组拷贝一份并让原来数组的长度+1后就得到一个新数组,将元素放入新数组的最后一个位置,用新数组地址替换旧数组地址就能得到最新数据了;读操作时不加锁,一直都能读的。

并发Queue:

  1. ConcurrentLinkedQueue:高并发场景下的队列,通过无锁(CAS)的方式,实现高并发状态下的高性能。
  2. BlockingQueue:提供一种读写等待的机制,简化多线程间的数据共享。

并发Deque:

  1. LinkedBlockingDeque:线程安全的双端队列,内部使用双向链表结构

  2. ConcurrentLinkedQueue:基于链表节点的无限并发链表,可以安全的并发执行插入、删除、访问的操作

List

ArrayList是线程安全的嘛?有什么办法可以把ArrayList变成线程安全的?

  1. 使用Collections类的synchronizedList方法将ArrayList包装成线程安全的List
List<String> synchronizedList = Collections.synchronizedList(arrayList);
  1. 使用CopyOnWriteArrayList(这是一个线程安全的List)代替ArrayList
CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>(arrayList);
  1. 使用Vector类代替ArrayList
Vector<String> vector = new Vector<>(arrayList);

Map

HashMap是线程安全的吗?

HashMap不是线程安全的,在多线程环境下会存在以下问题:

  • JDK7,采用数组+链表,多线程环境下,数组扩容时会存在Entry链死循环和数据丢失的问题
  • JDK8,采用数组+链表+红黑树,解决了Entry链死循环和数据丢失的问题,但是多线程环境下,put元素会出现覆盖的情况

保证线程安全:

  • 使用Collections.synchronizedMap同步加锁的方式,还可以使用HashTable。
  • 使用ConcurrentHashMap。(JDK7使用分段锁;JDK8使用CAS+synchronized)

HashMap一般用什么作为Key?为什么String适合做key?

用String做key,因为String对象是不可变的,一旦创建旧不能被修改,保证了key的稳定性。如果key是可变的,会导致hashCode和equals方法的不一致。

为什么HashMap要用红黑树而不是平衡二叉树?

平衡二叉树追求的是绝对平衡,导致每次在插入或删除节点时,都需要通过左旋或右旋来调整,使它再次称为一颗符合要求的平衡二叉树

红黑树不支持这种完全平衡的规则, 牺牲了一部分的查找效率,但是可以换取一部分维持平衡状态的成本。

HashMap的key可以为null嘛

HashMap使用hash()方法来计算key的哈希值,当key为空时,直接设置hash值为0,不走hashCode方法。

static final int hash(){return (key == null) ? 0 :hashCode()后处理
}

null作为key只能有一个,作为value可以有多个

重写HashMap的equals和hashCode方法需要注意什么?

从HashMap中获取值时,这些方法也会被用到,如果这些方法没有被正确的实现,两个不同的Key可能会产生相同的hashCode()和equals()输出,HashMap会认为他们是相同的,把他们存在不同的地方。

重写HashMap的equals方法不当会出现什么情况?

HashMap在比较元素时,先比较hashCode方法,如果hash值相同,再比较equals方法。

如果重写hashCode方法,但是不重写equals方法,会出现equals方法返回false,导致hashMap中存储多个一样的对象,与hashMap只有唯一的key的规范不符合。

HashMap在多线程下可能出现的问题?

  1. JDK7使用头插法插入元素,多线程环境下,扩容时可能导致环形链表的出现,形成死循环。因此JDK8使用尾插法插入元素,在扩容时保持链表原本的顺序不变,不会出现环形链表的问题

因为JDK7使用头插法,对数组长度进行扩容时,如果原来是1->2->3,扩容后就会变成3->2->1

如果多个线程同时对HashMap进行扩容操作,可能会导致链表的指针混乱,形成环形链表。

  1. 多线程同时执行put操作,如果计算出来的索引位置是想通的,那会造成前一个key被后一个key覆盖,从而导致元素丢失的问题

HashMap的扩容机制

在扩充HashMap时,不需要重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0

索引 = (数组下标 - 1) & 哈希值

在这里插入图片描述

  • 如果是0:索引没变
  • 如果是1:索引 = 原索引 + 旧容量

所以HashMap的大小是2的n次方,这样就不需要重新计算hash值。

HashMap和HashTable的区别

  1. HashMap线程不安全,可以存储null的key和value,每次扩容成原来的2n,要保证线程安全也可以用ConcurrentHashMap。
  2. HashTable线程安全,所有的方法都加synchronized锁,不可以有null的key和value,效率低,每次扩容为原来的2n + 1。

HashTable和ConcurrentHashMap的区别?

  • ConcurrentHashMap是线程安全的。读操作不需要加锁,写操作需要加锁。JDK7,ConcurrentHashMap采用数组 + 链表,并发控制用分段锁;JDK8,ConcurrentHashMap采用数组 + 链表 + 红黑树,并发控制用CAS + synchronized
  • HashTable采用的是数组 + 链表,所有的方法都加synchronized锁

Set

Set集合的特点?

Set集合是唯一的,不会有重复的元素。

向Set集合中插入元素时,先通过hashCode确定元素的存储位置,再通过equals判断是否存在相同的元素,如果存在不会再次插入。

有序Set是什么?

有序的Set是TreeSet和LinkedHashSet

  • TreeSet是基于红黑树来保证元素的自然顺序
  • LinkedHashSet是基于双向链表和哈希表的结合来保证元素添加的自然顺序(可以记录插入顺序的集合,不仅保证元素的唯一性,还可以保证元素的插入顺序)

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

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

相关文章

vue3 文件类型传Form Data数据格式给后端

在 Vue 3 中&#xff0c;如果你想将文件&#xff08;例如上传的 Excel 文件&#xff09;以 FormData 格式发送到后端&#xff0c;可以通过以下步骤实现。这种方式通常用于处理文件上传&#xff0c;因为它可以将文件和其他数据一起发送到服务器。 首先&#xff0c;创建一个 Vue…

使用 INFINI Console 配置集群监控 Webhook 通知指南

在集群管理中&#xff0c;监控关键指标如CPU、内存、磁盘、JVM等是至关重要的。对于Easysearch及ES生态系统&#xff0c;还需要关注集群本身的指标&#xff0c;例如搜索延迟、集群状态、节点移除等。INFINI Console不仅提供了默认的监控指标&#xff0c;还支持用户自定义监控项…

WPF的页面设计和实用功能实现

目录 一、TextBlock和TextBox 1. 在TextBlock中实时显示当前时间 二、ListView 1.ListView显示数据 三、ComboBox 1. ComboBox和CheckBox组合实现下拉框多选 四、Button 1. 设计Button按钮的边框为圆角&#xff0c;并对指针悬停时的颜色进行设置 一、TextBlock和TextBox…

二级公共基础之数据结构与算法篇(八)排序技术

目录 前言 一、交换类排序 1.冒泡排序法 1. 冒泡排序的思想 2. 冒泡排序的实现步骤 3. 示例 4. 冒泡排序的特点 2.快速排序 1. 快速排序的核心思想 2. 快速排序的实现步骤 3. 示例代码(C语言) 4. 快速排序的特点 二、插入类排序 1. 简单插入排序 1.简单插入排…

记录一次 ALG 的处理过程

前几天朋友找我帮忙&#xff0c;说碰到很大困难了&#xff0c;实际上&#xff0c;不过如此 现象是这样的&#xff1a; FreeSWITCH mod_unimrcp 工作不正常 FS 和 mrcp-server 两边同时抓包&#xff0c;看到的是&#xff1a; sip 流程正常 FS TCP 连接到 mccp-server 失败&…

【Linux网络编程】IP协议格式,解包步骤

目录 解析步骤 1.版本字段&#xff08;大小&#xff1a;4比特位&#xff09; 2.首部长度&#xff08;大小&#xff1a;4比特位&#xff09;&#xff08;单位&#xff1a;4字节&#xff09; &#x1f35c;细节解释&#xff1a; 3.服务类型&#xff08;大小&#xff1a;8比特…

CSDN文章质量分查询系统【赠python爬虫、提分攻略】

CSDN文章质量分查询系统 https://www.csdn.net/qc 点击链接-----> CSDN文章质量分查询系统 <------点击链接 点击链接-----> https://www.csdn.net/qc <------点击链接 点击链接-----> CSDN文章质量分查询系统 <------点击链接 点击链…

HTML应用指南:利用GET请求获取全国泸溪河门店位置信息

随着新零售业态的快速发展,门店位置信息的获取变得越来越重要。作为新兴烘焙品牌之一,泸溪河自2013年在南京创立以来,一直坚持“健康美味,香飘世界”的企业使命,以匠人精神打造新中式糕点。为了更好地理解和利用这些数据,本篇文章将深入探讨GET请求的实际应用,并展示如何…

如何在 React 中测试高阶组件?

在 React 中测试高阶组件可以采用多种策略&#xff0c;以下是常见的测试方法&#xff1a; 1. 测试高阶组件返回的组件 高阶组件本身是一个函数&#xff0c;它返回一个新的组件。因此&#xff0c;可以通过测试这个返回的组件来间接测试高阶组件的功能。通常使用 Jest 作为测试…

R语言Stan贝叶斯空间条件自回归CAR模型分析死亡率多维度数据可视化

全文链接&#xff1a;https://tecdat.cn/?p40424 在空间数据分析领域&#xff0c;准确的模型和有效的工具对于研究人员至关重要。本文为区域数据的贝叶斯模型分析提供了一套完整的工作流程&#xff0c;基于Stan这一先进的贝叶斯建模平台构建&#xff0c;帮助客户为空间分析带来…

Casbin 权限管理介绍及在 Go 语言中的使用入门

引言 在现代软件开发过程中&#xff0c;权限管理是一个至关重要的环节&#xff0c;它关系到系统的安全性和用户体验。Casbin 是一个强大的访问控制库&#xff0c;支持多种访问控制模型&#xff0c;如 ACL&#xff08;访问控制列表&#xff09;、RBAC&#xff08;基于角色的访问…

快速入门——第三方组件element-ui

学习自哔哩哔哩上的“刘老师教编程”&#xff0c;具体学习的网站为&#xff1a;10.第三方组件element-ui_哔哩哔哩_bilibili&#xff0c;以下是看课后做的笔记&#xff0c;仅供参考。 第一节 组件间的传值 组件可以有内部Data提供数据&#xff0c;也可由父组件通过prop方式传…

【算法通关村 Day7】递归与二叉树遍历

递归与二叉树遍历青铜挑战 理解递归 递归算法是指一个方法在其执行过程中调用自身。它通常用于将一个问题分解为更小的子问题&#xff0c;通过重复调用相同的方法来解决这些子问题&#xff0c;直到达到基准情况&#xff08;终止条件&#xff09;。 递归算法通常包括两个主要…

朴素贝叶斯法

文章目录 贝叶斯定理朴素贝叶斯法的学习与分类条件独立假设朴素贝叶斯的后验概率最大化准则朴素贝叶斯的基本公式 朴素贝叶斯法的参数估计极大似然估计 贝叶斯定理 前置知识&#xff1a;条件概率、全概率、贝叶斯公式 推荐视频&#xff0c;看完视频后搜索博客了解先验概率、后…

《A++ 敏捷开发》- 20 从 AI 到最佳设计

“我们现在推行AIGC&#xff0c;服务端不需要UI交互设计的用AI自动产出代码&#xff0c;你建议的结对编程、TDD等是否还适用&#xff1f;” 这两年AI确实很火&#xff0c;是报纸、杂志的热门话题。例如&#xff0c;HBR杂志从2024年9月至2025年二月份3期&#xff0c;里面有接近一…

GO系列-IO 文件操作

os io 判断文件是否存在 func fileExist(filePath string) (bool, error) {_, err : os.Stat(filePath)if err nil {return true, nil}if os.IsNotExist(err) {return false, nil}return false, &CheckFileExistError{filePath} } 读取文件内容 func readFileContext(…

rs485协议、电路详解(保姆级)

起源 RS-485即Recommended Standard 485 协议的简写。1983年被电子工业协会(EIA)批准为一种通讯接口标准. 数据在通信双方之间传输&#xff0c;本质是传输物理的电平&#xff0c;比方说传输5V的电压 -1V的电压信号&#xff0c;这些物理信号在传输过程中会受到很多干扰&#x…

JavaWeb-Tomcat服务器

文章目录 Web服务器存在的意义关于Web服务器软件Tomcat服务器简介安装Tomcat服务器Tomcat服务器源文件解析配置Tomcat的环境变量启动Tomcat服务器一个最简单的webapp(不涉及Java) Web服务器存在的意义 我们之前介绍过Web服务器进行通信的原理, 但是我们当时忘记了一点, 服务器…

【愚公系列】《Python网络爬虫从入门到精通》008-正则表达式基础

标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…

视觉分析之边缘检测算法

9.1 Roberts算子 Roberts算子又称为交叉微分算法&#xff0c;是基于交叉差分的梯度算法&#xff0c;通过局部差分计算检测边缘线条。 常用来处理具有陡峭的低噪声图像&#xff0c;当图像边缘接近于正45度或负45度时&#xff0c;该算法处理效果更理想。 其缺点是对边缘的定位…