《Guava学习笔记之一》:不可变集合Immutable

时间:2021-02-23 20:47:31

《Guava学习笔记之一》:不可变集合Immutable

Guava,简单来说,就是一些类库,来简化我们常用的一些操作的实现。

在学习Guava之前,先看一个场景。这样可以让我们先对这样一个类库有一个感性的认识。

现在一个方法,接受list作为参数,当这个方法被调用的时候,我们需要检查list是不是null和是不是空,一般的java解决方案如下所示:

    public void doSomething( List<Object> list ) {
if( list == null ) {
throw new IllegalArgumentException( "List must not be null" );
}
if( list.isEmpty() ) {
throw new IllegalArgumentException( "List must not be empty" );
}
doSomethingMore( list );
}

当使用guava的预判断,代码的数量明显减少,解决方案如下所示.

    public void doSomething( List<Object> list ) {
checkArgument( list != null, "List must not be null" );
checkArgument( !list.isEmpty(), "List must not be empty" );
doSomethingMore( list );
}

看到guava的解决方案,是不是很爽,很霸气。guava在很多方面都为我们编写简洁的代码提供了便利。

关于Guava在这篇博文中,有一个比较详细的介绍:http://www.cnblogs.com/snidget/archive/2013/02/05/2893344.html

在并发编程网:http://ifeve.com/google-guava/也有关于guava的详细介绍。

以下是自己学习guava的一点学习笔记。本篇博文先来看下不可变集合Immutable.

Immutable(不可变)集合

不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变。

为什么要用Immutable对象?Immutable对象有以下的优点:

1.对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象

2.线程安全的:Immutable对象在多线程下安全,没有竞态条件

3.不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
    

4.可以被使用为一个常量,并且期望在未来也是保持不变的

Immutable对象可以很自然地用作常量,因为它们天生就是不可变的对于Immutable对象的运用来说,它是一个很好的防御编程(defensive programming)的技术实践。

在JDK中提供了Collections.unmodifiableXXX系列方法来实现不可变集合, 但是存在一些问题,看一个例子

    @Test
public void testUnmodifiableCollection(){
List<String> list = Lists.newArrayList("a","b","c");
List<String> unmodifyList = Collections.unmodifiableList(list);
System.out.println(unmodifyList);

//unmodifyList.add("d"); //抛异常

list.add("e");

System.out.println(unmodifyList);
}

输出结果如下:

[a, b, c]
[a, b, c, e]

从以上可以看出,Collections.unmodifiableList实现的不是真正的不可变集合,当原始集合修改后,不可变集合也发生变化。不可变集合不可以修改集合数据,当强制修改时会报错,实例中的unmodifyList.add(“d”);会直接抛出不可修改的错误。

Guava的Immutable集合

  Guava提供了对JDK里标准集合类里的Immutable版本的简单方便的实现,例如:ImmutableList、ImmutableList等,以及Guava自己的一些专门集合类的Immutable实现。当你不希望修改一个集合类,或者想做一个常量集合类的时候,使用immutable集合类就是一个最佳的编程实践。

注意:每个Guava immutable集合类的实现都拒绝null值。我们做过对Google内部代码的全面的调查,并且发现只有5%的情况下集合类允许null值,而95%的情况下都拒绝null值。万一你真的需要能接受null值的集合类,你可以考虑用Collections.unmodifiableXXX。

一个Immutable集合可以有以下几种方式来创建:

1.用copyOf方法, 譬如, ImmutableSet.copyOf(set)

2.使用of方法,譬如,ImmutableSet.of(“a”, “b”, “c”)或者ImmutableMap.of(“a”, 1, “b”, 2)

3.使用Builder类

看一个小的例子


@Test
public void testImmutable(){
List<String> list = Lists.newArrayList("a","b","c");
ImmutableList<String> imList = ImmutableList.copyOf(list);
System.out.println(imList);
list.add("d");
System.out.println("修改原始集合之后,imList的内容为:"+imList);

ImmutableList<String> imOfList = ImmutableList.of("wo", "jiu","shi","mogui");
System.out.println(imOfList);

ImmutableSet<String> imBuildSet = ImmutableSet.<String>builder().add("hello")
.add("world").build();

System.out.println(imBuildSet);
}

输出结果

[a,b,c]
[a,b,c]

从这里可以看出,Immutable集合是不会随着原始集合的改变而改变的。

比想象中更智能的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(),它是一个ImmutableSet的常量时间复杂度的List视图。

asList视图

asList方法是在ImmutableCollection中定义,而所有的不可变集合都会从ImmutableCollection继承,所以所有的不可变集合都会有asList()方法返回当前不可变集合的list视图,这个视图也是不可变的。因此所有不可变集合都可以用asList()方法来获得ImmutableList视图,来帮助我们用列表形式方便地读取集合元素。例如,你可以使用sortedSet.asList().get(k)从ImmutableSortedSet中读取第k个最小元素。

asList()返回的ImmutableList通常是——并不总是——开销稳定的视图实现,而不是简单地把元素拷贝进List。也就是说,asList返回的列表视图通常比一般的列表平均性能更好。

参考资料

1、http://ifeve.com/google-guava-immutablecollections/

2、http://www.cnblogs.com/snidget/archive/2013/02/05/2893112.html

3、Guava在线参考API:http://tool.oschina.net/apidocs/apidoc?api=guava