我是靠谱客的博主 大意白羊,这篇文章主要介绍Guava ImmutableSet.Builder源码分析,移位原码补码反码复习,现在分享给大家,希望可以做个参考。

建筑者模式的继承结构

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

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

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

  • 基本上的代码都是继承父类的方法去实现的,什么扩容,添加元素等等,这里还是用了模板方法,让父类调用子类的实现。addAll(…)
  • 我很喜欢这种链式的书写风格,感觉非常的不错。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/** * 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数组传递过去就好了,任务完成。

  • 这里做的扩容检查哈哈,防止数组超过大小,感觉还不错哦!
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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后的值
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/** * 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

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 ImmutableSet.Builder源码分析,移位原码补码反码复习的全部内容,更多相关Guava内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部