使用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); // 不应在这里修改fruit
    builder.add(fruit);
}
小心空指针异常

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

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

第8章:总结

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

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

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

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

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

相关文章

STL体系结构概述

文章目录 STL是什么&#xff1f;STL的六大组件STL的实现版本额外补充一、容器范围区间二、容器结构与分类序列式容器关联容器有序关联容器不定序关联容器 参考 本文将是STL系列的第一篇文章&#xff0c;主要参考《STL源码剖析》&#xff0c;辅以网络博文&#xff0c;不定时更新…

DevC++ easyx实现图片拖动,一种悬浮窗实现原理与完整代码

翻出来之前写的代码&#xff0c; EasyxDevC开发地图编辑和游戏编辑代码工程文件附注释_哔哩哔哩_bilibili 每次把代码备份下来&#xff0c;等着有一天能够复用代码&#xff0c;产生新的价值。 结果最近这几天才来回顾记录emm “这是怎么搓出来的&#xff1f;”从10行代码到…

代码审查工具FishEye详细使用教程

1. Git代码仓库设置 1、登录并进入到FishEye主页面&#xff0c;点击Repositories进入仓库管理页面&#xff0c;如下图&#xff1a; 2、填写仓库信息&#xff0c;如下图&#xff1a; 3、填写Git地址 http://gitAccount:gitPwd118.24.231.166:8080/git/git/iot-lvdao/iot-dhcc.…

【小黑嵌入式系统第十二课】μC/OS-III程序设计基础(二)——系统函数使用场合、时间管理、临界区管理、使用规则、互斥信号量

上一课&#xff1a; 【小黑嵌入式系统第十一课】μC/OS-III程序设计基础&#xff08;一&#xff09;——任务设计、任务管理&#xff08;创建&基本状态&内部任务&#xff09;、任务调度、系统函数 文章目录 一、系统函数使用场合1.1 时间管理1.1.1 控制任务的执行周期1…

自动生成数控加工的轨迹刀具轨迹阿基米德螺旋线(3D)

文章目录 1. 阿基米德螺旋线2. 生成步骤目标: 基于点云自动生成阿基米德螺旋线轨迹点 针对的是半球形模型效果 1. 阿基米德螺旋线 阿基米德螺旋线(Archimedean spiral)是一种数学曲线,由古希腊数学家阿基米德(Archimedes)在公元前225年左右首次研究和描述。这条曲线的方…

如何实现酷狗音乐pc页面点击播放时,打开多个歌曲播放时,始终在一个播放页面,(标签页的通讯)

大致有两种思路&#xff0c; 一种是通过wind.open()方法传第二个参数&#xff0c; A页面&#xff1a; //点击跳转播放页函数function toPlayPage(){window.open(path/xxxx/xxxx?name音乐名,music)//第二个参数写一个定值&#xff0c;代表跳转页面都为music标签页&#xff0…

计算机服务器中了halo勒索病毒如何解密,halo勒索病毒解密数据恢复

计算机技术的不断发展&#xff0c;为企业的生产运营提供了极大便利&#xff0c;但也为网络安全埋下隐患&#xff0c;网络上的勒索病毒种类也在不断增加&#xff0c;给企业的数据安全带来了严重威胁。近日&#xff0c;云天数据恢复中心接到许多企业的求助&#xff0c;企业的计算…

Unity3D移动端实现摇一摇功能

手机摇一摇功能在平时项目开发中是很常见的需求&#xff0c;利用Unity的重力感应可以很方便的实现该功能。 Unity简化了重力感应的开发&#xff0c; 通过访问Input.acceleration属性&#xff0c;取回加速度传感器的值。首先我们看一下重力传感器的方向问题。Unity3D中重量的取…

【内存泄漏】内存泄漏及常见的内存泄漏检测工具介绍

内存泄漏介绍 什么是内存泄漏 内存泄漏是指程序分配了一块内存&#xff08;通常是动态分配的堆内存&#xff09;&#xff0c;但在不再需要这块内存的情况下未将其释放。内存泄漏会导致程序浪费系统内存资源&#xff0c;持续的内存泄漏还导致系统内存的逐渐耗尽&#xff0c;最…

