博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Guava系列之不可变集合
阅读量:4187 次
发布时间:2019-05-26

本文共 4031 字,大约阅读时间需要 13 分钟。

 

Guava的不可变集合(Immutable Collections)

什么是不可变对象?

对象创建后,所有的状态和属性在整个生命周期内不能被修改;同理,不可变集合就是集合创建后,不能对集合中的对象进行修改为什么需要不可变对象?或者说不可变对象有什么好处?

好处1:让并发处理变得更简单了,对象是线程安全的

好处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:不可变对象可以减少集合出错的概率

经常以String作为Key,那假如String是可变的对象,想想会有什么问题?假如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:Hellostr:Hell_

可以看出来,String对象创建后,可以利用反射来进行修改;既然能改变,为何还叫不可变对象?

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

如果真要靠通过反射来改变一个对象的状态,此时编写代码的人也应该会意识到此类在设计的时候就不希望其状态被更改,从而引起编写代码的人的注意

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

为什么要使用不可变集合?

  • 不可变对象提供给别人使用时是安全的
  • 不可变集合节省内存空间,因为不可变,集合空间在创建时就已经确定好了,不用考虑扩容等问题,内存利用率高
  • 不可变集合可用于常量

不可变集合的使用方法

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

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

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

list.add(d);

此时,可以修改成功,并且不可变unList中的元素也被修改了,没有达到不可变的特性

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

1、copyOf方法

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

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

在原list中增加元素,不可变集合不受影响

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

输出如下:

[a,b,c]

2、of方法

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

3、Builder方法

List
list = new ArrayList
();list.add("a");list.add("b");list.add("c");ImmutableList
immutableList = ImmutableList.
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天堂公众号,我会定期分享自己的学习成果,第一时间推送给你

你可能感兴趣的文章
Java并发| Atomic包下的原子操作类使用与原理解析
查看>>
Mac M1 安装 iTerm2+Oh My Zsh+zsh-syntax-highlighting 真香!
查看>>
M1芯片Mac 安装git
查看>>
M1芯片Mac Homebrew 安装
查看>>
一篇文章看懂ZooKeeper内部原理
查看>>
全面理解Java内存模型
查看>>
Java类型信息详解
查看>>
深入理解Java线程池
查看>>
Java线程堆栈分析
查看>>
Java中子类能否继承父类的私有属性和方法
查看>>
JVM内存模型详解
查看>>
(二)Git--工作区和暂存区、管理修改与撤销
查看>>
(七)Git--自定义Git
查看>>
(五)Git--分支管理
查看>>
(四)Git--远程仓库
查看>>
(六) Git--标签管理
查看>>
java中继承,子类是否继承父类的构造函数
查看>>
什么是Spring Cloud ?
查看>>
Qt下D-Bus的具体运用(软键盘输入法的实现)
查看>>
嵌入式环境的搭建(用于Arm开发板)
查看>>