JavaSE知识点整理---集合篇

news2025/4/7 15:13:55

在这里插入图片描述

文章目录

      • 1、数组与集合的区别?
      • 2、List、Set、Map的区别?
      • 3、ArrayList、LinkedList、Vector的区别?
      • 4、Java集合的快速失败机制【fail-fast】
      • 5、List接口常用方法
      • 6、List的三种遍历方式

1、数组与集合的区别?


在Java中,数组和集合都是容器,都可以用来存储多个数据,它们在使用的时候有一些区别:

1)类型:在Java中数组是最基本的数据结构,在内存中是线性存储的,可以用来存储基本数据类型(如int,double等)和引用类型(如对象)。集合(Collection),在Java中是一种接口,它是一种存储对象的容器,只能存储引用类型的数据。

// 数组
int[] arr1 = new int[5]; //int型数组
String[] arr2 = new String[5]; //对象数组

// 集合对象
List<String> list = ArrayList<>(); //存储Stirng类型数据,如果不指定泛型,它默认类型为object

2)长度:数组在初始化时需要指定长度(大小),一旦定义,其长度无法改变。而集合的长度是可变的,提供了add()、remove()等方法操作数据;(动态扩容、缩容)

int[] arr = new int[5];  //数组长度为5

3)操作数据:数组可以通过索引直接访问和修改元素,具有较高的访问效率,时间复杂度O(1)。而集合类通常提供了一系列方法来操作元素,例如添加、删除、查找等,对元素的访问需要通过方法调用。

// 1.数组通过索引方式操作数据
int[] arr = new int[5]; //int类似数组,默认值为0
// 添加数据
arr[1]=18;
// 覆盖数据
arr[1]=20;
// 访问数据
System.out.println(arr[1]);//20
// 获取数组长度
System.out.println(arr.length);
  
// 2.集合通过方法调用方式操作数据
List<String> list = ArrayList<>();
// 添加数据
list.add("张三");
list.add("李四");
list.add("王五");
// 删除数据
list.remove(2);
// 获取数据
System.out.println(list.get(1));//李四
// 获取集合中元素的个数
System.out.println(list.size());//2	

4)内存分配方式:数组在内存中是连续存储的,占用的内存空间是固定的。而集合类中的元素可以在内存中不连续存储,可以根据需要动态分配内存空间。

5)功能扩展性:集合类提供了丰富的方法和功能,例如排序、查找、遍历等,可以方便地进行各种操作。而数组的功能相对较少,需要自己编写代码实现相应的功能。

6)泛型:集合支持泛型,可以指定存储的元素类型,提供了更好的类型安全性。(避免类型转换问题)


小结:

  • 数组和集合都是容器,可以存储多个数据。
  • 数组的长度是固定的,一旦创建,无法改变。
  • 集合的长度是可变的,可以动态添加或删除元素。
  • 数组可以存储基本类型数据和引用类型数据,而集合只能存储引用类型数据。如果要存储基本类型数据,需要使用对应的包装类。
  • 数组可以通过索引方式直接访问元素,而集合通常通过方法调用进行元素的操作和访问。

总的来说,数组适用于长度固定且元素类型简单的情况,而集合适用于长度可变且元素类型复杂的情况。

单列集合:

Snipaste_2023-08-07_10-47-20

双列集合:

Snipaste_2023-08-07_11-46-15


2、List、Set、Map的区别?


List、Set和Map是Java中常用的集合接口,它们有以下区别:

  • List是一个有序的集合,可以包含重复元素。它允许按照插入顺序访问集合中的元素,并且可以根据索引位置进行操作。常见的实现类有ArrayList、LinkedList 和 Vector。

  • Set是一个不允许包含重复元素的集合。它不保证元素的顺序,即不按照插入顺序存储元素。常见的实现类有HashSet、TreeSet 和LinkedHashSet。

    • HashSet:基于哈希表实现,不保证元素的顺序。
    • TreeSet:基于红黑树实现,按照元素的自然顺序或指定的比较器进行排序。
    • LinkedHashSet:基于哈希表和链表实现,保持元素的插入顺序。
  • Map是一种键值对的集合,每个键都是唯一的。它允许通过键来访问和操作对应的值。常见的实现类有HashMap、TreeMap和LinkedHashMap、ConcurrentHashMap。

    • HashMap:基于哈希表实现,不保证键值对的顺序。

    • TreeMap:基于红黑树实现,按照键的自然顺序或指定的比较器进行排序。

    • LinkedHashMap:基于哈希表和双向链表实现,保持键值对的插入顺序。

    • ConcurrentHashMap:线程安全的Map。

