Guava ImmutableSet.Builder源码分析,移位原码补码反码复习

建筑者模式的继承结构

建筑者模式对于构建非常的爽,这种写法也是比较的喜欢的,看看这里处理的继承体系吧
使用ImmutableSet作为例子

  • 每一个都有一个static的成员方法,更好的统一所有集合的构造调用
    使用ImmutableSet作为例子中的
 public static <E> Builder<E> builder() {
    return new Builder<E>();
}
  • ImmutableSet内部都有static的Builder类的继承,这里继承更加可以构造满足当前ImmutableSet的结构,构造出来build().方法在这里很有体现。

  • ImmutableCollection.ArrayBasedBuilder,这个从名字就可以知道这个包含了容器相关的 object[] coents= new object[defaultsize],我们构造集合中的数据都是放置在这里进行处理的。根据类的单一原则来说,一个类只做一件事,这点做的不错,封装的很好,能够最大限度的重用代码。

  • 基本上的代码都是继承父类的方法去实现的,什么扩容,添加元素等等,这里还是用了模板方法,让父类调用子类的实现。addAll(…)
  • 我很喜欢这种链式的书写风格,感觉非常的不错。
  /**
   * A builder for creating {@code ImmutableSet} instances
   *
   *   static final ImmutableSet<Color> GOOGLE_COLORS =
   *       ImmutableSet.<Color>builder()
   *           .addAll(WEBSAFE_COLORS)
   *           .add(new Color(0, 191, 255))
   *           .build();}</pre>
   */
  public static class Builder<E> 
    extends 
    ImmutableCollection.ArrayBasedBuilder<E> {
    /**
     * Creates a new builder. 
     *The returned builder is equivalent to the builder
     * generated by {@link ImmutableSet#builder}.
     *下面这些都是调用父类的方法
     */
    public Builder() {
      this(DEFAULT_INITIAL_CAPACITY);//4
    }
    Builder(int capacity) {
      super(capacity);
    }
    @Override
    public Builder<E> add(E element) {
      super.add(element);
      return this;
    }
    @Override
    public Builder<E> add(E... elements) {
      super.add(elements);
      return this;
    }
    @Override
    public Builder<E> addAll(Iterable< ? extends E> elements) {
      super.addAll(elements);
      return this;
    }
    @Override
    public Builder<E> addAll(Iterator< ? extends E> elements) {
      super.addAll(elements);
      return this;
    }
    @Override
    Builder<E> combine(ArrayBasedBuilder<E> builder) {
      super.combine(builder);
      return this;
    }
    /**
     * Returns a newly-created  based on the contents of
     * the {@code Builder}.
     * construct调用当前类ImuutableSet的构造方法,这里要处理去重
     * 所以size改变感觉没啥用
     */
    @Override
    public ImmutableSet<E> build() {
      ImmutableSet<E> result = construct(size, contents);
      // construct has the side effect of deduping contents, so we update size
      // accordingly.
      size = result.size();
      return result;
    }
  }
}
  • ImmutableCollection.ArrayBasedBuilder这个是一个真正的实现类
    继承了ImmutableCollection.Builder,这里抽象了很多的方法,而且统一实现了扩容的方法,一会慢慢的讲解

  • Object[] contents这个是重点,容器,一组数据的容器,向这个容器中增加数据信息,或者合并集合的数据信息等等,都是在这里统一实习,对于不同的具体的类的信息,如何构造出不可变的集合那个是他们自己的事情了,我们只需要将当前的object数组传递过去就好了,任务完成。

  • 这里做的扩容检查哈哈,防止数组超过大小,感觉还不错哦!