【Linux】进程周边007之进程控制

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.进程创建 2.进程终止 2.…

python调用DALL·E绘画

实现用gpt的api和他对话后&#xff0c;我们试着调用DALLE的api进行绘画 参考文档 OpenAI API 运行代码 from openai import OpenAIclient OpenAI()user_prompt input("请输入您想生成的图片描述: ")response client.images.generate(model"dall-e-3"…

SpringIOC之SimpleTimeZoneAwareLocaleContext

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

CSS新手入门笔记整理:CSS3弹性盒模型

特点 子元素宽度之和小于父元素宽度&#xff0c;所有子元素最终的宽度就是原来定义的宽度。子元素宽度之和大于父元素宽度&#xff0c;子元素会按比例来划分宽度。在使用弹性盒子模型之前&#xff0c;必须为父元素定义“display:flex;”或“display:inline-flex;”。 弹性盒子…

Linux笔记本电脑投屏到电视,用网页浏览器就能投屏到电视!

Linux系统的电脑如果要投屏到安卓电视屏幕上&#xff0c;可以使用投屏工具AirDroid Cast的网页版和TV版一起实现。 首先&#xff0c;在Linux系统的电脑里用chrome浏览器或edge浏览器打开网址webcast.airdroid.com。这个网址就是AirDroid Cast的网页版。你可以看到中间白色框框的…

一文搞懂 java8 reduce操作

什么是 reduce Java8 中有两大最为重要的改变&#xff0c;其一是 Lambda 表达式&#xff0c;另一个就是 Stream API 了。 Stream 是 Java8 中处理集合的关键抽象概念&#xff0c;它将数据源流化后&#xff0c;可以执行非常复杂的查找、过滤和映射数据、排序、切片、聚合统计等…

中国化妆品头部企业环亚集团携美肤宝、法兰琳卡、滋源、肌肤未来等“新朋友”加入实在智能数智生态圈

广州环亚化妆品科技股份有限公司&#xff08;以下简称“环亚集团”&#xff09;是一家综合性美容化妆品高新技术企业&#xff0c;旗下拥有美肤宝、法兰琳卡、滋源、肌肤未来等多个品牌&#xff0c;产品涵盖洁肤护肤、洗护发、身体护理、精油等多个领域。在中国、澳大利亚、美国…

27 redis 的 sentinel 集群

前言 redis 的哨兵的相关业务功能的实现 哨兵的主要作用是 检测 redis 主从集群中的 master 是否挂掉, 单个哨兵节点识别 master 下线为主管下线, 超过 quorum 个 哨兵节点 认为 master 挂掉, 识别为 客观下线 然后做 failover 的相关处理, 重新选举 master 节点 我们这里…

【C++刷题】前缀和

【C刷题】前缀和 一、前缀和1、题目链接2、解析3、代码 二、二位前缀和1、题目链接2、解析3、代码 三、寻找数组的中心下标1、题目链接2、解析3、代码 四、除自身以外数组的乘积1、题目链接2、解析3、代码 五、和为K的子数组1、题目链接2、解析3、代码 六、和可被K整除的子数组…

Linux Shell 001-Bash简介

Linux Shell 001-Bash简介 本节关键字&#xff1a;Linux、Bash Shell、shell分类 相关指令&#xff1a;bash、sh、cat Shell的介绍 计算机只能认识&#xff08;识别&#xff09;机器语言(0和1)&#xff0c;如&#xff08;11000000 这种&#xff09;。但是&#xff0c;我们的…

手写MapReduce实现WordCount

水善利万物而不争&#xff0c;处众人之所恶&#xff0c;故几于道&#x1f4a6; 文章目录 需求分析编写MapReduce实现上述功能Mapper类Reducer类Driver类 查看输出结果 需求 假设有一个文本文件word.txt&#xff0c;我们想要统计这个文本文件中每个单词出现的次数。 文件内容如下…