Guava源码分析——Immutable Collections(3)

这一次,我们来分析ImmutableSet,与ImmutableList大同小异,建议大家先看完上一篇Immutable Collections(2),在继续往下看

相同:

  • ImmutableSet底层也采用数组实现
  • of()、copyOf()方法实现逻辑也相同
  • 元素也是按传入顺序排列的
  • 实现是根据元素个数,分为EmptyImmutableSet、SingletonImmutableSet、RegularImmutableSet

不同:

  • construct()方法的实现不再是简单的copy,需要计算hash值,并且ImmutableSet内部维护两套数组(后面会说到)

EmptyImmutableSet、SingletonImmutableSet不需要在做更多详细的说明,他们的存在的意义在于,对单个元素和空集合的操作优化,RegularImmutableSet的实现比较巧妙,它内部维护了两套数组,一套依照传入元素顺序,记录元素。另一套根据Guava自定义的hash算法产生经过hash后的集合的位置,Guava的hash算法效率高于HashSet内部提供的hash算法,而且元素保证传入顺序。

private static <E> ImmutableSet<E> construct(int n, Object... elements) {
    //依然是根据元素个数的不同,选择不同的实现方式
    switch (n) {
      case 0:
        return of();
      case 1:
        @SuppressWarnings("unchecked") // safe; elements contains only E‘s
        E elem = (E) elements[0];
        return of(elem);
      default:
        // continue below to handle the general case
    }
    int tableSize = chooseTableSize(n);
    Object[] table = new Object[tableSize];
    int mask = tableSize - 1;
    int hashCode = 0;
    int uniques = 0;
    for (int i = 0; i < n; i++) {
      Object element = checkElementNotNull(elements[i], i);
      int hash = element.hashCode();
      for (int j = Hashing.smear(hash); ; j++) {
        int index = j & mask;
        Object value = table[index];
        if (value == null) {
          // Came to an empty slot. Put the element here.
          elements[uniques++] = element;//原有元素完全不变的copy到elements集合中
          table[index] = element;//table中存的是hash计算后的元素
          hashCode += hash;//hash值
          break;
        } else if (value.equals(element)) {
          break;
        }
      }
    }
    Arrays.fill(elements, uniques, n, null);
    if (uniques == 1) {
      // There is only one element or elements are all duplicates
      @SuppressWarnings("unchecked") // we are careful to only pass in E
      E element = (E) elements[0];
      return new SingletonImmutableSet<E>(element, hashCode);
    //如果table的size和uniques差的很多,证明重复元素很多,需要重新计算table的size,避免空间浪费
    } else if (tableSize != chooseTableSize(uniques)) {
      return construct(uniques, elements);
    } else {
      Object[] uniqueElements = (uniques < elements.length)
          ? ObjectArrays.arraysCopyOf(elements, uniques)
          : elements;
      return new RegularImmutableSet<E>(uniqueElements, hashCode, table, mask);
    }
  }

而且不必担心空间的浪费,其实两套数组内部,只是维护元素的引用。如果传入的元素集合存在大量的重复元素,ImmutableSet会copy后重新构造Set,以减少table数组(hash过后的)的空间浪费。详见代码。

那么,在RegularImmutableSet中,与set相关的O(1)操作,就会转为对table的操作,而对set的遍历等操作,会转为对elements数组的操作,代码如下:

    RegularImmutableSet(
        Object[] elements, int hashCode, Object[] table, int mask) {
        this.elements = elements;//维护原有集合顺序
        this.table = table;//经过hash后的集合
        this.mask = mask;
        this.hashCode = hashCode;
    }

    //contains操作,使用经过hash后的table
    @Override
    public boolean contains(Object target) {
        if (target == null) {
            return false;
        }
        for (int i = Hashing.smear(target.hashCode()); true; i++) {
            Object candidate = table[i & mask];
            if (candidate == null) {
                return false;
            }
            if (candidate.equals(target)) {
                return true;
            }
        }
    }

    @Override
    public int size() {
        return elements.length;//size的实现肯定使用elements,因为table在有少量重复元素的情况下,稍大于elements
    }
时间: 2024-10-11 22:16:31

Guava源码分析——Immutable Collections(3)的相关文章

Guava源码分析——Immutable Collections(1)

在Java中,conrrent包提供了很多线程安全的集合,但有的时候我们可以换一种方式对思考使用线程安全集合,Guava的Immutable提供了一系列不可变集合类型,不可变就使得集合成为了常量,常量必然线程安全.对于集合的不可变,除了Guava提供的Immutable Collections以外,还是有Collections.unmodifiableCollection(),而两者之间,还是有些区别的.从UML图中,可以看出,ImmutableCollection继承了AbstractColl

