您的位置:首页 > 产品设计 > UI/UE

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
}


今天的收获如上,清明节….
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: