首页 > 编程语言 >【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(不可变集合篇)

【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(不可变集合篇)

时间:2023-06-28 20:02:12浏览次数:36  
标签:指南 ImmutableSet Set Java Map 不可 使用 集合 Guava

【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(不可变集合篇)_不可变对象

为什么要使用不可变集合

不可变集合包括元组和冻结集合,其特点是不能被修改。元组有序、不可变、可包含不同类型元素,不能进行修改、删除操作,可通过索引访问元素。冻结集合是一种无序的集合类型,内部元素不能修改、添加或删除,因此常用于处理集合的不可变。

不可变对象有很多优点

不可变对象有以下优点:

  1. 对于不可信的库调用,不可变对象是安全的;
  2. 在多线程环境下,由于不可变对象不会发生变化,因此不存在竞态条件问题;
  3. 不可变集合不需要考虑变化,因此可以节省时间和,所有不可变的集合都比可变集合有更好的内性,可以作为常量来安全使用。

Java原生的不可变集合

JDK提供的Collections.unmodifiableXXX方法可以将集合包装为不可变形式,但是我们认为这种方法并不理想。

  • 笨重而繁琐:不能适用于所有需要防御性拷贝的情况;
  • 不安全:必须确保没有人能够通过原始集合的引用进行修改,才能得到实际上不可变的集合;
  • 低效:包装成的集合仍然带有可变集合的负担,例如并发修改检查、散列表需要的额外空间等。

如果不需要修改某个集合,或者希望保持集合不变,将其防御性地拷贝到不可变集合是一个很好的实践。

重要提示:Guava不可变集合的实现不支持null值。通过对Google内部的代码库的详细研究,发现只有5%的情况需要在集合中允许null元素,而其余95%的场景都会快速失败处理null值。如果您需要在不可变集合中使用null,请使用JDK中的Collections.unmodifiableXXX方法

Java原生的不可变集合的案例

这段 Java 代码定义了一个名为 LUCKY_NUMBERS 的静态常量 Set(即不可变集合)。

public static final Set<Integer> LUCKY_NUMBERS;
static {
 Set<Integer> set = new LinkedHashSet<Integer>();
 set.add(4);
 set.add(8);
 set.add(15);
 set.add(16);
 set.add(23);
 set.add(42);
 LUCKY_NUMBERS = Collections.unmodifiableSet(set);
}

使用 static 修饰符来表示这个 Set 常量是与类相关联,而不是与类的实例相关联的。static 代码块是一个静态初始化块,用于在类加载时初始化静态成员变量。在这里,用 LinkedHashSet 创建了一个 Set 对象,并将其设置为不可变集合。LinkedHashSet 可以保 持插入元素的顺序。

这个 Set 常量包含 6 个自然数,即 4,8,15,16,23 和 42。由于 Set 对象被设置为不可变集合,因此无法将新元素添加到集合中,以在代码运行时保持其中元素的不变性。

public static final Set<Integer> LUCKY_NUMBERS
 = Collections.unmodifiableSet(
 new LinkedHashSet<Integer>(
 Arrays.asList(4, 8, 15, 16, 23, 42)));

Java原生定义了一个名为 LUCKY_NUMBERS 的静态常量 Set(即不可变集合)。这个 Set 常量包含 6 个自然数,即 4,8,15,16,23 和 42。与之前的示例代码不同的是,这里使用了更简单的方式实现了相同的功能——使用 Collections 类的 unmodifiableSet() 方法。在这里,使用 Arrays 类的 asList() 方法将一些整数转换成一个 List,然后再使用 LinkedHashSet 构造函数创建一个 LinkedHashSet。最后,将创建的 LinkedHashSet 转换为不可变 Set,并将其赋值为 LUCKY_NUMBERS 静态常量。由于 Set 对象被设置为不可变集合,因此无法将新元素添加到集合中,以在代码运行时保持其中元素的不变性。

public static final ImmutableSet<Integer> LUCKY_NUMBERS
 = ImmutableSet.of(4, 8, 15, 16, 23, 42);