abstract static class ArrayBasedBuilder<E>
     extends ImmutableCollection.Builder<E> {
    Object[] contents;//容器哦!
    int size;//当前容器使用的大小!
    ArrayBasedBuilder(int initialCapacity) {
      //CollectPreconditions 中的检测不为负数
      checkNonnegative(initialCapacity, "initialCapacity");
      this.contents = new Object[initialCapacity];
      this.size = 0;
    }

    /**
     * Expand the absolute capacity of the builder 
     * so it can accept at least
     * the specified number of elements without being resized.
     * 每增加一个元素就去检测当前的容器的大小,然后在进行扩容
     * expandedCapacity是父类扩容的方法,一会会说
     * Arrays.copyOf复制一个数据,扩展它的长度
     */
    private void ensureCapacity(int minCapacity) {
      if (contents.length < minCapacity) {
        this.contents =
            Arrays.copyOf(
                this.contents, 
                expandedCapacity(contents.length, minCapacity)
                );
      }
    }
    /**
    *这里只是添加元素,对于底层Set的数据构造,是由他们自己去搞定
    */
    @Override
    public ArrayBasedBuilder<E> add(E element) {
      checkNotNull(element);//非空判断
      ensureCapacity(size + 1);//扩容
      contents[size++] = element;//插入数据
      return this;
    }
   /*
    *static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 
    *从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。 
    */
    @Override
    public Builder<E> add(E... elements) {
      checkElementsNotNull(elements);
      ensureCapacity(size + elements.length);
      System.arraycopy(elements, 0, contents, size, elements.length);
      size += elements.length;
      return this;
    }
     /*
     *Iterable抵代器都是基本上Collection
     */
    @Override
    public Builder<E> addAll(Iterable<? extends E> elements) {
      if (elements instanceof Collection) {
        Collection<?> collection = (Collection<?>) elements;
        ensureCapacity(size + collection.size());
      }
      super.addAll(elements);
      return this;
    }
    //两个builder和并啊
    @CanIgnoreReturnValue
    ArrayBasedBuilder<E> combine(ArrayBasedBuilder<E> builder) {
      checkNotNull(builder);
      ensureCapacity(size + builder.size);
      System.arraycopy(builder.contents, 0, this.contents, size, builder.size);
      size += builder.size;
      return this;
    }
  }
}
  • ImmutableCollection.Builder 这个是个抽象类,是所有的建筑者模式的父类哦,这里实现了扩容性的检测,添加集合,添加迭代等等,添加单个元素是个抽象,使用模板方法进行处理,父类调用子类的方法,这里对于扩容的处理进行了实现,和JDK中anrrylist处理有点差不多。
  • 扩容的处理主要的实现步骤
  • 将当前的空间的大小扩大3倍+1
  • 和当前的最小的空间大小比较
  • 如果扩容3倍还小,那么使用当前的传入的最小的空间,也就是当前的数组的长度的需要容纳的量的最高位之后全部填充为1后的值
/**
   * Abstract base class for builders of 
   * ImmutableCollectiontypes.
   */
  public abstract static class Builder<E> {
    static final int DEFAULT_INITIAL_CAPACITY = 4;
    //默认的数组的大小
    //如下是扩容的具体的做法,非常的精妙,一会在分析
    //Integer.highestOneBit 
    static int expandedCapacity(int oldCapacity, int minCapacity) {
      if (minCapacity < 0) {
        throw new AssertionError("cannot store more than MAX_VALUE elements");
      }
      // careful of overflow!
      //这里是扩大容量扩大
      int newCapacity = oldCapacity + (oldCapacity >> 1) + 1;
      if (newCapacity < minCapacity) {
        //highestOneBit取最高位的值,-1也就是相当于右移一位
        //比如 1101->1000(highestOneBit)->0111(-1)->1111(<<1)
        //这样处理的好处因为minCapacity是大于0的,在当前位肯定不会超出31位
        //这样就可以把当前位全部填充为1,这个是最大的值了。
        newCapacity = Integer.highestOneBit(minCapacity - 1) << 1;
      }
      //这里的处理感觉没必要了,不会超位的
      if (newCapacity < 0) {
        newCapacity = Integer.MAX_VALUE;
        // guaranteed to be >= newCapacity
      }
      return newCapacity;
    }

    Builder() {}
    //模板方法,留给子类自己去实现
    public abstract Builder<E> add(E element);
    public Builder<E> add(E... elements) {
      for (E element : elements) {
        add(element);
      }
      return this;
    }
    public Builder<E> addAll(Iterable<? extends E> elements) {
      for (E element : elements) {
        add(element);
      }
      return this;
    }

    @CanIgnoreReturnValue
    public Builder<E> addAll(Iterator<? extends E> elements) {
      while (elements.hasNext()) {
        add(elements.next());
      }
      return this;
    }

    /**
     *这里的构造才是最后最重要的部门哦,最后被ImmutableSet构造,你可以回去看看逻辑 ImmutableSet<E> result = construct(size, contents);
     * Returns a newly-created 
     *{@code ImmutableCollection} of the appropriate
     * type, containing the elements provided to this builder.
     *
     * <p>Note that each builder class covariantly returns the appropriate type
     * of {@code ImmutableCollection} from this method.
     */
    public abstract ImmutableCollection<E> build();
  }