ConcurrentHashMap:

ConcurrentHashMap位于juc包,它是哈希表的线程安全版本,并且对HashMap进行改进。相比于HashMap,在多线程环境下,ConcurrentHashMap提供了更好的并发性能和线程安全性,主要通过以下几个方面来实现:

  • 分段锁:ConcurrentHashMap将整个哈希表分成多个段(Segment),每个段维护着一个独立的哈希表。不同的线程可以同时访问不同的段,从而提高并发性能。每个段都有自己的锁,只有在修改该段的时候才需要加锁,这样可以减小锁的粒度,提高并发性能。
  • CAS操作:ConcurrentHashMap使用了CAS(Compare and Swap)操作来保证线程安全。CAS是一种无锁的原子操作,它可以在不使用锁的情况下实现对共享变量的原子操作。
  • volatile修饰符:ConcurrentHashMap使用volatile修饰符来保证可见性。volatile修饰的变量对所有线程可见,当一个线程修改了volatile变量的值,其他线程可以立即看到最新的值。

ConcurrentHashMap的使用方式与HashMap类似,可以通过put、get、remove等方法来操作元素。但需要注意的是,虽然ConcurrentHashMap是线程安全的,但在某些情况下,仍然需要额外的同步措施来保证一致性。


3、ArrayList、LinkedList、Vector的区别?


1)数据结构层面:

  • ArrayList底层基于动态数组实现的,适合随机访问元素。
  • LinkedList底层基于双向链表实现,适合数据的频繁插入和删除操作。
  • Vector和ArrayList类似,底层也是基于动态数组实现的,Vector大部分方法都是线程同步的。(JDK1.0就诞生了)

image-20230807164540707

2)线程安全层面:

  • ArrayList和LinkedList在多线程场景下都是不安全的(不提供内置的同步机制),需要外部同步(同步方法或者锁)。

    • 如何保证ArrayList的线程安全:https://blog.csdn.net/qq_44544908/article/details/129020075
  • Vector是线程安全的,因为它的大部分方法都是同步的,但在性能上不如ArrayList和LinkedList。

3)性能层面:

  • ArrayList在随机访问和尾部添加元素方面性能好,因为它底层基于动态数组实现,可以通过索引直接访问元素,随机访问元素的时间复杂度是O(1),尾部添加元素的时间复杂度也是O(1)。而ArrayList在列表的中间或头部插入和删除元素时,需要进行移位操作,所以性能较差,时间复杂度是O(n),n是列表的长度。

  • LinkedList在插入或删除元素时性能好(双向链表),在随机访问元素时性能较差。

  • Vector由于同步处理,性能较差,不推荐在非线程安全环境中使用。


示例:ArrayList、LinkedList、Vector的使用

package cn.z3inc.list;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 测试ArryList、LinkedList、Vector的使用
 *
 * @author 白豆五
 * @version 2023/8/7
 * @since JDK8
 */
public class ListDemo1 {
    public static void main(String[] args) {
        // ArrayList
        List<String> arrayList = new ArrayList<>();
        Collections.addAll(arrayList, "a", "b", "c", "d", "e", "f", "g"); // 批量添加
        System.out.println("arrayList = " + arrayList); // arrayList = [a, b, c, d, e, f, g]
        arrayList.remove(0);
        System.out.println("arrayList = " + arrayList); // arrayList = [b, c, d, e, f, g]

        // LinkedList
        List<String> linkedList = new ArrayList<>();
        Collections.addAll(linkedList, "a", "b", "c", "d", "e", "f", "g"); // 批量添加
        System.out.println("linkedList = " + linkedList); // linkedList = [a, b, c, d, e, f, g]
        linkedList.remove(0);
        System.out.println("linkedList = " + arrayList); // arrayList = [b, c, d, e, f, g]

        // Vector
        List<String> vector = new Vector<>();
        Collections.addAll(vector, "a", "b", "c", "d", "e", "f", "g"); // 批量添加
        System.out.println("vector = " + vector); // vector = [a, b, c, d, e, f, g]
        vector.remove(0);
        System.out.println("vector = " + vector); // vector = [b, c, d, e, f, g]
    }
}

运行结果:

image-20230807173955141

