使用Guava轻松创建和管理不可变集合

第1章:引言

大家好,我是小黑。今天,我们来聊聊一个在Java编程里超有用的话题:使用Guava创建和管理不可变集合。首先,咱们得明白,什么是不可变集合。简单来说,不可变集合就是一旦创建就不能被修改的集合。

为啥要用不可变集合呢?想象一下,你写了一段代码,把一个集合传给了别的方法。如果那个方法不小心改了你的集合,那岂不是一场灾难?但如果你的集合是不可变的,这种情况就绝对不会发生。不可变集合还有助于编写更加清晰、更容易维护的代码,还能提高程序的性能哦。

第2章:Guava不可变集合简介

Guava是Google推出的一个Java库,里面有一堆好用的工具类,其中就包括了不可变集合。Guava的不可变集合和咱们平时用的Java标准库集合有啥不同呢?主要是Guava的集合一旦创建,就不能被修改,这就大大减少了出错的可能性。

来,让我给你演示一下怎么用Guava创建不可变集合。比如说,咱们要创建一个不可变的列表:

import com.google.common.collect.ImmutableList;public class ImmutableDemo {public static void main(String[] args) {ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");System.out.println(immutableList);}
}

这段代码创建了一个包含三种水果名称的不可变列表。你看,使用ImmutableList.of就能轻松创建。这样一来,无论谁拿到这个列表,都不能添加、删除或者修改里面的元素。

同理,不可变的集合(Set)和映射(Map)也可以用类似的方式创建。只要记住,一旦创建,就不能更改了。这样的特性在很多场景下都非常实用,比如在多线程环境下,不可变集合可以保证数据的安全性,防止出现并发修改异常。

第3章:创建不可变集合

让我们来看看不可变列表(ImmutableList)的创建。咱们已经看过最基本的创建方式了,现在再来看点高级的:

import com.google.common.collect.ImmutableList;public class ImmutableListDemo {public static void main(String[] args) {// 创建一个不可变列表ImmutableList<String> immutableList = ImmutableList.<String>builder().add("Apple").add("Banana").add("Cherry").build();System.out.println(immutableList);}
}

在这个例子中,小黑用了ImmutableList.builder()方法。这个方法特别棒,因为它可以让咱们一步步地添加元素,最后再用build()方法一次性创建不可变列表。

接下来,看看不可变集(ImmutableSet)的创建:

import com.google.common.collect.ImmutableSet;public class ImmutableSetDemo {public static void main(String[] args) {// 创建一个不可变集ImmutableSet<String> immutableSet = ImmutableSet.of("Apple", "Banana", "Cherry");System.out.println(immutableSet);}
}

ImmutableSet.of()方法和列表的创建很像,也是一次性把所有元素传进去。

我们来看看不可变映射(ImmutableMap)的创建:

import com.google.common.collect.ImmutableMap;public class ImmutableMapDemo {public static void main(String[] args) {// 创建一个不可变映射ImmutableMap<String, Integer> immutableMap = ImmutableMap.of("Apple", 1, "Banana", 2, "Cherry", 3);System.out.println(immutableMap);}
}

在这个例子中,ImmutableMap.of()方法是用键值对的方式来创建映射的。这里,“Apple”是键,1是它的值,以此类推。

第4章:不可变集合的优势

安全性

首先是安全性。不可变对象是线程安全的,这意味着在多线程环境中,你完全不需要担心并发修改的问题。因为数据一旦创建就不会改变,所以不会出现线程间的冲突。这在编写并发程序时特别有用。

举个例子吧,假设有这样一个场景:

public class ThreadSafetyDemo {public static void main(String[] args) {ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");// 多线程环境下访问不可变列表Runnable task = () -> {for (String fruit : immutableList) {System.out.println(Thread.currentThread().getName() + " - " + fruit);}};new Thread(task).start();new Thread(task).start();}
}

在这个例子中,即使多个线程同时访问immutableList,也不会引发任何问题,因为它是不可变的。

效率

接下来谈谈效率。不可变对象由于状态不变,可以减少内存的占用,因为它们可以被自由地共享。这就意味着在一定条件下,不可变集合比可变集合更加内存高效。

可读性

不可变集合还能提高代码的可读性和维护性。当你看到一个集合是不可变的,你就可以立即确定这个集合在整个生命周期内都不会改变。这就减少了理解和维护代码的复杂性。

使用Guava的不可变集合可以带来很多好处,特别是在处理安全性、效率和可读性方面。虽然它们在某些情况下可能有些限制,但在正确的场景下使用,绝对可以帮助你写出更优雅、更稳定的Java代码。

第5章:使用场景与最佳实践

使用场景
  1. 常量集合:当你想定义一些永远不变的数据,比如配置项、选项列表等,不可变集合是最佳选择。它们可以公开访问而不用担心被意外修改。

    public class Constants {public static final ImmutableList<String> FRUITS = ImmutableList.of("Apple", "Banana", "Cherry");// 其他常量...
    }
    
  2. 类内部状态:在创建类时,使用不可变集合来保存内部状态可以确保类的实例一旦创建就保持不变,这对于创建不可变类(immutable class)非常有用。

    public class UserPreferences {private final ImmutableSet<String> preferences;public UserPreferences(Set<String> preferences) {this.preferences = ImmutableSet.copyOf(preferences);}// Getter方法...
    }
    
  3. 函数返回值:当函数需要返回集合时,使用不可变集合作为返回类型可以确保调用者不会修改这个集合,从而保证了数据的完整性。

    public class ProductService {public ImmutableList<Product> getAvailableProducts() {// 查询并返回产品列表}
    }
    
最佳实践
  1. 避免预期外的修改:使用不可变集合可以防止调用者意外修改集合内容,从而导致难以追踪的bug。

  2. 提前拷贝:当从可变集合创建不可变集合时,应当在创建时就进行拷贝,以确保不可变集合的独立性。

    public class ConfigLoader {public ImmutableSet<String> loadConfig(Set<String> mutableSet) {return ImmutableSet.copyOf(mutableSet);}
    }
    
  3. 在构造函数中使用不可变集合:当创建对象时,可以使用Guava的不可变集合作为构造函数参数,这样可以在对象创建时就确保其不可变性。

    public class Employee {private final ImmutableList<String> skills;public Employee(List<String> skills) {this.skills = ImmutableList.copyOf(skills);}// Getter方法...
    }
    

第6章:与Java 8及以后版本的整合

利用Streams处理不可变集合

Java 8的Streams API可以和Guava的不可变集合无缝合作。比如说,你可以轻松地将一个不可变集合转换成Stream,进行各种操作,然后再收集回不可变集合。

import com.google.common.collect.ImmutableList;
import java.util.stream.Collectors;public class StreamIntegrationDemo {public static void main(String[] args) {ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");// 使用Stream API处理Guava不可变集合ImmutableList<String> processedList = immutableList.stream().filter(s -> s.startsWith("B")).collect(ImmutableList.toImmutableList());System.out.println(processedList);}
}

在这个例子里,小黑先把一个不可变列表转换成了Stream,然后用filter方法筛选出以"B"开头的元素,最后再收集回一个不可变列表。这就是Java 8 Stream和Guava不可变集合强强联合的例子。

使用Lambda表达式

Java 8的Lambda表达式也可以和Guava的不可变集合很好地结合。它们可以使得对集合的操作更加简洁。

import com.google.common.collect.ImmutableList;public class LambdaIntegrationDemo {public static void main(String[] args) {ImmutableList<String> immutableList = ImmutableList.of("Apple", "Banana", "Cherry");// 使用Lambda表达式遍历不可变集合immutableList.forEach(fruit -> System.out.println("Fruit: " + fruit));}
}

在这个例子中,咱们使用了forEach方法和Lambda表达式来遍历不可变集合。这种方式比传统的for循环更简洁,更易读。

Guava的不可变集合与Java 8及以后版本的特性相结合,可以提供更强大的数据处理能力,同时让代码变得更加简洁和易于理解。

第7章:避免常见陷阱

不可变集合不等于只读集合

首先要清楚,Guava的不可变集合和只读集合不是一回事。不可变集合是在创建时就确定了内容,而只读集合只是不能修改,其底层数据可能被其他引用修改。看个例子:

import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class ImmutableVsReadOnly {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");// 创建只读集合List<String> readOnlyList = Collections.unmodifiableList(list);// 创建不可变集合ImmutableList<String> immutableList = ImmutableList.copyOf(list);// 修改原始列表list.add("Cherry");// 只读集合的内容发生了变化System.out.println("Read-only list: " + readOnlyList);// 不可变集合的内容没变System.out.println("Immutable list: " + immutableList);}
}

这个例子展示了只读集合和不可变集合的区别。当原始集合修改时,只读集合的内容也会跟着变,但不可变集合的内容不会变。

注意构建时的副作用

在使用ImmutableList.builder()或类似的构建器时,要注意不要引入副作用。比如,不要在添加元素的过程中修改这些元素。

// 错误示范:在构建不可变集合时修改元素
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (String fruit : fruits) {modifyFruit(fruit); // 不应在这里修改fruitbuilder.add(fruit);
}
小心空指针异常

Guava的不可变集合在创建时会对元素进行非空校验,这意味着如果你试图添加一个null元素,它会立即抛出NullPointerException

// 这将抛出NullPointerException
ImmutableList<String> list = ImmutableList.of("Apple", null, "Cherry");

第8章:总结

选择不可变集合不仅仅是为了编码的方便,更重要的是它们提供了额外的安全性、效率和可维护性。在多线程环境下,不可变集合几乎是必不可少的,因为它们天生就是线程安全的。此外,它们还能帮助减少bug和意外行为,尤其是在大型和复杂的项目中。

要注意,在某些情况下,使用不可变集合可能会有性能开销,特别是在需要频繁修改集合的场景中。因此,选择合适的数据结构和集合类型对于任何项目都至关重要。

希望这篇博客能够帮助你更好地理解Guava的不可变集合,让你在Java编程的道路上更进一步。如果你有任何问题或者想深入讨论,欢迎在评论区留言,小黑很乐意和你一起探讨和学习!

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

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

相关文章

设计模式之-原型模式,快速掌握原型模式,通俗易懂的理解原型模式以及使用场景

系列文章目录 设计模式之-6大设计原则简单易懂的理解以及它们的适用场景和代码示列 设计模式之-单列设计模式&#xff0c;5种单例设计模式使用场景以及它们的优缺点 设计模式之-3种常见的工厂模式简单工厂模式、工厂方法模式和抽象工厂模式&#xff0c;每一种模式的概念、使用…

使用OpenCV DNN模块进行人脸检测

内容的一部分来源于贾志刚的《opencv4应用开发、入门、进阶与工程化实践》。这本书我大概看了一下&#xff0c;也就后面几章比较感兴趣&#xff0c;但是内容很少&#xff0c;并没有想像的那种充实。不过学习还是要学习的。 在实际工程项目中&#xff0c;并不是说我们将神经网络…

时间序列分析

常用数据集 2.monash数据集 官网链接 我们的存储库包含30个数据集&#xff0c;包括公开可用的时间序列数据集(不同格式)和由我们管理的数据集。 DatasetDomainNo: of SeriesMin. LengthMax. LengthCompetitionMultivariateDownloadSourceM1Multiple100115150YesNoYearly Quart…

MySQL语句练习题(持续更新~)

表名和字段 –1.学生表 Student(s_id,s_name,s_birth,s_sex) --学生编号,学生姓名, 出生年月,学生性别 –2.课程表 Course(c_id,c_name,t_id) – --课程编号, 课程名称, 教师编号 –3.教师表 Teacher(t_id,t_name) --教师编号,教师姓名 –4.成绩表 Score(s_id,c_id,s_score) --…

【AI】人工智能复兴的推进器之机器学习

目录 一、机器学习的定义 二、机器学习的发展历程 2.1 萌芽期&#xff08;20世纪50年代-60年代&#xff09; 2.2 符号主义时期&#xff08;20世纪60年代-80年代&#xff09; 2.3 统计学习时期&#xff08;20世纪90年代-21世纪初&#xff09; 2.4 深度学习时期&#xff08…

深度剖析Ajax实现方式(原生框架、JQuery、Axios,Fetch)

Ajax学习 简介&#xff1a; ​ Ajax 代表异步 JavaScript 和 XML&#xff08;Asynchronous JavaScript and XML&#xff09;的缩写。它指的是一种在网页开发中使用的技术&#xff0c;通过在后台与服务器进行数据交换&#xff0c;实现页面内容的更新&#xff0c;而无需刷新整个…

高级算法设计与分析(六) -- 分支限界法

系列文章目录 高级算法设计与分析&#xff08;一&#xff09; -- 算法引论 高级算法设计与分析&#xff08;二&#xff09; -- 递归与分治策略 高级算法设计与分析&#xff08;三&#xff09; -- 动态规划 高级算法设计与分析&#xff08;四&#xff09; -- 贪心算法 高级…

RIPV1配置实验

查看路由器路由表&#xff1a; 删除手工配置的静态路由项&#xff1a; Route1->Config->static Remove删除路由项 删除Route3的路由项&#xff0c;方法同上删除Route2的路由项&#xff0c;方法同上 完成路由器RIP配置&#xff1a; Route1->Config->RIP->Ne…

lv12 根文件系统12

目录 1 根文件系统 2 BusyBox 3 实验九 3.1 在 busybox 官网下载 busybox 源码&#xff08;这里我们下载 busybox-1.22.1.tar.bz2&#xff09; 3.2 拷贝 busybox 源码包到 ubuntu 的家目录下&#xff0c;解压并进入其顶层目录 3.3 进入 busybox 配置界面&#xff08;…

新零售模式:重新定义商业未来

随着科技的飞速发展&#xff0c;我们的生活方式正在经历着前所未有的变革。其中&#xff0c;新零售模式正逐渐成为商业领域的新热点&#xff0c;它正在重新定义我们的购物方式&#xff0c;并为企业带来更多的商业机会。 一、新零售模式概述 新零售模式是指将互联网、大数据、…

在 Windows 中关闭指定端口的方法

方法一&#xff1a;使用命令行&#xff08;Command Prompt&#xff09; 查找端口占用情况 打开命令提示符&#xff08;Command Prompt&#xff09;并输入以下命令来查找占用指定端口的进程&#xff1a; netstat -aon|findstr "<port_number>" 这里的 <p…

[已解决] Ubuntu远程桌面闪退+登录显示“远程桌面由于数据加密错误 , 这个会话将结束“

两个月前&#xff0c;由于跑代码在Ubuntu配置环境&#xff0c;乱七八糟的下载了很多东西&#xff0c;导致了一系列问题..... 问题1 Ubuntu远程桌面闪退 实验室有两台服务器&#xff0c;IP后三位分别为141和142&#xff0c;其中141在输入密码后立即闪退&#xff0c;142可以正常…

Linux下 自定义多线程并发快速压缩解压缩脚本

文章目录 自定义多线程压缩解压缩脚本使用 Linux下 自定义多线程并发快速压缩解压缩脚本 Linux下常用的tar工具无法支持并行 压缩和解压&#xff0c;对于大量小文件的解压缩&#xff0c;可借助pigz工具实现多线程并行工作&#xff0c;实现更为高效的压缩和解压缩。 自定义多线…

【分享】4个方法打开PDF文件

PDF是很多人工作中经常使用的电子文档格式&#xff0c;但是可能有些刚接触的小伙伴不知道用什么工具来打开PDF文件&#xff0c;今天小编就来分享一下4种常用的工具。 1. 使用浏览器 只要有电脑基本都会安装一到两款浏览器&#xff0c;其实浏览器也可以用来打开PDF文件。 只需…

B2122 单词翻转

B2122 单词翻转 [B2122 单词翻转]&#xff08;https://www.luogu.com.cn/problem/B2122?contestId150480 B2122 单词翻转 题意 输入一串字符&#xff0c;将它倒着输出&#xff0c;但是&#xff0c;单词之间要换行&#xff0c;才能输出。 思路 先写for循环&#xff0c;在往上…

python 用OpenCV 将图片转视频

import os import cv2 import numpy as npcv2.VideoWriter&#xff08;&#xff09;参数 cv2.VideoWriter() 是 OpenCV 中用于创建视频文件的类。它的参数如下&#xff1a; filename&#xff1a;保存视频的文件名。 fourcc&#xff1a;指定视频编解码器的 FourCC 代码&#xf…

经常使用的排序算法

一、直接插入排序 #include <stdio.h>void insert_sort(int arr[], int n){int i, j, tmp;for (i 1; i < n; i){tmp arr[i];j i - 1;while (j > 0 && arr[j] > tmp){ // 将要插入的元素与数组中的元素比较&#xff08;从后向前比&#xff09;arr[j …

Redis可视化工具Redis Desktop Manager mac功能特色

Redis Desktop Manager mac是一款非常实用的Redis可视化工具。RDM支持SSL / TLS加密&#xff0c;SSH隧道&#xff0c;基于SSH隧道的TLS&#xff0c;为您提供了一个易于使用的GUI&#xff0c;可以访问您的Redis数据库并执行一些基本操作&#xff1a;将键视为树&#xff0c;CRUD键…

【springboot】功能合集

目录 全局监听请求&#xff1a;HandlerInterceptor创建拦截器类添加拦截器拦截器类调用Service服务 全局异常处理&#xff1a;ExceptionHandler统一处理业务异常自定义JsonResult 全局跨域配置&#xff1a;WebMvcConfigurer静态(static)方法中调用接口&#xff08;Service层&am…

PDF.js介绍以及使用

一、PDF.js是什么 PDF.js是一个JavaScript库&#xff0c;可以在现代Web浏览器中渲染和显示PDF文件。它的主要作用是将PDF文件转换为HTML5格式&#xff0c;以便在浏览器上进行展示和交互。 PDF.js的主要功能包括&#xff1a; 在浏览器中显示PDF&#xff1a;PDF.js使用HTML5的…