Guava系列之不可变集合

Guava是一组来自谷歌的核心Java库,其中包括新的集合类型(好比multimap射和multiset)、不可变集合、并发、I/O、散列、缓存、字符串等的实用工具。它在谷歌中的大多数Java项目中被普遍使用,也被许多其余公司普遍使用html

今天咱们就来看一下Guava的不可变集合(Immutable Collections)java

首先咱们思考一个问题,什么是不可变,咱们知道在Java里面String就是不可变的,为何要设计不可变的类和集合呢?web

你能够先想一想这个问题,再继续往下看编程

在这里插入图片描述

什么是不可变对象?

对象建立后,全部的状态和属性在整个生命周期内不能被修改缓存

同理,不可变集合就是集合建立后,不能对集合中的对象进行修改安全

为何须要不可变对象?或者说不可变对象有什么好处?

好处1:让并发处理变得更简单了数据结构

在并发编程的世界里,最麻烦的问题就是处理多个线程间的资源共享问题,稍不注意就会出现线程间相互影响的问题多线程

并且这种问题排查起来很是困难,不是每次都会出现。并发

通常咱们处理并发问题,都是在共享资源上加锁,好比synchronize、Lock等,让线程串行的来进行处理svg

其实仔细想一下,之全部会有并发的问题,是由于线程之间会进行资源争抢,对共享资源进行修改才会影响到其余线程

那假如共享资源不能被修改,每一个线程获取到的都是同样的,就不存在并发的问题了

想一想是否是?每一个线程获取到的数据都是同样的,并且共享资源不能被任何线程修改,那线程之间根本就不会相互影响,自然就是线程安全的

因此不可变对象的好处之一是不用处理并发状况下的资源竞争问题

好处2:消除了反作用

下面咱们看一个例子

public class DemoTest {

    public static void main(String[] args){
        Student student = new Student();
        student.setName("tom");
        student.setAge(30);
        DemoTest demoTest = new DemoTest();
        demoTest.validateAge(student);

        //若是后续要使用Age,极可能不知道Age被改了,容易产生BUG

    }

    public  boolean validateAge(Student student){
        if(student.getAge() > 20){
            student.setAge(student.getAge() - 3);//此处对年龄进行了反作用处理
            return false;
        }
        return true;
    }
}



class Student{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

那假如Student是不可变对象,建立后属性和状态都不能被改变,就不会出现这种问题了

好处3:不可变对象能够减小集合出错的几率

你们在使用Map的时候,是否是常常以String做为Key,那假如String是可变的对象,想一想会有什么问题?

Map<String,String> map = new HashMap<String,String>();
String key = "hello";
map.put(key,"world");

假如key是可变的对象,可能出现的问题是,当你去经过String对象get数据的时候,有可能Map中Key已经变了

会致使你取不到对象了,一转眼对象没啦~~~ 想一想就可怕~~~
在这里插入图片描述

上面咱们说不可变对象有不少好处, 那不可变对象是彻底不可变的吗?

有的人可能就迷惑了

在这里插入图片描述

既然设计成不可变对象了,难道你还能把它给变了不成?

看看下面这段代码:

String str = "Hello";
System.out.println("str:" + str);

Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] arr = (char[]) value.get(str);
arr[4] = '_';
System.out.println("str:" + str);

输出结果:

str:Hello
str:Hell_

能够看出来,String对象建立后,能够利用反射来进行修改

既然能改变,为什么还叫不可变对象?

这里面你们不要误会不可变的本意,从不可变对象的意义分析能看出来对象的不可变性只是用来辅助帮助你们更简单地去编写代码,减小程序编写过程当中出错的几率,这是不可变对象的初衷。

若是真要靠经过反射来改变一个对象的状态,此时编写代码的人也应该会意识到此类在设计的时候就不但愿其状态被更改,从而引发编写代码的人的注意

以上咱们介绍了不可变对象,下面咱们就看一下Guava中的不可变集合

Guava中的不可变集合

为何要使用不可变集合?

  • 不可变对象提供给别人使用时是安全的,由于不可变,全部人都没法进行修改,只能读
  • 支持多个线程调用,不存在竞争的问题,自然支持多线程
  • 不可变集合节省内存空间,由于不可变,集合空间在建立时就已经肯定好了,不用考虑扩容等问题,内存利用率高
  • 不可变集合可用于常量

不可变集合的使用方法

其实JDK中也提供了不可变集合,以下:

List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
List<String> unList = Collections.unmodifiableList(list);
unList.add("d");//往不可变List中添加元素会报错

表面上看,也实现了不可变集合,可是我修改原list呢

list.add(d);

此时,能够修改为功,而且不可变unList中的元素也被修改了,没有达到不可变的特性

Guava中不可变集合的使用方法

一、copyOf方法

基于已有的集合建立不可变集合

List<String> list  = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
ImmutableList<String> immutList = ImmutableList.copyOf(list);

咱们能够验证一下上面JDK不可变集合的问题,在原list中增长元素,不可变集合不受影响

list.add("d");
System.out.println(immutList);

输出以下:

[a,b,c]

二、of方法

ImmutableList<String> immutableList = ImmutableList.of("a","b","c");

三、Builder方法

List<String> list  = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
ImmutableList<String> immutableList = ImmutableList.<String>builder().addAll(list).add("d").build();

能够看到,Builder方法更像是组合了copyOf和of方法

此处,对于有序的不可变集合来讲,是在集合构造完成时就已经排序完成

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

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

智能的Copyof

  • 在常量时间内使用底层数据结构是可能的——例如,ImmutableSet.copyOf(ImmutableList)就不能在常量时间内完成
  • 不会形成内存泄露——例如,你有个很大的不可变集合ImmutableList hugeList, ImmutableList.copyOf(hugeList.subList(0, 10))就会显式地拷贝,以避免没必要要地持有hugeList的引用
  • 不改变语义——因此ImmutableSet.copyOf(myImmutableSortedSet)会显式地拷贝,由于和基于比较器的ImmutableSortedSet相比,ImmutableSet对hashCode()和equals有不一样语义

asList视图

全部不可变集合都有一个asList()方法提供ImmutableList视图,来帮助你用列表形式方便地读取集合元素。例如,你可使用sortedSet.asList().get(k)从ImmutableSortedSet中读取第k个最小元素。

asList()返回的ImmutableList一般是——并不老是——开销稳定的视图实现,而不是简单地把元素拷贝进List。也就是说,asList返回的列表视图一般比通常的列表平均性能更好,好比,在底层集合支持的状况下,它老是使用高效的contains方法。

可变集合与不可变集合对照表

可变集合类型 可变集合源:JDK or Guava? Guava不可变集合
Collection JDK ImmutableCollection
List JDK ImmutableList
Set JDK ImmutableSet
ImmutableSortedSet JDK ImmutableSortedSet
Map JDK ImmutableMap
Multiset Guava ImmutableMultiset
SortedMultiset Guava ImmutableSortedMultiset
Multimap Guava ImmutableMultimap
ListMultimap Guava ImmutableListMultimap
SetMultimap Guava ImmutableSetMultimap
BiMap Guava ImmutableBiMap
ClassToInstanceMap Guava ImmutableClassToInstanceMap
Table Guava ImmutableTable

参考:

https://www.cnblogs.com/dolphin0520/p/10693891.html

https://blog.51cto.com/kaolaa/1794793

若是感受对你有些帮忙,想跟我一块儿学习,坚信技术改变世界,请关注Java天堂公众号,我会按期分享本身的学习成果,第一时间推送给你

在这里插入图片描述