小节:ArrayList底层基于动态数组实现,适用于随机访问遍历元素和尾部添加元素,但在中间插入和删除时性能较差;LinkedList底层基于双向链表实现,适用于插入和删除操作,但随机访问的性能较差;Vector与ArrayList类似但线程安全,性能较差,一般在多线程环境下使用。


4、Java集合的快速失败机制【fail-fast】


1)概念:

  • “fail-fast” 是Java集合类的一种错误检测机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如 某一个线程通过迭代器遍历集合时,如果其他线程对集合进行结构上的修改(添加、删除元素),则会抛出ConcurrentModificationException并发修改异常。

  • “fail-fast” 机制可以及时发现问题,避免后续操作基于错误的数据继续进行,但它并不能保证线程安全。因为它并不能阻止其他线程修改集合,只能在访问时进行检测。

2)原理:

  • “fail-fast” 机制的实现是通过记录集合在结构上被修改的次数来实现的。每个集合中都有一个modCount 字段,用于记录集合的修改次数。每当对集合进行结构上的修改时,modCount 就会加1。而在进行迭代时,迭代器会存储一个 expectedModCount 值,用于记录迭代器对集合进行修改的次数。

  • 在迭代过程中,每次调用 hasNext()next() 方法时,会通过比较迭代器的expectedModCount和集合的modCount值来判断集合是否被修改过,如果两者不相等,则说明在迭代过程中有其他线程修改了集合,会立即抛出 ConcurrentModificationException异常。

image-20230807181905133

image-20230807183149494


示例:测试并发修改异常

package cn.z3inc.list;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * 测试并发修改异常
 *
 * @author 白豆五
 * @version 2023/8/7
 * @since JDK8
 */
public class ListDemo2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "a", "b", "c", "d", "e");
        // 获取list的迭代器
        Iterator<String> it = list.iterator();

        // 遍历
        //it.hasNext()判断是否有下一个元素
        //it.next()获取元素并将指针向后移动一位
        while (it.hasNext()) {
            System.out.println(it.next());
            list.add("f");
        }
    }
}

运行结果:

image-20230807183941683

5、List接口常用方法


添加元素:

  • boolean add(E e):向列表尾部添加一个元素。
  • add(int index, E element):向列表的指定位置处插入一个元素。
  • addAll(int index,Collection<? extends E> c):把另一个集合中的所有元素按照指定位置插入到当前列表中。

获取元素:

  • E get(int index):获取列表中指定索引处的元素。

  • int indexOf(Object o):获取列表中指定元素第一次出现的索引。

  • int lastIndexOf(Object o):获取列表中指定元素最后一次出现的索引。

删除元素:

  • E remove(int index):从列表中删除指定索引处的元素,返回值是被删除的元素。
  • boolean remove(Object obj):从列表中删除指定的元素(只删除列表中首次出现的元素)。

修改元素:

  • E set(int index,E element):修改列表中指定索引处的元素,返回值是被修改的元素。

其他方法:

  • int size():返回列表中的元素个数。
  • boolean isEmpty():用于判断列表是否为空。
  • boolean contains(Object o):用于判断列表是否包含指定的元素。
  • void clear():清除列表中的所有元素。
  • Object[] toArray():把列表转成数组
  • Iterator<E> iterator():获取用于遍历列表的迭代器。

6、List的三种遍历方式


Collection集合支持两种遍历方式:

  • Iterator迭代器。
  • 增强for循环。

List接口继承了Collection接口,所以以上两种遍历方式也适用于List,除次以外,List集合还支持for循环+get(索引)方式遍历。

  • Iterator迭代器。
  • 增强for循环。
  • for循环+get(索引)。

方式一: 使用迭代器(Iterator)进行遍历

迭代器是一种设计模式,它提供了一种统一的方式来遍历集合中的元素,而无需暴露底层数据结构的细节。通过使用迭代器,我们可以按顺序访问集合中的每个元素,迭代器模式封装了集合的内部结构,使得遍历过程更加简单、安全和灵活。

迭代器模式在Java中被广泛应用,例如在集合类(如List、Set)和Map类中都提供了迭代器来遍历元素。同时,我们也可以自定义实现迭代器接口来支持自定义的数据结构的遍历。

示例:

package cn.z3inc.list;

import org.junit.Test;

import java.util.*;

/**
 * List集合3种遍历方式
 *
 * @author 白豆五
 * @version 2023/8/7
 * @since JDK8
 */
