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

Immutable的集合体系,还有中很重要的集合没有介绍,就是ImmutableMap,通过UML图,可以看出ImmutableMap的结构体系。

首先来看一下ImmutableBiMap,因为普通ImmutableMap的实现依赖于它。ImmutableBiMap在ImmutableMap的基础上,加入inverse()等方法,可以使键值反转。ImmutableBiMap的构造,也是根据元素个数的不同,使用不同的实现(0-->EmptyImmutablBiMap,1-->SingletonImmutablBiMap,n(n>=2)-->RegularImmubtalMap),代码如下所示:

public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> implements BiMap<K, V> {

   public static <K, V> ImmutableBiMap<K, V> of() {
        //Empty元素内部,不维护存储结构,inverse()方法直接返回this
        return (ImmutableBiMap<K, V>) EmptyImmutableBiMap.INSTANCE;
    }

    public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1) {
        //单个元素构造时,返回此类,内部维护两个元素K,V,inverse()时,返回V,K的SingletonImmutableBiMap
        return new SingletonImmutableBiMap<K, V>(k1, v1);
    }

    public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1, K k2, V v2) {
        //多个元素构造是,返回此类,内部维护两个Entry[]集合,一个以key作为hashbucket的位置,     //另一个以value作为hashbucket的位置,用于inverse()的时候,key-value的反转
        return new RegularImmutableBiMap<K, V>(entryOf(k1, v1), entryOf(k2, v2));
    }
}

copyOf()方法,在ImmutableCollections中实现的原则就是,如果copyOf()的还是一份ImmutableCollections集合,那么只是进行引用的赋值,因为集合本身不可变。

看过ImmutableBiMap之后,在回头看ImmutableMap就简单了很多,只是在ImmutableBiMap基础上去除了inverse()方法,并在内部为户单一数组(hashbucket)

不需要维护反转的数组。在无元素和单一元素构造的时候,直接调用ImmutableBiMap.of()和ImmutableBiMap.of(K,V)方法,代码如下所示:

public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {

  /**
   * Returns the empty map. This map behaves and performs comparably to
   * {@link Collections#emptyMap}, and is preferable mainly for consistency
   * and maintainability of your code.
   */
  public static <K, V> ImmutableMap<K, V> of() {
    return ImmutableBiMap.of();
  }

  /**
   * Returns an immutable map containing a single entry. This map behaves and
   * performs comparably to {@link Collections#singletonMap} but will not accept
   * a null key or value. It is preferable mainly for consistency and
   * maintainability of your code.
   */
  public static <K, V> ImmutableMap<K, V> of(K k1, V v1) {
    return ImmutableBiMap.of(k1, v1);
  }
}

多个元素构造的时候,返回RegularImmubtalMap,与RegularImmutableBiMap内部实现大同小异,去除对反转(值-键)的数组维护,去除inverse()等方法。

最后简单的阐述一下ImmutableSortedMap的实现,ImmutableMap单一元素和空元素的实现,就不详细说了,有兴趣的读者可以自己看看。多元素实现的时候,

ImmutableSortedMap的具体实现类是RegularImmutableSortedMap,有意思的是,它的内部维护key和value的数据结构是两个List,那么可想而知,排序早在构造的时候就已经完成了,而事实确实是这样,具体代码如下所示:

 @SuppressWarnings("unchecked")
 public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
       //将排序器和entires传入fromEntries方法
       return fromEntries(Ordering.natural(), false, 4, entryOf(k1, v1), entryOf(k2, v2),
                entryOf(k3, v3), entryOf(k4, v4));
 }
  static <K, V> ImmutableSortedMap<K, V> fromEntries(
        Comparator<? super K> comparator, boolean sameComparator, int size, Entry<K, V>... entries) {
        for (int i = 0; i < size; i++) {
            Entry<K, V> entry = entries[i];
            entries[i] = entryOf(entry.getKey(), entry.getValue());
        }
        if (!sameComparator) {
            sortEntries(comparator, size, entries);//遍历entries排序
            validateEntries(size, entries, comparator);
        }

        return fromSortedEntries(comparator, size, entries);
    }
