Java 集合框架与 Stream 流深入剖析(重点详细讲解)

目录

引言

一、ArrayList

1. 概述

2. 特点

动态扩容

初始容量

扩容倍数

随机访问高效

插入和删除效率低

3. 代码示例

4. 分析

二、HashSet

1. 概述

2. 特点

唯一性

插入、删除和查找效率高

无序性

3. 代码示例

4. 分析

三、HashMap

1. 概述

2. 特点

键唯一性

插入、删除和查找效率高

无序性

3. 代码示例

4. 分析

四、Java Stream 流

1. 概述

2. 特点

声明式编程

惰性求值

并行处理

3. 代码示例

4. 分析

五、总结


引言

        在 Java 编程领域里,高效地存储和处理数据是一项关键任务。集合框架就像是一个功能强大的工具箱,为开发者提供了各种数据结构来满足不同的需求。而 Java 8 引入的 Stream 流,则如同给这个工具箱增添了一把锋利的瑞士军刀,让集合操作变得更加高效和便捷。本文将深入且详细地介绍 Java 中常用的集合类 ArrayListHashSetHashMap 以及强大的 Stream 流。

一、ArrayList

1. 概述

       ArrayList 是 Java 集合框架中 List 接口的一个重要实现类。它本质上是一个动态数组,继承自 AbstractList 类并实现了 List 接口。这意味着 ArrayList 可以存储重复的元素,并且元素是按照插入的顺序进行存储的。打个比方,ArrayList 就像是一个可以自动变长的书架,你可以按照顺序一本一本地往书架上放书,而且同一本书可以放多本。

2. 特点

动态扩容

        当我们使用数组时,需要提前确定数组的大小。但在实际开发中,数据的数量往往是不确定的。ArrayList 就很好地解决了这个问题,它具有动态扩容的特性。当 ArrayList 中的元素数量超过其当前容量时,它会自动进行扩容,就像书架的格子不够用了,它会自动增加格子的数量,这样我们就可以继续往里面放书,而无需手动去管理数组的大小,使用起来非常方便。

初始容量

        在 Java 中,ArrayList 有多种构造方法。当我们使用无参构造方法创建 ArrayList 对象时,其初始容量并不是立即分配为某个固定值。实际上,在 Java 7 及以后的版本中,使用无参构造方法创建 ArrayList 时,初始容量为 0。当第一次向 ArrayList 中添加元素时,它会将容量初始化为 10。例如:

import java.util.ArrayList;
import java.util.List;public class ArrayListInitialCapacityExample {public static void main(String[] args) {List<String> list = new ArrayList<>();// 此时 list 的容量为 0System.out.println("添加元素前的容量猜测:" + getCapacity(list)); list.add("element");// 第一次添加元素后,容量变为 10System.out.println("添加元素后的容量猜测:" + getCapacity(list)); }// 此方法通过反射获取 ArrayList 的容量private static int getCapacity(List<?> list) {try {java.lang.reflect.Field field = ArrayList.class.getDeclaredField("elementData");field.setAccessible(true);return ((Object[]) field.get(list)).length;} catch (Exception e) {return -1;}}
}

        上述代码通过反射机制尝试获取 ArrayList 的实际容量。可以看到,在使用无参构造方法创建 ArrayList 后,在添加元素之前容量为 0,添加第一个元素后,容量变为 10。

如果我们使用带有初始容量参数的构造方法来创建 ArrayList,则可以指定其初始容量。例如:

List<String> list = new ArrayList<>(20);

这里创建的 ArrayList 初始容量就是 20。

扩容倍数

        当 ArrayList 中的元素数量达到其当前容量时,就需要进行扩容操作。ArrayList 的扩容机制是将原容量扩大为原来的 1.5 倍。具体来说,在 ArrayList 的源码中,扩容操作主要通过 grow 方法实现,其核心代码如下:

private void grow(int minCapacity) {// 获取旧容量int oldCapacity = elementData.length;// 计算新容量,新容量为旧容量的 1.5 倍int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 调用 Arrays.copyOf 方法将原数组元素复制到新数组elementData = Arrays.copyOf(elementData, newCapacity);
}

        在上述代码中,oldCapacity >> 1 表示将旧容量右移一位,相当于将旧容量除以 2。所以 newCapacity = oldCapacity + (oldCapacity >> 1) 实际上就是将原容量扩大为原来的 1.5 倍。例如,如果原容量为 10,当需要扩容时,新容量将变为 10 + 10 / 2 = 15。

随机访问高效

        由于 ArrayList 是基于数组实现的,它在内存中是连续存储元素的。这就好比书架上的书是一本挨着一本整齐排列的,我们可以根据书的位置(索引)快速地找到某一本书。在 ArrayList 中,通过索引访问元素的时间复杂度为 O(1),也就是说无论 ArrayList 中有多少个元素,我们都能在几乎相同的时间内找到指定索引位置的元素。

插入和删除效率低

        虽然 ArrayList 在随机访问方面表现出色,但在插入和删除元素时,尤其是在中间或开头的位置,效率就会比较低。这是因为当我们在 ArrayList 中间或开头插入或删除元素时,需要移动大量的元素。就像在书架中间插入一本书&#x

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Golang的Goroutine(协程)与runtime

目录 Runtime 包概述 Runtime 包常用函数 1. GOMAXPROCS 2. Caller 和 Callers 3. BlockProfile 和 Stack 理解Golang的Goroutine Goroutine的基本概念 特点&#xff1a; Goroutine的创建与启动 示例代码 解释 Goroutine的调度 Gosched的作用 示例代码 输出 解…

【51单片机】3-3【定时器/计数器/中断】超声波测距模块测距

1.硬件 51最小系统超声波测距模块 2.软件 #include "reg52.h"//距离小于10cm,D5亮&#xff0c;D6灭&#xff0c;反之相反现象sbit D5 P3^7;//根据原理图&#xff08;电路图&#xff09;&#xff0c;设备变量led1指向P3组IO口的第7口 sbit D6 P3^6;//根据原理图&…

2-Visual Studio 2022 NET开发Windows桌面软件并连接SQL Server数据库

引言 今天尝试Visual Studio 2022 NET开发一个NET桌面软件&#xff0c;并尝试连接SQL Server的数据库&#xff0c;此文章为开发笔记。 --------------------------------------------------------------------------------------------------------------------------------- …

Kafka 漏消费和重复消费问题

Kafka 虽然是一个高可靠、高吞吐的消息系统&#xff0c;但如果使用不当&#xff0c;**“漏消费”和“重复消费”**问题是非常容易发生的&#xff0c;尤其在业务系统中会造成数据丢失、重复写库等严重问题。 &#x1f3af; 一句话理解&#xff1a; Kafka 本身提供 “至多一次”…

【大模型深度学习】如何估算大模型需要的显存

一、模型参数量 参数量的单位 参数量指的是模型中所有权重和偏置的数量总和。在大模型中&#xff0c;参数量的单位通常以“百万”&#xff08;M&#xff09;或“亿”&#xff08;B&#xff0c;也常说十亿&#xff09;来表示。 百万&#xff08;M&#xff09;&#xff1a;表示…

LeetCode算法题(Go语言实现)_32

题目 在一个大小为 n 且 n 为 偶数 的链表中&#xff0c;对于 0 < i < (n / 2) - 1 的 i &#xff0c;第 i 个节点&#xff08;下标从 0 开始&#xff09;的孪生节点为第 (n-1-i) 个节点 。 比方说&#xff0c;n 4 那么节点 0 是节点 3 的孪生节点&#xff0c;节点 1 是…

Linux makefile的一些语法

一、定义变量 1. 变量的基本语法 在 makefile 中&#xff0c;变量的定义和使用非常类似于编程语言中的变量。变量的定义格式&#xff08;最好不要写空格&#xff09;如下&#xff1a; VARIABLE_NAMEvalue 或者 VARIABLE_NAME:value 表示延迟赋值&#xff0c;变量的值在引…

CAN/FD CAN总线配置 最新详解 包含理论+实战(附带源码)

看前须知&#xff1a;本篇文章不会说太多理论性的内容&#xff08;重点在理论结合实践&#xff09;&#xff0c;顾及实操&#xff0c;应用&#xff0c;一切理论内容支撑都是为了后续实际操作进行铺垫&#xff0c;重点在于读者可以看完文章应用。&#xff08;也为节约读者时间&a…

木马学习记录

一句话木马是什么 一句话木马就是仅需要一行代码的木马&#xff0c;很简短且简单&#xff0c;木马的函数将会执行我们发送的命令 如何发送命令&#xff06;发送的命令如何执行? 有三种方式&#xff1a;GET&#xff0c;POST&#xff0c;COOKIE&#xff0c;一句话木马中用$_G…

Linux系统调用编程

一、进程和线程的概念 1.进程 进程是指一个具有独立功能的程序在某个数据集上的一次动态执行过程,它是系统进行资源分配和调度的最小单元。 定义&#xff1a;进程是程序的一次执行实例&#xff0c;拥有独立的地址空间、资源&#xff08;如内存、文件描述符等&#xff09;和系…

PostgreSQL的扩展(extensions)-常用的扩展-pg_dirtyread

PostgreSQL的扩展&#xff08;extensions&#xff09;-常用的扩展-pg_dirtyread pg_dirtyread 是 PostgreSQL 的一个特殊扩展&#xff0c;它允许读取已被删除但尚未被 VACUUM 清理的数据行&#xff0c;是数据恢复的重要工具。 原理&#xff1a; pg_dirtyread 通过直接访问表的…

花卉识别分类系统,Python/resnet18/pytorch

花卉识别分类系统,Python/resnet18/pytorch 基于pytorch训练, resnet18网络&#xff0c;可用于训练其他分类问题&#xff0c;也可自己重新训练 共五种花卉&#xff1a;雏菊&#xff0c;蒲公英&#xff0c;玫瑰&#xff0c;向日葵&#xff0c;郁金香 标价包含GUI源码、数据集…