Guava源码分析——Immutable Collections(2)

分析过Immutable Collections后,进入具体的数据结构来分析,这一次我们来看看ImmutableList.作为线性可重复集合,ImmutableList的底层实现采用了数组,因为不可变集合,就不存插入删除的操作.数组的下标使得根据index的read的操作,时间复杂度变为了O(1).对于ImmutableList的两种实现,我们先来看下UML图. 首先,我们看下构造一个ImmutableList的方法 @Test public void constructImmutableLis

Guava源码分析——Immutable Collections(4)

Immutable的集合体系,还有中很重要的集合没有介绍,就是ImmutableMap,通过UML图,可以看出ImmutableMap的结构体系. 首先来看一下ImmutableBiMap,因为普通ImmutableMap的实现依赖于它.ImmutableBiMap在ImmutableMap的基础上,加入inverse()等方法,可以使键值反转.ImmutableBiMap的构造,也是根据元素个数的不同,使用不同的实现(0-->EmptyImmutablBiMap,1-->SingletonI

Guava源码分析——Multi Collections(1)

Immutable集合作为Guava提供的集合类型,并没有脱离集合的接口,例如ImmutableList依然实现List接口.但接下来几章要分析的Multi Collections则几乎脱离了JAVA原本所带的集合(这也是为什么Multixxx,代表集合数据结构的单词为小写),作为了JAVA集合的一个补充. 对于Map<T,Integer>这样的Map结构,会经常被使用到,而我们统计T出现的次数的时候,大多时候进行的操作遍历统计,代码如下所示: Map<String, Integer&g

Guava 源码分析之Cache的实现原理

Guava 源码分析之Cache的实现原理 前言 Google 出的 Guava 是 Java 核心增强的库,应用非常广泛. 我平时用的也挺频繁,这次就借助日常使用的 Cache 组件来看看 Google 大牛们是如何设计的. 缓存 本次主要讨论缓存.缓存在日常开发中举足轻重,如果你的应用对某类数据有着较高的读取频次,并且改动较小时那就非常适合利用缓存来提高性能. 缓存之所以可以提高性能是因为它的读取效率很高,就像是 CPU 的 L1.L2.L3 缓存一样,级别越高相应的读取速度也会越快. 但也

【集合框架】JDK1.8源码分析之Collections &amp;&amp; Arrays(十)

一.前言 整个集合框架的常用类我们已经分析完成了,但是还有两个工具类我们还没有进行分析.可以说,这两个工具类对于我们操作集合时相当有用,下面进行分析. 二.Collections源码分析 2.1 类的属性   2.2 构造函数 private Collections() { } 说明:私有构造函数,在类外无法调用. 2.3 方法分析 下面是Collections的所有方法. 可以看到,Collections的方法包含了各种各样的操作.下面分析最常用的方法. 1. sort函数 该函数有两个重载函

【集合框架】JDK1.8源码分析之Collections &amp;&amp; Arrays

一.前言 整个集合框架的常用类我们已经分析完成了,但是还有两个工具类我们还没有进行分析.可以说,这两个工具类对于我们操作集合时相当有用,下面进行分析. 二.Collections源码分析 2.1 类的属性 public class Collections { // 二分查找阈值 private static final int BINARYSEARCH_THRESHOLD = 5000; // 反向阈值 private static final int REVERSE_THRESHOLD = 1

Guava源码分析——Optional

Google的Guava库的出现,使Java代码的书写更加流畅,无论是从效率还是代码风格上,Guava都必将成为一种趋势(Java8明显可以看出,大多数的功能开始借鉴Guava),今天开始,PoNa就以自己微薄的水平试着分析一下Guava的源码,借此使自己更上一层楼,还请各位多多扔砖. Guava文档中,第一篇就提到的尽量避免使用Null,会给代码带来一些负面影响,并举出map.get(key) == null,带来的混淆.由此.Guava提出了Optional的概念. 如图所示,Guava的O

Guava源码分析——Preconditions

Preconditions作为Guava中异常的前置检查,提供了一系列方法.从源码的实现中可以看出,所有的方法都满足以下形式(除format()方法以外). if (!status) { throw new xxException(); } 例如: public static void checkArgument(boolean expression) { if (!expression) { throw new IllegalArgumentException(); } } 源码实现比较简单,