马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
Immutable
如《Effective Java》Item1)所述,在设计类的时候,倾向优先利用静态工厂方法(static factory method)而非构造函数(constructor)创建对象,长处在于:
- 静态工厂方法多了一层名称信息,比构造函数更富表达性。
- 可以更灵活地创建对象,好比缓式初始化,缓存已创建对象。
- 静态方法内部返回的对象类型,可以是其声明类型的子类。
同样,如《Effective Java》Item17所述,需要最小化可变性,ImmutableList遵照了最佳实践。首先,ImmutableList不可以通过构造函数实例化,更正确地说,不可以在package外部通过构造函数实例化。
而在步伐设计中利用不可变对象,也可以提高代码的可靠性和可维护性,其优势包罗:
- 线程安全性(Thread Safety):不可变对象是线程安全的,无需同步操作,避免了竞态条件
- 安全性:可以防止在步伐运行时被不测修改,提高了步伐的安全性
- 易于理解和测试:不可变对象在创建后不会发生变化,更容易理解和测试
- 克隆和拷贝:不可变对象不需要实现可变对象的复制(Clone)和拷贝(Copy)逻辑,因为它们的状态不可变,克隆即是自己
创建对象的不可变拷贝是一项很好的防御性编程技巧。Guava为所有JDK标准集合类型和Guava新集合类型都提供了简单易用的不可变版本。JDK也提供了Collections.unmodifiableXXX方法把集合包装为不可变形式.
JDK不可变集合存在的问题
JDK 的 Collections 提供了 Unmodified Collections 不可变集合,但仅仅是通过装饰器模式提供了一个只读的视图,unmodifiableList自己是无法进行add等修改操作,但并没有阻止对原始集合的修改操作,所以说Collections.unmodifiableList实现的不是真正的不可变集合。- List<String> list=new ArrayList<String>();
- list.add("a");
- list.add("b");
- list.add("c");
- //通过list创建一个不可变的unmodifiableList集合
- List<String> unmodifiableList = Collections.unmodifiableList(list);
- System.out.println(unmodifiableList);//[a,b,c]
- //通过list添加元素
- list.add("ddd");
- System.out.println("往list添加一个元素:" + list);//[a,b,c,ddd]
- System.out.println("通过list添加元素之后的unmodifiableList:" + unmodifiableList);[]//[a,b,c,ddd]
- //通过unmodifiableList添加元素
- unmodifiableList.add("eee");//报错
- System.out.println("往unmodifiableList添加一个元素:" + unmodifiableList);
复制代码
- 笨重而且累赘:不能舒适地用在所有想做防御性拷贝的场景;
- 不安全:要保证没人通过原集合的引用进行修改,返回的集合才是事实上不可变的;
- 低效:包装过的集合仍然保有可变集合的开销,好比并发修改的检查、散列表的额外空间,等等。
Guava不可变集合案例
而 Guava 提供的不可变集合不是原容器的视图,而是原容器的一份拷贝,因此更加简单高效,确保了真正的不可变性。
但是还要注意,由于immutable只是copy了元容器自己,并不是deep copy,因此对原容器的引用的内容进行修改,也会影响immutableXXX
注意:每个Guava immutable集合类的实现都拒绝null值。如果确实需要能接受null值的集合类,可以考虑用Collections.unmodifiableXXX。
immutable集合可以有以下几种方式来创建:
- 用copyOf方法,好比,ImmutableSet.copyOf(set)
- 利用of方法,好比,ImmutableSet.of("a", "b", "c") 或者 ImmutableMap.of("a", 1, "b", 2)
- 利用Builder类:减少中间对象的创建,提高内存利用服从。
- List<String> list = new ArrayList<>();
- list.add("a");
- list.add("b");
- list.add("c");
- System.out.println("list:" + list);//[a, b, c]
- ImmutableList<String> imlist = ImmutableList.copyOf(list);
- System.out.println("imlist:" + imlist);//[a, b, c]
- ImmutableList<String> imOflist = ImmutableList.of("seven", "seven1", "seven2");
- System.out.println("imOflist:" + imOflist);//[seven, seven1, seven2]
- ImmutableSortedSet<String> imSortList = ImmutableSortedSet.of("a", "b", "c", "a", "d", "b");
- System.out.println("imSortList:" + imSortList);//[a, b, c, d]
- list.add("seven");
- System.out.println("list add a item after list:" + list);//[a, b, c, seven]
- System.out.println("list add a item after imlist:" + imlist);//[a, b, c]
- ImmutableSet<Color> imColorSet =
- ImmutableSet.<Color>builder()
- .add(new Color(0, 255, 255))
- .add(new Color(0, 191, 255))
- .build();
- System.out.println("imColorSet:" + imColorSet); //[java.awt.Color[r=0,g=255,b=255], java.awt.Color[r=0,g=191,b=255]]
复制代码 更智能的copyOf
ImmutableXXX.copyOf会在符合的情况下避免拷贝元素的操作。- ImmutableSet<String> imSet = ImmutableSet.of("seven", "lisa", "seven1", "lisa1");
- System.out.println("imSet:" + imSet);//[seven, lisa, seven1, lisa1]
- ImmutableList<String> imlist = ImmutableList.copyOf(imSet);
- System.out.println("imlist:" + imlist);//[seven, lisa, seven1, lisa1]
- ImmutableSortedSet<String> imSortSet = ImmutableSortedSet.copyOf(imSet);
- System.out.println("imSortSet:" + imSortSet);//[lisa, lisa1, seven, seven1]
- List<String> list = new ArrayList<>();
- for (int i = 0; i < 20; i++) {
- list.add(i + "x");
- }
- System.out.println("list:" + list);//[0x, 1x, 2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x, 18x, 19x]
- ImmutableList<String> imInfolist = ImmutableList.copyOf(list.subList(2, 18));
- System.out.println("imInfolist:" + imInfolist);//[2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x]
- int imInfolistSize = imInfolist.size();
- System.out.println("imInfolistSize:" + imInfolistSize);//16
- ImmutableSet<String> imInfoSet = ImmutableSet.copyOf(imInfolist.subList(2, imInfolistSize - 3));
- System.out.println("imInfoSet:" + imInfoSet);//[4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x]
复制代码 在这段代码中,ImmutableList.copyOf(imSet)会智能地直接返回 imSet.asList(),它是一个ImmutableSet的常量时间复杂度的List视图。
实际上,要实现copyOf方法,最简单的就是直接将底层的每个元素做深拷贝然后生成ImmutableList。但是对于所有情况都深拷贝的话,性能和存储开销必然比力大,那么源码里面是如何优化的呢?
所有不可变集合都有一个asList() 方法提供ImmutableList视图,让我们可以用列表形式方便地读取集合元素。例如,我们可以利用sortedSet.asList().get(k) 从 ImmutableSortedSet 中读取第k个最小元素。
asList()返回的ImmutableList 通常是(但并不总是)开销稳固的视图实现,而不是简单地把元素拷贝进List,也就是说,asList返回的列表视图通常比一般的列表平均性能更好,好比,在底层集合支持的情况下,它总是利用高效的contains方法。
源码如下:- // com.google.common.collect.ImmutableList#copyOf(java.util.Collection<? extends E>)
- public static <E> ImmutableList<E> copyOf(Collection<? extends E> elements) {
- //判断是否是不可变集合
- if (elements instanceof ImmutableCollection) {
- //如果传入的结合本身就是一个不可变集合,那么asList获取视图后返回;其实就是直接复用原来的collection
- ImmutableList<E> list = ((ImmutableCollection)elements).asList();
- //判断是否是要返回局部视图:是的话重新构建->调用Arrays.copyOf做深拷;不是的话就复用原来的
- return list.isPartialView() ? asImmutableList(list.toArray()) : list;
- } else {//如果不是,则执行construct方法:底层调用Arrays.copyOf做深拷贝
- return construct(elements.toArray());
- }
- }
- // com.google.common.collect.ImmutableCollection#asList
- public ImmutableList<E> asList() {
- switch (this.size()) {
- case 0:
- // 返回一个空的不可变集合,这个空集合是个static final常量,可复用
- return ImmutableList.of();
- case 1:
- // 返回一个不可变的 SingletonImmutableList 集合
- return ImmutableList.of(this.iterator().next());
- default:
- return new RegularImmutableAsList(this, this.toArray());
- }
- }
- //com.google.common.collect.RegularImmutableAsList#RegularImmutableAsList(com.google.common.collect.ImmutableCollection<E>, java.lang.Object[])
- RegularImmutableAsList(ImmutableCollection<E> delegate, Object[] array) {
- this(delegate, ImmutableList.asImmutableList(array));
- }
- RegularImmutableAsList(ImmutableCollection<E> delegate, ImmutableList<? extends E> delegateList) {
- this.delegate = delegate;
- this.delegateList = delegateList;
- }
复制代码 以两个Set的并集作为视图
以两个Set的对称部分作为视图
[code]public static Sets.SetView symmetricDifference(Set |