Integer.highestOneBit

在了解这个之前,笔者也想先去了解计算机组成原理的一些简单知识哈哈,不经常用容易忘掉细节的东西,回顾一下。

原码补码反码

正数原码反码补码一样,补码(负数的二进制)=反码+1

原码

  1. 左边的第一位表示符号(0为正,1为负), 其余位表示数值.
  2. 真值变成原码的转换方法:
  3. 第一:取真值的绝对值的2进制表示
  4. 第二:左边第一位添加符号。
  5. 例如:
    考虑一个字节的存储,-127,绝对值为127的2进制表示为0111 1111
    添加符号(1)为 1111 1111。
    当真值=0的时候,[+0]原可以表示成 0000 0000, [-0]原可以表示成 1000 0000。

反码

反码的表示方法是:
1. 如果是正数,反码与原码一样。
2. 如果是负数,反码是符号位不变,原码其余各个位取反.
3. 例子:
[+127]原=0111 1111,
[+127]反=0111 1111,
[-127]原=1111 1111,
[-127]反=1000 0000。

补码表示方法:

  1. 如果是正数, 补码与原码一样。
  2. 如果是负数,在反码的基础上+1。
  3. 补码(负数的二进制)=反码+1
  4. 例子:
    [+127]原=0111 1111,
    [+127]反=0111 1111,
    [+127]补=0111 1111
    [-127]原=1111 1111,
    [-127]反=1000 0000,
    [-127]补=1000 0001

无符号位移(>>>、<<<) 有符号位移(>>、<<)

移位总结

  1. 有符号左移时低位补0
  2. 有符号右移时,若符号为正则高位补0,反之补1;
  3. 无符号右移操作符无论正负都在高位补0.
    例子: 15

    • 15的二进制
      00000000 00000000 00000000 00001111

    • -15的二进制
      11111111 11111111 11111111 11110001
      计算过程:补码(负数的二进制)=反码+1
      原码:10000000 00000000 00000000 00001111
      反码:11111111 11111111 11111111 11110000
      补码(即加1):11111111 11111111 11111111 11110001
      也就是-15的二进制

正数移位

  • 无符号位移 15>>>2
    二进制:00000000 00000000 00000000 00001111
    移动后:00000000 00000000 00000000 00000011 (11 舍弃)缩小四倍

  • 有符号位移15>>2
    二进制:00000000 00000000 00000000 00001111
    移动后:00000000 00000000 00000000 00000011 (11 舍弃)同(>>>)

负数移位

  • 无符号位移 -15>>>2
    二进制:11111111 11111111 11111111 11110001
    移动后:00111111 11111111 11111111 11111100 (01舍弃)
    *无符号右移操作符无论正负都在高位补0

  • 有符号位位移 -15>>2
    二进制:11111111 11111111 11111111 11110001
    移动后:11111111 11111111 11111111 11111100 (01舍弃)
    有符号右移时,若符号为正则高位补0,反之补1;
    求这个移动后的值:
    补码(负数的二进制)=反码+1
    补码的 11111111 11111111 11111111 11111100
    先减1:11111111 11111111 11111111 11111011 反码结果
    原码: 10000000 00000000 00000000 00000100 (此结果为-4)
    负数反码转原码:符号为不变,其他取反

Integer.highestOneBit源码

作用:将一个整数(二进制)设置最高位为1,其它位为0,然后返回改变后的值,如果这个整数是0返回0

移位操作的精妙之处,慢慢体会吧,有符号和无符号不一样的哦!

public static int highestOneBit(int i) {
    // 例如1000
    i |= (i >>  1); 
    // 使前2位变为1,相当于i = i | (i >> 1); 
    //i = 1000 | 0100 = 1100
    i |= (i >>  2); 
    // 使前4位变为1,由于上一步确保了前两位都是1,\
    //所以这一次移动两位,1111
    i |= (i >>  4); // 使前8位变为1,1111
    i |= (i >>  8); // 使前16位变为1,1111
    i |= (i >> 16); // 使前32位变为1,1111
    return i - (i >>> 1); 
    // i >>> 1 无符号右移,使最高位为0,其余位为1
    //相减即得出结果,1111 - 0111 = 1000 
}

今天的收获如上,清明节….

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值