Guava ImmutableSet.Builder源码分析,移位原码补码反码复习
2017-04-02 16:07
483 查看
建筑者模式的继承结构
建筑者模式对于构建非常的爽,这种写法也是比较的喜欢的,看看这里处理的继承体系吧使用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原码
左边的第一位表示符号(0为正,1为负), 其余位表示数值.真值变成原码的转换方法:
第一:取真值的绝对值的2进制表示
第二:左边第一位添加符号。
例如:
考虑一个字节的存储,-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。
补码(负数的二进制)=反码+1
例子:
[+127]原=0111 1111,
[+127]反=0111 1111,
[+127]补=0111 1111
[-127]原=1111 1111,
[-127]反=1000 0000,
[-127]补=1000 0001
无符号位移(>>>、<<<) 有符号位移(>>、<<)
移位总结
有符号左移时低位补0有符号右移时,若符号为正则高位补0,反之补1;
无符号右移操作符无论正负都在高位补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 }
今天的收获如上,清明节….
相关文章推荐
- 原码、反码与补码的问题分析
- 原码、反码、补码、负数的移位
- 原码、反码、补码的细节分析
- 原码补码反码——复习
- 原码, 反码和补码分析
- 原码反码补码以及移位按位与或异或等位操作的实例
- 原码,反码,补码 及 移位运算
- 关于原码,反码,补码和左右移位的若干思考
- 关于原码,反码,补码和左右移位的若干思考
- Guava缓存器源码分析——CacheBuilderSpec
- 正负数二进制表示,正负数二进制移位运算、二进制源码、反码、补码
- java 二进制(原码 反码 补码),位运算,移位运算,约瑟夫问题
- 关于原码,反码,补码和左右移位的若干思考
- java 二进制(原码 反码 补码),位运算,移位运算,约瑟夫问题
- JAVA:二进制(原码 反码 补码),位运算,移位运算,约瑟夫问题(5)
- 补码、原码、反码-移位操作 编解码
- 计算机中的原码、反码、补码分析
- 关于原码, 反码, 补码的复习