Java 进阶 -- 集合(三)
4、实现
实现是用于存储集合的数据对象,它实现了接口部分中描述的接口。本课描述了以下类型的实现:
- 通用实现是最常用的实现,是为日常使用而设计的。它们在标题为“通用实现”的表格中进行了总结。
- 特殊目的实现是为在特殊情况下使用而设计的,并显示非标准的性能特征、使用限制或行为。
- 并发实现旨在支持高并发性,通常以牺牲单线程性能为代价。这些实现是
java.util.concurrent
包的一部分。 - Wrapper 实现:与其他类型的实现(通常是通用实现)结合使用,以提供添加的或受限制的功能。
- 便利实现是小型实现,通常通过静态工厂方法提供,它为特殊集合(例如,单例sets)提供方便、高效的通用实现替代方案。
- 抽象实现是便于构建自定义实现的骨架实现——稍后将在自定义集合实现部分进行描述。这是一个高级的话题,不是特别难,但相对来说很少有人需要做。
下表总结了通用实现。
从表中可以看到,Java Collections Framework提供了Set、List和Map接口的几种通用实现。在每种情况下,一种实现——HashSet、ArrayList和HashMap——在其他条件相同的情况下显然适合大多数应用程序。注意,SortedSet和SortedMap接口在表中没有行。这些接口中的每一个都有一个实现(TreeSet和TreeMap),并在Set
和Map
行中列出。有两种通用的Queue
实现——LinkedList(也是一个列表实现)和PriorityQueue(从表中省略)。这两个实现提供了非常不同的语义:LinkedList
提供FIFO语义,而PriorityQueue
根据其值对其元素排序。
每个通用实现都提供其接口中包含的所有可选操作。它们都允许null
元素、键和值。没有一个是同步的(线程安全)。它们都有快速失败迭代器(fail-fast iterators
),可以在迭代期间检测非法的并发修改,并快速而干净地失败,而不是在未来不确定的时间冒任意的、不确定的行为的风险。它们都是可序列化的(Serializable
),并且都支持公共clone
方法。
这些实现是不同步的,这代表了与过去的决裂:遗留集合Vector
和Hashtable
是同步的。之所以采用目前的方法,是因为在同步没有好处的情况下经常使用集合。这些用途包括单线程使用、只读使用以及作为自己进行同步的较大数据对象的一部分使用。一般来说,不让用户为他们不使用的功能付费是良好的API设计实践。此外,在某些情况下,不必要的同步可能导致死锁。
如果需要线程安全的集合,那么在包装器实现一节中描述的同步包装器允许将任何集合转换为同步集合。因此,同步对于通用实现是可选的,而对于遗留实现是强制的。此外,java.util.concurrent
包提供了扩展Queue
的BlockingQueue
接口和扩展Map
的ConcurrentMap
接口的并发实现。这些实现比单纯的同步实现提供更高的并发性。
通常,您应该考虑接口,而不是实现。这就是本节中没有编程示例的原因。在大多数情况下,实现的选择只影响性能。如接口部分所述,首选的风格是在创建Collection
时选择一个实现,并立即将新集合分配给相应接口类型的变量(或将集合传递给期望具有接口类型参数的方法)。通过这种方式,程序不会依赖于给定实现中添加的任何方法,从而使程序员可以根据性能考虑或行为细节随时自由地更改实现。
接下来的小节将简要讨论这些实现。实现的性能使用诸如常数时间(constant-time
)、对数(log
)、线性(linear
)、nlog(n)
和二次(quadratic
)等词来描述,以表示执行操作的时间复杂度的渐近上界。所有这些都很拗口,如果你不知道它的意思也没关系。如果你有兴趣了解更多,可以参考任何好的算法教科书。需要记住的一点是,这种性能指标有其局限性。有时,名义上较慢的实现可能更快。当有疑问时,衡量性能!
4.6 Wrapper 实现
包装器(Wrapper
)实现将其所有实际工作委托给指定的集合,但在该集合提供的功能之上添加额外的功能。对于设计模式爱好者来说,这是装饰器(decorator)模式的一个示例。虽然这看起来有点奇怪,但实际上非常简单。
这些实现是匿名的;该库不是提供一个公共类,而是提供一个静态工厂方法。所有这些实现都可以在Collections类中找到,该类仅由静态方法组成。
4.6.1 同步包装器
同步包装器向任意集合添加自动同步(线程安全)。六个核心集合接口(Collection、Set、List、Map、SortedSet和SortedMap)中的每一个都有一个静态工厂方法。
// 返回由指定集合支持的同步(线程安全)集合。为了保证串行访问,
// 对backing集合的所有访问都必须通过返回的集合来完成,这一点至关重要。
// 当通过Iterator、Spliterator或Stream遍历返回的集合时,用
// 户必须手动同步返回的集合:Collection c = Collections.synchronizedCollection(myCollection);...synchronized (c) {Iterator i = c.iterator(); // Must be in the synchronized blockwhile (i.hasNext())foo(i.next());}// 不遵循此建议可能会导致不确定的行为。
// 返回的集合不将hashCode和equals操作传递给backing集合,而是依
// 赖于Object的equals和hashCode方法。在backing集合是set或list
// 的情况下,这对于保留这些操作的契约是必要的。
// 如果指定的集合是可序列化的,则返回的集合将是可序列化的。
public static <T> Collection<T> synchronizedCollection(Collection<T> c);
public static <T> Set<T> synchronizedSet(Set<T> s);
public static <T> List<T> synchronizedList(List<T> list);
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m);
public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s);
public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m);
这些方法中的每一个都返回由指定集合的同步(线程安全)Collection
。为了保证串行访问,所有对后备集合的访问都必须通过返回的集合来完成**。保证这一点的简单方法是不保留对后备集合的引用**。使用以下技巧创建同步集合。
List<Type> list = Collections.synchronizedList(new ArrayList<Type>());
以这种方式创建的集合与通常同步的集合(如Vector)一样,都是线程安全的。
面对并发访问,当迭代返回的集合时,用户必须手动同步返回的集合。原因是迭代是通过对集合的多个调用来完成的,这些调用必须组合成单个原子操作
。下面是迭代包装器同步集合的习惯用法。
Collection<Type> c = Collections.synchronizedCollection(myCollection);
synchronized(c) {for (Type e : c)foo(e);
}
如果使用显式迭代器,则必须从synchronized
内部调用iterator
方法。不遵循此建议可能会导致不确定性行为。迭代同步Map
的Collection
视图的习惯用法与此类似。当迭代任何Collection
视图时,用户必须在synchronized Map
上同步,而不是在Collection
视图本身同步,如下面的示例所示。
Map<KeyType, ValType> m = Collections.synchronizedMap(new HashMap<KeyType, ValType>());...
Set<KeyType> s = m.keySet();...
// Synchronizing on m, not s!
synchronized(m) {while (KeyType k : s)foo(k);
}
使用包装器实现的一个小缺点是,您无法执行包装实现的任何非接口(noninterface
)操作。因此,例如,在前面的List
示例中,您不能在包装的ArrayList
上调用ArrayList's ensureCapacity
操作。
4.6.2 不可变包装器
与同步包装器不同,同步包装器将功能添加到包装的集合中,不可修改的包装器将功能删除。特别是,它们通过拦截所有可能修改集合的操作并抛出UnsupportedOperationException
,从而剥夺了修改集合的能力。不可修改的包装有两个主要用途,如下:
- 使集合在构建后不可变。在这种情况下,最好不要维护对后备集合的引用。这绝对保证了不变性。
- 允许某些客户端只读访问您的数据结构。您保留了对backing 集合的引用,但分发了对包装器的引用。通过这种方式,客户端可以查看但不能修改,而您可以保持完全访问权限。
与同步包装器一样,六个核心Collection
接口中的每一个都有一个静态工厂方法。
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c);
public static <T> Set<T> unmodifiableSet(Set<? extends T> s);
public static <T> List<T> unmodifiableList(List<? extends T> list);
public static <K,V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m);
public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<? extends T> s);
public static <K,V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> m);
4.6.3 已检查的接口包装
Collections.checked
接口包装器提供用于泛型集合。这些实现返回指定集合的动态类型安全视图,如果客户端试图添加错误类型的元素,则会抛出ClassCastException
。该语言中的泛型机制提供了编译时(静态)类型检查,但也有可能破坏这种机制。动态类型安全视图完全消除了这种可能性。
4.7 便利实现
本节描述几个迷你实现,当您不需要它们的全部功能时,它们比通用实现更方便、更高效。本节中的所有实现都是通过静态工厂方法而不是公共类提供的。
Array的列表视图
Arrays.asList方法返回其数组参数的List
视图。对List
的更改贯穿写入数组,反之亦然。集合的大小是数组的大小,不能更改。如果在List
上调用add
或remove
方法,则会产生UnsupportedOperationException
。
这个实现的正常用途是作为基于数组和基于集合的api之间的桥梁。它允许您将数组传递给期望是Collection
或List
的方法。然而,这个实现还有另一个用途。如果您需要固定大小的List,那么它比任何通用的List实现都更有效。这就是习语。
List<String> list = Arrays.asList(new String[size]);
注意,不保留对后备数组的引用。
不可变多副本列表
有时,您需要一个由同一元素的多个副本组成的不可变列表。Collections.nCopies方法返回这样一个列表。这个实现有两个主要用途。第一个是初始化新创建的List;例如,假设您想要一个初始包含1,000个null
元素的ArrayList。下面的语句可以达到这个目的。
List<Type> list = new ArrayList<Type>(Collections.nCopies(1000, (Type)null));
当然,每个元素的初始值不必为null
。第二个主要用途是增长现有的List。例如,假设您想在List< string >的末尾添加69个字符串“fruit bat”的副本。我不清楚你为什么要做这样的事,但让我们假设你做了。下面是你应该怎么做。
lovablePets.addAll(Collections.nCopies(69, "fruit bat"));
通过使用同时接受索引和Collection
的addAll
形式,您可以将新元素添加到List
的中间而不是末尾。
不可变单例 Set
有时您需要一个不可变的单例 Set(singleton Set
),它由单个指定元素组成。Collections.singleton方法返回这样一个Set
。此实现的一个用途是从Collection
中删除所有出现的指定元素。
c.removeAll(Collections.singleton(e));
一个相关的习惯用法是从Map
中删除映射到指定值的所有元素。例如,假设您有一个Map - job,它将人们映射到他们的工作领域,并且假设您想要消除所有的律师。下面的一行代码将完成这个任务。
job.values().removeAll(Collections.singleton(LAWYER));
此实现的另一个用途是向编写为接受值集合的方法提供单个输入值。
空Set、List和Map 常量
Collections类提供了返回空Set、List和Map的方法——emptySet、emptyList和emptyMap。这些常量的主要用途是作为方法的输入,当您根本不想提供任何值时,这些方法接受值的Collection ,如本例所示。
tourist.declarePurchases(Collections.emptySet());
相关文章:

Java 进阶 -- 集合(三)
4、实现 实现是用于存储集合的数据对象,它实现了接口部分中描述的接口。本课描述了以下类型的实现: 通用实现是最常用的实现,是为日常使用而设计的。它们在标题为“通用实现”的表格中进行了总结。特殊目的实现是为在特殊情况下使用而设计的࿰…...
【华为OD机试真题 C语言】5、TLV解析 | 机试真题+思路参考+代码解析
文章目录 一、题目🎃题目描述🎃输入输出🎃样例1 二、思路参考三、代码参考🏆C语言 作者:KJ.JK 🍂个人博客首页: KJ.JK 🍂专栏介绍: 华为OD机试真题汇总,定期…...

(七)CSharp-刘铁锰版-事件
一、初步了解事件 定义:单词 Event ,译为“事件” 《牛津词典》中的解释是“a thing that happens,especially something important”通顺的解释就是“能够发生的什么事情” 角色: 使对象或类具备通知能力的成员 (中译&#x…...
【ROS】郭老二博文之:ROS目录
1、ROS2 【ROS】Ubuntu22.04安装ROS2(Humble Hawksbill) 【ROS】ROS2命令行工具详解 【ROS】ROS2中的概念和名词解释 【ROS】ROS2编程示例:话题订阅-发布-C版 【ROS】ROS2编程示例:服务和客户端-C版 【ROS】ROS2编程示例…...

Android应用程序进程的启动过程
Android应用程序进程的启动过程 导语 到这篇文章为止,我们已经简要地了解过了Android系统的启动流程了,其中比较重要的内容有Zygote进程的启动和SystemService以及Launcher的启动,接下来我们将要学习的是Android应用程序的启动过程ÿ…...

【2】Midjourney注册
随着AI技术的问世,2023年可以说是AI爆炸性成长的一年,近期最广为人知的AI服务除了chatgpt外,就是从去年五月就已经问世的AI绘画工具mid journey了。 ▲几个AI工具也代表了人工智能的热门阶段 只要输入一段文字,AI就会根据语意计算…...

第六十八天学习记录:高等数学:导数(宋浩板书)
导数是微积分中的一个概念,描述了函数在某一个点上的变化率。具体地说,函数 f ( x ) f(x) f(x)在 x a xa xa处的导数为 f ′ ( a ) f(a) f′(a),表示当 x x x在 a a a处发生微小的变化 Δ x \Delta x Δx时, f ( x ) f(x) f(x)对…...

unreal 5 实现角色拾取功能
要实现角色拾取功能,我们需要实现蓝图接口功能,蓝图接口主要提供的是蓝图和蓝图之间可以通信,接下来,跟着教程,实现一下角色的拾取功能。 首先,我们要实现一个就是可视区的物品在朝向它的时候,会…...

chatgpt赋能python:如何使用Python升序排列一个列表?
如何使用Python升序排列一个列表? 在Python编程中,我们经常需要对列表进行排序。列表排序是一种常见的操作,可以帮助我们对数据进行分析和管理。在这篇文章中,我们将学习如何使用Python对一个列表进行升序排列。 什么是升序排列…...

Lecture 20 Topic Modelling
目录 Topic ModellingA Brief History of Topic ModelsLDAEvaluationConclusion Topic Modelling makeingsense of text English Wikipedia: 6M articlesTwitter: 500M tweets per dayNew York Times: 15M articlesarXiv: 1M articlesWhat can we do if we want to learn somet…...

ThreadPoolExecutor线程池
文章目录 一、ThreadPool线程池状态二、ThreadPoolExecutor构造方法三、Executors3.1 固定大小线程池3.2 带缓冲线程池3.3 单线程线程池 四、ThreadPoolExecutor4.1 execute(Runnable task)方法使用4.2 submit()方法4.3 invokeAll()4.4 invokeAny()4.5 shutdown()4.6 shutdownN…...

chatgpt赋能python:Python实践:如何升级pip
Python实践:如何升级pip Python作为一门高效的脚本语言,被广泛应用于数据分析、人工智能、Web开发等领域。而pip则是Python的包管理工具,是开发Python应用的必备工具。但是pip在使用过程中,有时候会出现版本不兼容或者出现漏洞等…...

【JavaEE进阶】mybatis
目录: 一、Mybatis是什么 三个映射关系如下图: 二、mybatis的使用(前置工作简单案例) 第一步:导入MAVEN依赖 第二步: 在spring项目当中新建数据源 第三步:新建一个实体类,是和…...

Redis的大key
什么是 redis 的大 key redis 的大 key 不是指存储在 redis 中的某个 key 的大小超过一定的阈值,而是该 key 所对应的 value 过大对于 string 类型来说,一般情况下超过 10KB 则认为是大 key;对于set、zset、hash 等类型来说,一般…...

MMPretrain
title: mmpretrain实战 date: 2023-06-07 16:04:01 tags: [image classification,mmlab] mmpretrain实战 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ccTl9bOl-1686129437336)(null)] 主要讲解了安装,还有使用教程.安装教程直接参考官网.下面讲…...

栈和队列(数据结构刷题)[一]-python
文章目录 前言一、原理介绍二、用栈实现队列1.操作2.思路 三、关于面试考察栈里面的元素在内存中是连续分布的么? 前言 提到栈和队列,大家可能对它们的了解只停留在表面,再深入一点,好像知道又好像不知道的感觉。本文我将从底层实…...

【备战秋招】JAVA集合
集合 前言 一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象 的操作,就要 对对象进行存储。 另一方面,使用Array存储对象方面具有一些弊端,而Java 集合就像一种容器,可以动态地把多…...

setState详解
this. setState( [partialState], [callback]) 1.[partialState] :支持部分状态更改 this, setState({ x:100 //不论总共有多少状态,我们只修改了x,其余的状态不动 });callback :在状态更改/视图更新完毕后触发执行,也可以说只要执行了setS…...

Qt5.12.6配置Android Arm开发环境(windows)
1. 安装jdk1.8 2.安装Android Studio 并安装 SDK 与NDK SDK Tools 选择 26.0.3 SDK Platform 选择 Android SDK Platform 26 NDK选择19版本 安卓ARM环境配置成功如下: JDK1.8 , SDK 26 , NDK 19 在安装QT时要选择 ARMv7(32位CPU)与ARM64-v8a(64位CPU) 选择支持android平台…...

七、进程程序替换
文章目录 一、进程程序替换(一)概念(二)为什么程序替换(三)程序替换的原理(四)如何进行程序替换1. execl2. 引入进程创建——子进程执行程序替换,会不会影响父进程呢? &…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...

【若依】框架项目部署笔记
参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作: 压缩包下载:http://download.redis.io/releases 1. 上传压缩包,并进入压缩包所在目录,解压到目标…...

C# WPF 左右布局实现学习笔记(1)
开发流程视频: https://www.youtube.com/watch?vCkHyDYeImjY&ab_channelC%23DesignPro Git源码: GitHub - CSharpDesignPro/Page-Navigation-using-MVVM: WPF - Page Navigation using MVVM 1. 新建工程 新建WPF应用(.NET Framework) 2.…...

LINUX编译vlc
下载 VideoLAN / VLC GitLab 选择最新的发布版本 准备 sudo apt install -y xcb bison sudo apt install -y autopoint sudo apt install -y autoconf automake libtool编译ffmpeg LINUX FFMPEG编译汇总(最简化)_底部的附件列表中】: ffmpeg - lzip…...

NineData数据库DevOps功能全面支持百度智能云向量数据库 VectorDB,助力企业 AI 应用高效落地
NineData 的数据库 DevOps 解决方案已完成对百度智能云向量数据库 VectorDB 的全链路适配,成为国内首批提供 VectorDB 原生操作能力的服务商。此次合作聚焦 AI 开发核心场景,通过标准化 SQL 工作台与细粒度权限管控两大能力,助力企业安全高效…...