Guava定义了一个名为 LUCKY_NUMBERS 的静态常量集合,其元素为 4,8,15,16,23 和 42。与之前的示例不同的是,这里使用了 Guava 提供的 ImmutableSet,其是一个不可变集合。通过调用 of() 静态方法并传入要添加到集合中的元素,会返回一个不可变的 ImmutableSet 集合对象。ImmutableSet 允许我们创建一个在运行时不可更改的集合,从而使多线程代码的编写更加轻松,并且可以更好地保证代码安全性。由于 LUCKY_NUMBERS 集合使用了 ImmutableSet,因此在其对象的生命周期内,它的内容不会被修改,这有助于确保代码的稳定性和正确性。

怎么使用不可变集合

可用的方法来创建不可变集合有:

  1. 使用copyOf方法,例如ImmutableSet.copyOf(set);
class Foo {
	Set<Bar> bars;
	Foo(Set<Bar> bars) {
		this.bars = ImmutableSet.copyOf(bars); // defensive copy!
	}
}
  1. 使用of方法,例如ImmutableSet.of("a","b","c")或ImmutableMap.of("a",1,"b",2);
public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of(
	"red",
	"orange",
	"yellow",
	"green",
	"blue",
	"purple");

此外,对有序不可变集合来说,排序是在构造集合的时候完成的,如:

ImmutableSortedSet.of("a", "b", "c", "a", "d", "b")

会在构造时就把元素排序为 a, b, c, d。

  1. 使用构建器工具,例如使用ImmutableList.builder()返回一个builder对象,之后可使用该对象来添加或删除元素,并使用它们来创建不可变的集合。
public static final ImmutableSet<Color> GOOGLE_COLORS =
	ImmutableSet.<Color>builder()
	.addAll(WEBSAFE_COLORS)
	.add(new Color(0, 191, 255))
	.build();

比想象中更智能的copyOf

值得注意的是,ImmutableXXX.copyOf() 方法在安全的情况下会尝试避免进行拷贝。实现细节不详,但通常是非常智能的,例如:

ImmutableSet<String> foobar = ImmutableSet.of("foo", "bar", "baz");
thingamajig(foobar);
void thingamajig(Collection<String> collection) {
	ImmutableList<String> defensiveCopy = ImmutableList.copyOf(collection);
	...
}

在这段代码中,调用ImmutableList.copyOf(foobar)会智能地直接返回 foobar.asList(),这是一个常量时间复杂度的 List 视图,其存储的元素是不可变的。

ImmutableXXX.copyOf(ImmutableCollection) 是一种探索性实现,旨在避免对于一些可能的情况进行线性时间复杂度的拷贝操作,例如:

  • 可以在常量时间内使用底层数据结构,但是ImmutableSet.copyOf(ImmutableList)就不能在常量时间内完成。
  • 如使用ImmutableList.copyOf(hugeList.sublist(0,10)),可以避免不必要地持有 hugeList 的引用,从而防止内存泄漏问题的发生。
  • 如果我们想使用 ImmutableSet.copyOf(myImmutableSortedSet) 操作来获取不改变语义的 ImmutableSet,需要注意不同语义对 hashCode() 和 equals 的影响。为避免此类问题,需要显式地拷贝一份新的 ImmutableSet。

为了减少防御性编程风格带来的性能开销,在合适的情况下我们可以尽量避免进行线性拷贝操作。这样可以最大限度地提高代码的性能表现。

asList视图

所有不可变集合都提供 asList() 方法,可以方便地将集合元素以列表形式进行读取。例如,可以使用 sortedSet.asList().get(k) 在 ImmutableSortedSet 中读取第k个最小元素。

注意,asList() 返回的 ImmutableList 视图实现通常是稳定的且开销较小,而非简单地将元素拷贝进列表中。因此,asList() 返回的列表视图通常比一般列表的平均性能更好。此外,在底层集合的支持下,asList() 总是使用高效的 contains() 方法。

可变集合和不可变集合的关系

可变集合接口

JDK

不可变版本

Collection