public class ListNBForDemo1 {
    @Test
    public void testIterator() {
        // 1.创建list集合
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "a", "b", "c");
        
        // 2.获取list集合的迭代器对象
        Iterator<String> it = list.iterator();

        // 3.遍历
        // hasNext() 判断是否有下一个元素
        // next() 获取下一个元素
        while (it.hasNext()) {
            String item = it.next();
            System.out.println(item);
        }
    }
}

运行结果:

image-20230807222730158

  • list.iterator():获取list集合的迭代器对象
  • hasNext(): 判断是否有下一个元素
  • next(): 获取下一个元素

方式二: 使用增强For进行遍历(foreach)

使用foreach遍历数组的底层实现是普通的for循环,而使用foreach遍历集合的底层实现是迭代器。

示例:

public class ListNBForDemo2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "a", "b", "c");

        // 使用增强for遍历list集合
        for (String item : list) {
            System.out.println(item);
        }
        
		// 简写
        //list.forEach(item -> System.out.println(item));
        //list.forEach(System.out::println);
    }
}

运行结果:

image-20230807223845773

通过编译查看源码:javac -encoding utf-8 xxx.java

image-20230807224120183

image-20230807224305730

image-20230807224342613


方式三:for循环+get(索引)方式遍历

public class ListNBForDemo2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "a", "b", "c");

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

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

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

相关文章

如何使用 reqwest 包

GET 请求 向连接发起一个 GET 请求&#xff1a;https://hacker-news.firebaseio.com/v0/topstories.json&#xff0c;并解析返回的内容。 尝试发起请求 因为是 GET 请求&#xff0c;可以先在浏览器中进行查看&#xff0c;浏览器可以正常显示一个 id 列表&#xff0c;如下所示…

24届华东理工大学近5年自动化考研院校分析

今天给大家带来的是华东理工大学控制考研分析 满满干货&#xff5e;还不快快点赞收藏 一、华东理工大学 学校简介 华东理工大学原名华东化工学院&#xff0c;1956年被定为全国首批招收研究生的学校之一&#xff0c;1960年起被中共中央确定为教育部直属的全国重点大学&#…

【Winform学习笔记(七)】Winform无边框窗体拖动功能

Winform无边框窗体拖动功能 前言正文1、设置无边框模式2、无边框窗体拖动方法1、通过Panel控件实现窗体移动2、通过窗体事件实现窗体移动3、调用系统API实现窗体移动4、重写WndProc()实现窗体移动 前言 在本文中主要介绍 如何将窗体设置成无边框模式、以及实现无边框窗体拖动功…

Java中的enum枚举类型

知识介绍 在Java中&#xff0c;enum&#xff08;枚举&#xff09;是一种特殊的数据类型&#xff0c;用于定义一组命名的常量。枚举类型允许你在代码中创建一个固定的、预定义的值集合&#xff0c;这些值可以在整个程序中使用。使用枚举可以使代码更加清晰、可读&#xff0c;并…

Bootload U-Boot分析

Bootloader是在操作系统运行之前执行的一段小程序。通过这段小程序可以初始化硬件设备、建立内存空间的映射表&#xff0c;从而建立适当的系统软硬件环境&#xff0c;为最终调用操作系统内核做好准备。 对于嵌入式系统&#xff0c;Bootloader是基于特定硬件平台来实现的。因此…

【Hystrix技术指南】(5)Command创建和执行实现

创建流程 构建HystrixCommand或者HystrixObservableCommand对象 *使用Hystrix的第一步是创建一个HystrixCommand或者HystrixObservableCommand对象来表示你需要发给依赖服务的请求。 若只期望依赖服务每次返回单一的回应&#xff0c;按如下方式构造一个HystrixCommand即可&a…

FreeRTOS(vTaskList与vTaskGetRunTimeStats)

目录 1、Cube配置 ①配置SYS ②配置TIM3 ③配置USART2 ④配置FreeRTOS ⑤配置中断优先级 2、代码添加改动 ①在main函数合适位置开启TIM3中断 ②修改HAL_TIM_PeriodElapsedCallback函数 ③完善两个相关函数 ④vTaskList与vTaskGetRunTimeStats的使用 vTaskList&#xff…

C# 有效的字母异位词