static <K, V> ImmutableSortedMap<K, V> fromSortedEntries( Comparator<? super K> comparator,int size,Entry<K, V>[] entries) {
        if (size == 0) {
            return emptyMap(comparator);
        }
        //遍历排序之后的entries,分开key和value,分别组成各自的List
        ImmutableList.Builder<K> keyBuilder = ImmutableList.builder();
        ImmutableList.Builder<V> valueBuilder = ImmutableList.builder();
        for (int i = 0; i < size; i++) {
            Entry<K, V> entry = entries[i];
            keyBuilder.add(entry.getKey());
            valueBuilder.add(entry.getValue());
        }

        return new RegularImmutableSortedMap<K, V>(
                new RegularImmutableSortedSet<K>(keyBuilder.build(), comparator),
                valueBuilder.build());
}

ImmutableMap中的Entry,也是被Guava重新实现,增加了bucket的计算逻辑,如下图UML:

AbstractMapEntry在原有Map.entry基础上,将写操作,置为直接抛异常,ImmutableEntry实现getKey()和getValue(),ImmutableMapEntry再加入bucket的计算和维护方法(链表),最终反映到NonTerminalMapEntry和TerminalEntry,对于这两个类,TerminalEntry为bucket链表的尾结点,所以实现如下:

static final class TerminalEntry<K, V> extends ImmutableMapEntry<K, V> {
        TerminalEntry(ImmutableMapEntry<K, V> contents) {
            super(contents);
        }

        TerminalEntry(K key, V value) {
            super(key, value);
        }

        @Override
        @Nullable
        ImmutableMapEntry<K, V> getNextInKeyBucket() {
            //尾节点,所以没有nuext
            return null;
        }

        @Override
        @Nullable
        ImmutableMapEntry<K, V> getNextInValueBucket() {
            //尾节点,所以没有nuext
            return null;
        }
    }

而NonTerminalMapEntry的构造则需要传入下一个Entry

private static final class NonTerminalMapEntry<K, V> extends ImmutableMapEntry<K, V> {
    private final ImmutableMapEntry<K, V> nextInKeyBucket;

    NonTerminalMapEntry(K key, V value, ImmutableMapEntry<K, V> nextInKeyBucket) {
      super(key, value);
      this.nextInKeyBucket = nextInKeyBucket;
    }

    NonTerminalMapEntry(ImmutableMapEntry<K, V> contents, ImmutableMapEntry<K, V> nextInKeyBucket) {
      super(contents);
      this.nextInKeyBucket = nextInKeyBucket;
    }

    @Override
    ImmutableMapEntry<K, V> getNextInKeyBucket() {
      //同一个bucket中的下一个Entry
      return nextInKeyBucket;
    }

    @Override
    @Nullable
    ImmutableMapEntry<K, V> getNextInValueBucket() {
      //BiMap才会维护Value的Bucket
      return null;
    }
  }

那么在构造的时候,如果产生hash冲突,就是用nonTerminalMapEntry,代码如下所示:

RegularImmutableMap(Entry<?, ?>[] theEntries) {
    int size = theEntries.length;
    entries = createEntryArray(size);
    int tableSize = Hashing.closedTableSize(size, MAX_LOAD_FACTOR);
    table = createEntryArray(tableSize);
    mask = tableSize - 1;
    for (int entryIndex = 0; entryIndex < size; entryIndex++) {
      @SuppressWarnings("unchecked") // all our callers carefully put in only Entry<K, V>s
      Entry<K, V> entry = (Entry<K, V>) theEntries[entryIndex];
      K key = entry.getKey();
      V value = entry.getValue();
      checkEntryNotNull(key, value);
      int tableIndex = Hashing.smear(key.hashCode()) & mask;
      @Nullable ImmutableMapEntry<K, V> existing = table[tableIndex];
      // prepend, not append, so the entries can be immutable
      //在构造是,如果产生hash冲突,那么直接的append到terminal的前面
      ImmutableMapEntry<K, V> newEntry = (existing == null)
          ? new TerminalEntry<K, V>(key, value)
          : new NonTerminalMapEntry<K, V>(key, value, existing);
      table[tableIndex] = newEntry;
      entries[entryIndex] = newEntry;
      checkNoConflictInBucket(key, newEntry, existing);
    }
  }
时间: 2024-11-07 16:43:19

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

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

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

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

这一次,我们来分析ImmutableSet,与ImmutableList大同小异,建议大家先看完上一篇Immutable Collections(2),在继续往下看 相同: ImmutableSet底层也采用数组实现 of().copyOf()方法实现逻辑也相同 元素也是按传入顺序排列的 实现是根据元素个数,分为EmptyImmutableSet.SingletonImmutableSet.RegularImmutableSet 不同: construct()方法的实现不再是简单的copy,需要

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

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

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(); } } 源码实现比较简单,