JDK

ImmutableCollection

List

JDK

ImmutableList

Set

JDK

ImmutableSet

SortedSet/NavigableSet

JDK

ImmutableSortedSet/

Map

JDK

ImmutableMap

SortedMap/NavigableMap

JDK

ImmutableSortedMap/

Multiset

Guava

ImmutableMultiset

SortedMultiset/NavigableMultiset

Guava

ImmutableSortedMultiset/

Multimap

Guava

ImmutableMultimap

ListMultimap

Guava

ImmutableListMultimap

SetMultimap

Guava

ImmutableSetMultimap

BiMap

Guava

ImmutableBiMap

ClassToInstanceMap

Guava

ImmutableClassToInstanceMap

Table

Guava

ImmutableTable

不可变的Map

定义了一个名为 ENGLISH_TO_INT 的静态不可变 Map,用于将字符串类型的英文单词转换为对应的整数值。

public static final Map<String, Integer> ENGLISH_TO_INT;
static {
 Map<String, Integer> map
 = new LinkedHashMap<String, Integer>();
 map.put("four", 4);
 map.put("eight", 8);
 map.put("fifteen", 15);
 map.put("sixteen", 16);
 map.put("twenty-three", 23);
 map.put("forty-two", 42); 
 ENGLISH_TO_INT = Collections.unmodifiableMap(map);
}

使用 LinkedHashMap 存储字符串-整数的映射关系,其中字符串代表英文单词,整数为对应的数字。在静态代码块中,程序使用 Collections.unmodifiableMap() 将这个 Map 转换为不可修改的 Map,并将其赋值给 ENGLISH_TO_INT 常量。这个过程将会确保 ENGLISH_TO_INT 对象在其生命周期内不会被修改,从而防止了可能的错误或安全问题。

public static final ImmutableMap<String, Integer>
 ENGLISH_TO_INT = ImmutableMap
 .with("four", 4)
 .with("eight", 8)
 .with("fifteen", 15)
 .with("sixteen", 16)
 .with("twenty-three", 23)
 .with("forty-two", 42)
 .build();

这段代码使用Guava定义了一个名为 ENGLISH_TO_INT 的静态不可变 Map,用于将字符串类型的英文单词转换为对应的整数值。这个代码的实现方式是使用 Guava 开源库中的 ImmutableMap 实现。静态常量 ENGLISH_TO_INT 的值是通过使用 with() 方法连续地向 ImmutableMap 对象中添加字符串-整数的映射关系,然后调用 build() 方法建立不可变的 Map 对象实现的。

与第一个代码示例相比,这个实现方式更加简洁且语法更加直观。此外,Guava 中的 ImmutableMap 实现具有更好的性能和可读性,并能够避免并发修改和不必要的内存使用,从而更容易实现代码的可靠性和稳定性。

ImmutableMap.of
static final ImmutableMap<Integer, String> MAP
 = ImmutableMap.of(1, "one", 2, "two");

这段 Java 代码定义了一个名为 MAP 的静态不可变 Map 对象,它的键是整数类型的 1 和 2,分别对应的值是字符串类型的 "one" 和 "two"。

ImmutableMap.of() 方法是 Guava 开源库中用于创建不可变 Map 对象的快捷方式。这个方法支持传递 1 到 5 个键值对作为参数,从而只需一行代码就能创建一个简单的不可变 Map 对象。由于这个 Map 对象是不可变的,因此它的键值对不能被增加、删除或修改。这种不可变性使得这个 Map 对象更加稳定和安全,并且在多线程环境中更加可靠。

总的来说,这种代码编写风格更加简洁和直观,同时使用了 Guava 提供的不可变集合工具类库,从而可以提高代码的可读性和稳定性。同时这种实现方式还可以避免程序的并发修改和不必要的内存使用,具有更好的性能表现。

标签:指南,ImmutableSet,Set,Java,Map,不可,使用,集合,Guava
From: https://blog.51cto.com/alex4dream/6575876