242 有效的字母异位词 给定两个字符串 和 &#xff0c;编写一个函数来判断 是否是 的字母异位词。stts 注意&#xff1a;若 和 中每个字符出现的次数都相同&#xff0c;则称 和 互为字母异位词。stst 示例 1: 输入: s “anagram”, t “nagaram” 输出: true 示例 2: 输…

探索国产嵌入式Python解决方案的方法(开源)

大家好&#xff0c;今天我们要介绍一款适用于单片机的嵌入式Python开源项目 -- PikaPython。 第一&#xff1a;嵌入式Python的发展趋势 在嵌入式领域软硬件的发展趋势中&#xff0c;硬件的成本日益降低&#xff0c;性能逐渐提升。这种趋势使得Python在芯片上的运行难度已经大大…

学python的心得体会1000字,学python的心得体会2000字

这篇文章主要介绍了学python的心得体会2000字&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 1. 初学者应该从简单的练习开始&#xff0c;先掌握基本的语法和概念&#xff0c;…

Three.js光源

目录 Three.js入门 Three.js光源 本文我们将研究three.js中的灯光类型和JavaScript中的光源&#xff0c;探索不同的光源设置。我们的目标是全面理解光源设置和类型&#xff0c;比如环境光、半球光、矩形光、方向光、点光源和聚光灯。我们将逐个介绍它们的属性参数和使用方法…

深度相机常见技术:深度相机的相位求解

1.1 深度相机概述 深度相机是近几年兴起的新技术&#xff0c;相比较传统的相机&#xff0c;深度相机在功能上添加了一个深度测量&#xff0c;从而更方便准确的感知周围的环境及变化。深度相机有很多的应用场景&#xff0c;在我们日常生活中有很多深度相机的影子。那它可以用来…

在线对对联

对对联的起源可以追溯到中国古代,它与中国文化有着密切的关系。 1. 最早的对对联出现在汉朝,当时称为“对句”。它起源于民间,后来逐渐成为文人雅士的精神寄托。 2. 唐代时,对对联的格式更加规范,并被称为“春联”。它成为春节张贴的主要内容,寓意吉祥。 3. 宋代以后,对对联…

小程序商品如何开启返佣

​越来越多的商家开始意识到小程序平台的潜力和价值。开启分销返佣机制是一种有效的方式&#xff0c;可以吸引更多的用户和合作伙伴&#xff0c;提高销售额和品牌知名度。下面将介绍商城小程序商品如何开启返佣。 1. 开启系统分销设置。在管理员后台->营销设置 点击分销功能…

MYSQL --Austindatabases 历年文章合集

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

高速过孔同进同出后续来了!影响大不大由你们自己说

高速先生成员---黄刚 话说Chris在上篇文章的结尾留下的悬念&#xff0c;其实在上周的答题里&#xff0c;也有不少粉丝猜到了接下来要验证的内容。我们知道&#xff0c;任何两个结构如果距离变近了&#xff0c;容性就会增加&#xff0c;无论是孔和孔&#xff0c;线和线&#xf…

JMeter命令行执行+生成HTML报告

1、为什么用命令行模式 使用GUI方式启动jmeter&#xff0c;运行线程较多的测试时&#xff0c;会造成内存和CPU的大量消耗&#xff0c;导致客户机卡死&#xff1b; 所以一般采用的方式是在GUI模式下调整测试脚本&#xff0c;再用命令行模式执行&#xff1b; 命令行方式支持在…

MySQL(1)

MySQL创建数据库和创建数据表 创建数据库 1. 连接 MySQL mysql -u root -p 2. 查看当前的数据库 show databases; 3. 创建数据库 create database 数据库名; 创建数据库 4. 创建数据库时设置字符编码 create database 数据库名 character set utf8; 5. 查看和显示…

redis入门2-命令

Redis的基本数据类型 redis的基本数据类型&#xff08;value&#xff09;: string,普通字符串 hash&#xff08;哈希&#xff09;,适合存储对象 list(列表),按照插入顺序排序&#xff0c;可以由重复的元素 set(无序集合)&#xff0c;没有重复的元素 sorted set(有序集合)&…

Spring Boot + Vue3前后端分离实战wiki知识库系统十一--文档管理功能开发三

文档内容的显示&#xff1a; 在上一次https://www.cnblogs.com/webor2006/p/17510360.html文档管理模块还差文档的显示木有完成&#xff0c;所以接下来先将这块模块给收尾了。 增加单独获取内容的接口&#xff1a; 概述&#xff1a; 在前端页面文档查询时&#xff0c;只查询了文…