一、Java 不可以变的集合
Guava学习笔记:Immutable(不可变)集合
不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变。
为什么要用immutable对象?immutable对象有以下的优点:
1.对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
2.线程安全的:immutable对象在多线程下安全,没有竞态条件
3.不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
4.可以被使用为一个常量,并且期望在未来也是保持不变的
immutable对象可以很自然地用作常量,因为它们天生就是不可变的对于immutable对象的运用来说,它是一个很好的防御编程(defensive programming)的技术实践。
JDK中实现immutable集合
在JDK中提供了Collections.unmodifiableXXX系列方法来实现不可变集合, 但是存在一些问题,下面我们先看一个具体实例:
import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.Test; public class ImmutableTest { @Test public void testJDKImmutable(){ List<String> list=new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c"); System.out.println(list); List<String> unmodifiableList=Collections.unmodifiableList(list); System.out.println(unmodifiableList); List<String> unmodifiableList1=Collections.unmodifiableList(Arrays.asList("a","b","c")); System.out.println(unmodifiableList1); String temp=unmodifiableList.get(1); System.out.println("unmodifiableList [0]:"+temp); list.add("baby"); System.out.println("list add a item after list:"+list); System.out.println("list add a item after unmodifiableList:"+unmodifiableList); unmodifiableList1.add("bb"); System.out.println("unmodifiableList add a item after list:"+unmodifiableList1); unmodifiableList.add("cc"); System.out.println("unmodifiableList add a item after list:"+unmodifiableList); } }
输出:
[a, b, c] [a, b, c] [a, b, c] unmodifiableList [0]:b list add a item after list:[a, b, c, baby] list add a item after unmodifiableList1:[a, b, c, baby]
说明:Collections.unmodifiableList实现的不是真正的不可变集合,当原始集合修改后,不可变集合也发生变化。不可变集合不可以修改集合数据,当强制修改时会报错,实例中的最后两个add会直接抛出不可修改的错误。
总结一下JDK的Collections.unmodifiableXXX方法实现不可变集合的一些问题:
1.它用起来笨拙繁琐你不得不在每个防御性编程拷贝的地方用这个方法
2.它不安全:如果有对象reference原始的被封装的集合类,这些方法返回的集合也就不是正真的不可改变。
3.效率低:因为它返回的数据结构本质仍旧是原来的集合类,所以它的操作开销,包括并发下修改检查,hash table里的额外数据空间都和原来的集合是一样的。
Guava的immutable集合
Guava提供了对JDK里标准集合类里的immutable版本的简单方便的实现,以及Guava自己的一些专门集合类的immutable实现。当你不希望修改一个集合类,或者想做一个常量集合类的时候,使用immutable集合类就是一个最佳的编程实践。
注意:每个Guava immutable集合类的实现都拒绝null值。我们做过对Google内部代码的全面的调查,并且发现只有5%的情况下集合类允许null值,而95%的情况下都拒绝null值。万一你真的需要能接受null值的集合类,你可以考虑用Collections.unmodifiableXXX。
Immutable集合使用方法:
一个immutable集合可以有以下几种方式来创建:
1.用copyOf方法, 譬如, ImmutableSet.copyOf(set)
2.使用of方法,譬如,ImmutableSet.of("a", "b", "c")或者ImmutableMap.of("a", 1, "b", 2)
3.使用Builder类
实例:
@Test public void testGuavaImmutable(){ List<String> list=new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c"); System.out.println("list:"+list); ImmutableList<String> imlist=ImmutableList.copyOf(list); System.out.println("imlist:"+imlist); ImmutableList<String> imOflist=ImmutableList.of("peida","jerry","harry"); System.out.println("imOflist:"+imOflist); ImmutableSortedSet<String> imSortList=ImmutableSortedSet.of("a", "b", "c", "a", "d", "b"); System.out.println("imSortList:"+imSortList); list.add("baby"); System.out.println("list add a item after list:"+list); System.out.println("list add a item after imlist:"+imlist); ImmutableSet<Color> imColorSet = ImmutableSet.<Color>builder() .add(new Color(0, 255, 255)) .add(new Color(0, 191, 255)) .build(); System.out.println("imColorSet:"+imColorSet); }
输出:
list:[a, b, c]
imlist:[a, b, c]
imOflist:[peida, jerry, harry]
imSortList:[a, b, c, d]
list add a item after list:[a, b, c, baby]
list add a item after imlist:[a, b, c]
imColorSet:[java.awt.Color[r=0,g=255,b=255], java.awt.Color[r=0,g=191,b=255]]
对于排序的集合来说有例外,因为元素的顺序在构建集合的时候就被固定下来了。譬如,ImmutableSet.of("a", "b", "c", "a", "d", "b"),对于这个集合的遍历顺序来说就是"a", "b", "c", "d"。
更智能的copyOf
copyOf方法比你想象的要智能,ImmutableXXX.copyOf会在合适的情况下避免拷贝元素的操作-先忽略具体的细节,但是它的实现一般都是很“智能”的。譬如:
@Test public void testCotyOf(){ ImmutableSet<String> imSet=ImmutableSet.of("peida","jerry","harry","lisa"); System.out.println("imSet:"+imSet); ImmutableList<String> imlist=ImmutableList.copyOf(imSet); System.out.println("imlist:"+imlist); ImmutableSortedSet<String> imSortSet=ImmutableSortedSet.copyOf(imSet); System.out.println("imSortSet:"+imSortSet); List<String> list=new ArrayList<String>(); for(int i=0;i<20;i++){ list.add(i+"x"); } System.out.println("list:"+list); ImmutableList<String> imInfolist=ImmutableList.copyOf(list.subList(2, 18)); System.out.println("imInfolist:"+imInfolist); int imInfolistSize=imInfolist.size(); System.out.println("imInfolistSize:"+imInfolistSize); ImmutableSet<String> imInfoSet=ImmutableSet.copyOf(imInfolist.subList(2, imInfolistSize-3)); System.out.println("imInfoSet:"+imInfoSet); }
输出:
imSet:[peida, jerry, harry, lisa] imlist:[peida, jerry, harry, lisa] imSortSet:[harry, jerry, lisa, peida] list:[0x, 1x, 2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x, 18x, 19x] imInfolist:[2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x] imInfolistSize:16 imInfoSet:[4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x]
在这段代码中,ImmutableList.copyOf(imSet)会智能地返回时间复杂度为常数的ImmutableSet的imSet.asList()。
一般来说,ImmutableXXX.copyOf(ImmutableCollection)会避免线性复杂度的拷贝操作。如在以下情况:
这个操作有可能就利用了被封装数据结构的常数复杂度的操作。但例如ImmutableSet.copyOf(list)不能在常数复杂度下实现。
这样不会导致内存泄漏-例如,你有个ImmutableList<String> imInfolist,然后你显式操作ImmutableList.copyOf(imInfolist.subList(0, 10))。这样的操作可以避免意外持有不再需要的在hugeList里元素的reference。
它不会改变集合的语意-像ImmutableSet.copyOf(myImmutableSortedSet)这样的显式拷贝操作,因为在ImmutableSet里的hashCode()和equals()的含义和基于comparator的ImmutableSortedSet是不同的。
这些特性有助于最优化防御性编程的性能开销。
asList方法
所有的immutable集合都以asList()的形式提供了ImmutableList视图(view)。譬如,你把数据放在ImmutableSortedSet,你就可以调用sortedSet.asList().get(k)来取得前k个元素的集合。
返回的ImmutableList常常是个常数复杂度的视图,而不是一个真的拷贝。也就是说,这个返回集合比一般的List更智能-譬如,它会更高效地实现contains这样的方法。
实例:
@Test public void testAsList(){ ImmutableList<String> imList=ImmutableList.of("peida","jerry","harry","lisa","jerry"); System.out.println("imList:"+imList); ImmutableSortedSet<String> imSortList=ImmutableSortedSet.copyOf(imList); System.out.println("imSortList:"+imSortList); System.out.println("imSortList as list:"+imSortList.asList()); }
输出:
imList:[peida, jerry, harry, lisa, jerry]
imSortList:[harry, jerry, lisa, peida]
imSortList as list:[harry, jerry, lisa, peida]
Guava集合和不可变对应关系
可变集合类型 | 可变集合源:JDK or Guava? | Guava不可变集合 |
Collection | JDK | ImmutableCollection |
List | JDK | ImmutableList |
Set | JDK | ImmutableSet |
SortedSet/NavigableSet | JDK | ImmutableSortedSet |
Map | JDK | ImmutableMap |
SortedMap | JDK | ImmutableSortedMap |
Multiset | Guava | ImmutableMultiset |
SortedMultiset | Guava | ImmutableSortedMultiset |
Multimap | Guava | ImmutableMultimap |
ListMultimap | Guava | ImmutableListMultimap |
SetMultimap | Guava | ImmutableSetMultimap |
BiMap | Guava | ImmutableBiMap |
ClassToInstanceMap | Guava | ImmutableClassToInstanceMap |
Table | Guava | ImmutableTable |
二、 Guava 之 Multimap 用法简介
前不久在这篇 使用 Google Guava 美化你的 Java 代码:1~4 中的 “ 一个集合统治一切 – Multimap” 部分
说个具体的应用场景吧:
比如现在我有一份日志记录,每条记录的内容是一个 url 对应一个访客的 userid,我现在想得到 每个 url 对应的 pv、uv 数据,你会怎么干?
一般这么想的:用 url 做 key,userid 作为对应 list 的内容:
Map<String,List<MyClass>> myClassListMap test2 = new HashMap<String,List<MyClass>>()
然后你需要检查key是否存在,否则创建一个,最后代码成为这个样子:
void putMyObject(String key, Object value) { List<Object> myClassList = myClassListMap.get(key); if(myClassList == null) { myClassList = new ArrayList<object>(); myClassListMap.put(key,myClassList); } myClassList.add(value); }
如果你希望检查List中的对象是否存在,删除一个对象,或者遍历整个数据结构,那么需要更多的代码。
下面看看用之前提到的 Guava MultiMap 怎么优雅的解决这个问题。
Multimap<String,Object> myMultimap = ArrayListMultimap.create();
这里需要注意,所有的guava的集合都有create()方法,这个好处就是比较简单,你不用重复泛型信息了。
好了,开始使用Multimap了:
package com.test; import java.util.Collection; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; public class MutliMapTest { public static void main(String... args) { Multimap<String, String> myMultimap = ArrayListMultimap.create(); // Adding some key/value myMultimap.put("Fruits", "Bannana"); myMultimap.put("Fruits", "Apple"); myMultimap.put("Fruits", "Pear"); myMultimap.put("Fruits", "Pear"); myMultimap.put("Vegetables", "Carrot"); // Getting the size int size = myMultimap.size(); System.out.println(size); // 5 // Getting values Collection<String> fruits = myMultimap.get("Fruits"); System.out.println(fruits); // [Bannana, Apple, Pear, Pear] System.out.println(ImmutableSet.copyOf(fruits));// [Bannana, Apple, Pear] // Set<Foo> set = Sets.newHashSet(list); // Set<Foo> foo = new HashSet<Foo>(myList); Collection<String> vegetables = myMultimap.get("Vegetables"); System.out.println(vegetables); // [Carrot] // Iterating over entire Mutlimap for (String value : myMultimap.values()) { System.out.println(value); } // Removing a single value myMultimap.remove("Fruits", "Pear"); System.out.println(myMultimap.get("Fruits")); // [Bannana, Apple, Pear] // Remove all values for a key myMultimap.removeAll("Fruits"); System.out.println(myMultimap.get("Fruits")); // [] (Empty Collection!) } }
这里有一点你可能会疑惑,就是为何get方法返回的是一个collection而不是list,这是因为前者会更加有用。如果你需要基于multimap直接操作list或者set,那么可以使用在定义类型的时候使用子类名称:ListMultimap,SetMultimap和SortedSetMultimap。例如:
ListMutlimap<String,String> myMutlimap = ArrayListMultimap.create(); List<string> myValues = myMutlimap.get("myKey"); // Returns a List, not a Collection.
好了,基本就是这样,你可以参考API获取更多信息:
http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/index.html
相关推荐
在标题和描述中提到的"guava-23.0.zip"是一个包含Guava库版本23.0的压缩文件,而"guava.jar"则是Guava库的JAR文件,"guava"可能指的是Guava库本身或者与其相关的其他内容。 Guava库的核心特性包括: 1. **集合框架...
Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, 等等. 这些高质量的 API 可以使你...
4. **断言ByteSource**: ByteSource是Guava中处理字节流的类,AssertJ Guava提供了相应的断言方法来验证其内容,比如文件大小、内容匹配等,这对于处理二进制数据的测试至关重要。 **使用与文档** 在使用AssertJ ...
Guava的I/O工具也非常强大,例如Files类提供了大量与文件操作相关的静态方法,如读写文件、创建目录、比较文件内容等。CharSource和ByteSource提供了一种更面向对象的方式来处理文本和二进制数据源。 Guava还引入了...
总的来说,结合《Java并发编程实践学习笔记》和这些Guava相关的文档,开发者可以系统地学习Java并发编程,掌握如何在实际项目中运用并发技术,提高代码的并发性和效率,同时了解Google内部的并发缓存策略,提升大型...
而“新建文本文档 (2).txt”可能是包含有关如何使用该JAR包或其他相关信息的文本文件,但没有详细描述,无法进一步分析其内容。 总之,Guava JAR包是Java开发中的重要工具,提供了许多实用功能,能显著提升代码质量...
因此,我们将基于“源码”这一标签来推测内容。通常,这样的资源会包含一系列的Java源代码文件(.java),可能是一个或多个Java项目,用于教授如何使用Spring框架,可能还涉及Guava库的使用。 Spring框架是Java企业...
Guava 是Google开发的一个高性能、现代的Java类库,它提供了一些高级功能和工具,以提高开发效率和代码性能: - Guava Cache 提供了一种高效的缓存机制,可以帮助减少不必要的计算或数据库查询。 - Guava EventBus ...
虽然这篇博文的具体内容没有详细给出,但是从标题和相关标签可以看出,本文将主要讨论Google内部使用的并发缓存技术,以及相关的源码和工具。 并发缓存的实现与优化是衡量一个系统性能的重要指标。在一个大型系统中...
`Base64`可能是一个核心类,包含与Base64编码相关的静态方法,比如编码和解码函数。`Encoder.java`可能表示一个更通用的编码接口或者类,其中可能包括Base64编码作为其中的一个实现。 在Java中,处理Base64有多种...
由于无法直接提供内容,我将根据常见的Web开发知识和提供的jar文件来讨论可能的相关知识点。 【标签】 1. **源码**:通常意味着我们将探讨编程语言(如Java),以及如何阅读、理解和修改已有的代码。 2. **工具**:...
"编译工程需要的guava-14.0.1.jar"是Google的Guava库,一个广泛使用的Java实用工具库,它包含了集合、并发、I/O等多个领域的工具类,对于项目的开发非常有帮助。 在Android上使用ZeroMQ,需要考虑到Android的限制,...
Guava项目最初是建立在google collections的基础上,旨在提供一系列高效、实用的Java集合类以及其他的工具类。随着Guava达到1.0版本,它将合并google collections,成为一个独立的项目进行维护。这为开发者提供了更...
压缩包中包含的文件名看起来像是Maven库的JAR文件,这些文件可能与Google的一些库相关,比如GWT(Google Web Toolkit)、Guava、Guice、Gson、GWTORM、GWTJSONRPC和Gerrit项目。具体来说: 1. **GWT(Google Web ...
标题"Java 遍历文件夹内文件"所指的就是使用`File`类及其相关方法来查找和访问一个目录下的所有文件和子目录。例如,你可以使用`listFiles()`方法来获取目录下的所有文件和子目录,它会返回一个`File`对象数组。但要...
6. **Guava Cache**:Google的Guava库提供了强大的缓存功能,可能会详细讲解如何使用Guava Cache构建和管理缓存。 7. **JCache(JSR 107)**:Java标准的缓存API,介绍如何利用JCache接口进行缓存操作,以及它与...
这些库涵盖了各种依赖,例如Spring框架(用于依赖注入和事务管理),JDBC驱动(用于与数据库交互),以及一些额外的Java库,如Apache Commons和Google Guava,它们提供了许多实用工具类和函数,帮助实现流程引擎的...
4. **Guava**:虽然不是专门为了发送邮件设计,Google的Guava库提供了一些通用工具,可以帮助我们在处理邮件时进行字符串操作、集合处理等,提高了代码的可读性和效率。 5. **Outlook或Notes相关库**:如果描述中...
3. **guava-23.6-jre.jar**: Google Guava 是一个常用的 Java 库,提供了一系列实用工具类,包括集合、缓存、并发工具等,它增强了 Java 标准库的功能,对 Selenium 运行时性能优化有积极帮助。 4. **selenium-...
5. **Guava**:Google的Guava库虽然不专注于压缩,但其`CompressedObject`类提供了一种简单的机制来透明地压缩和解压缩对象。此外,Guava的`Files`类有便捷的方法来压缩和解压缩文件和目录。 在实际应用中,我们还...