相关文章

  • Day05 5.1 Java环境搭建
    Day055.1Java环境搭建【一】Jdk的安装和配置【1】安装jdk是否需要再选择安装一个额外的JRE。这是可选的,因为之前安装的JDK中,已经包含了开发环境和JRE运行环境两部分,所以不必再安装一个JRE。【2】配置环境变量(1)新建JAVA_HOMEJAVA_HOMED:\ProgramFiles\Java\jdk-1......
  • 【Java】使用 fasterxml.jackson 反序列化的一个注意事项
    我们在对接接口时,不时会遇到以Json格式返回数据的接口。后端解析此类接口返回数据时,不免需要进行反序列化以获取到需要的数据对象。常用的反序列化工具有 Fastjson、Jackson、Gson。这三种都是不错的Json处理工具,我这里较常用的是Jackson。使用 Jackson反序列化:1.......
  • JavaScript学习 -- 内置函数(Math和Date)
    一、Date函数letdate=newDate()console.log("当前日期和时间:"+date)console.log("当前日期和时间:"+date.toLocaleString())console.log("年份:"+date.getFullYear())console.log("月份:"+(parseInt(date.getMonth())+1))console.log("日:"......
  • Java基础-Day05
    Java基础-Day05breake和continue使用上的相同点和不同点不相同点:break:可用于switch-case和循环结构(结束当前循环)continue:只能循环结构(结束档次循环)相同点:其后不可以声明执行语句衡量一个功能代码的优劣正确性可读性健壮性高效率与低储存:时间复杂度(衡量效率)和空间......
  • Java 12 新特性
    Java12是Java平台的另一个重要版本,它引入了一些新特性和改进。以下是Java12的一些新特性以及基于这些特性的Java代码案例:Switch表达式(SwitchExpressions):Java12引入了新的Switch表达式,使得Switch语句更加灵活和易用。下面是一个使用Switch表达式的示例:1......
  • Java反射
    动态语言:是指程序在运行是可以改变其结构,新的函数可以引进,已有的函数可以被删除等结构上的变化。比如常见的JavaScript就是动态语言,除此之外Ruby、Python等也属于动态语言,而C、C++则不属于动态语言。从反射角度说,Java属于半动态语言。反射机制概念(运行状态中知道类所有的属性和方法......
  • JavaScript引爆Salesforce职业生涯!抓住高薪机会
    Salesforce是一款领先的CRM软件,已被各种规模和行业的企业使用多年。Salesforce不仅易于使用,而且可定制,使企业能够改善其销售、营销、客户服务和其他业务流程。近年来,Salesforce一直在创新,从传统的基于Oracle的平台转变为在业界更具吸引力的解决方案。目前,许多工作可以由人工智能......
  • 1. 使用 IntelliJ IDEA 开发 Azure Function APP (Java)
    1.1前提Azure账户并有可食用的订阅。创建免费的Azure账户。Azure支持的JavaDevelopmentKit(JDK),JDK8或JDK11。IntelliJIDEA无限制版本或社区版本均可。Maven3.5+最新的FunctionCoreTools1.2安装插件并登录在IntelliJIDEA中安装AzureToolkit插......
  • 解锁高质量文档转换:通过Java应用程序免费将PDF转换为XPS
    在当今数字时代,文档转换已经成为我们日常生活中不可或缺的一部分。然而,不是所有的文件格式都是平等无差的,在与他人共享文档时可能会遇到兼容性问题。这就是我们需要进行文件格式转换的原因,而将PDF转换为XPS是一种受欢迎的转换选择。如果你正在寻找一种使用Java将PDF文件转换为XPS......
  • 讯飞离线语音合成新版(Aikit)-android sdk合成 demo(Java版本)
    前言:科大讯飞的新版离线语音合成,由于官网demo是kt语言开发的,咱也看不懂kt,搜遍了全网也没看到一个java版的新版离线语音demo,现记录下,留给有缘人参考!!!!!毕竟咱在这上面遇到了不少的坑。如果能留言指正,那就更好了。第一步:​ 官网注册账号---》实名认证---》点击语音合成---》离线语音......