为什么ImmutableSet是不可变类,ImmutableSet源码去重处理,Hash处理
关于
- 不可变类具备了啥子
- Immutable创建的过程,如何使用Hash
- 一些基本的方法的说明
- 构建一个和多个分开起来,估计是为了做Hash的分开处理吧!
从创建开始说起
of(E element)
- 从这个函数开始说起,of有很多的重载函数,我们就从简单的创建说起,一步步的跟踪看看这个函数到底为啥会是不可变类。
public static <E> ImmutableSet<E> of(E element) {
return new SingletonImmutableSet<E>(element);
}
- SingletonImmutableSet创建一个单一的不可变的Set
- 当前类中保存的元素是不可变的,而且返回出去的
- UnmodifiableIterator 和ImmutableList 同样的是不可变的
- 对外部返回值没有引用哦,final的Element,但是要保证E这个类型是不可变的哦
- final 的对象还是可以改变其内部的值哦!
final class SingletonImmutableSet<E> extends ImmutableSet<E> {
final transient E element;
@LazyInit
private transient int cachedHashCode;
SingletonImmutableSet(E element) {
this.element = Preconditions.checkNotNull(element);
}
@Override
public int size() {
return 1;
}
@Override
public UnmodifiableIterator<E> iterator() {
return Iterators.singletonIterator(element);
}
@Override
ImmutableList<E> createAsList() {
return ImmutableList.of(element);
}
@Override
public final int hashCode() {
// Racy single-check.
int code = cachedHashCode;
if (code == 0) {
cachedHashCode = code = element.hashCode();
}
return code;
}
}
- Iterators.singletonIterator(element) 返回一个
- UnmodifiableIterator< E> 看看这个到底是什么鬼
- 保证数据不可以被删除!
@GwtCompatible
public abstract class UnmodifiableIterator<E> implements Iterator<E> {
/** Constructor for use by subclasses. */
protected UnmodifiableIterator() {}
/**
* Guaranteed to throw an exception and leave the
* underlying data unmodified.
* @throws UnsupportedOperationException always
*/
@Deprecated
@Override
public final void remove() {
throw new UnsupportedOperationException();
}
}
- Iterators.singletonIterator(element) 这个匿名类也没做啥子!哈哈,返回一个不可变的final的value。不是真正意义上的不可变这个需要使用者保证传入的对象是个不可变的类型才可以很保证。
public static <T> UnmodifiableIterator<T> singletonIterator(@Nullable final T value) {
return new UnmodifiableIterator<T>() {
boolean done;
@Override
public boolean hasNext() {
return !done;
}
@Override
public T next() {
if (done) {
throw new NoSuchElementException();
}
done = true;
return value;
}
};
}
ImmutableSet< E> of这样的重载了很多2个元素,3个元素…
- 返回一个不可变的集合,其中包含给定的元素,减去重复,按顺序分别为首次指定。
- 还记得建筑者模式的时候,最后build的时候也是调用的这个方法啊!将最后的object数组传入进去进行处理。
/**
* Returns an immutable set containing the given elements, minus duplicates, *in the order each was first specified.
*/
public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4) {
return construct(4, e1, e2, e3, e4);
}
construct 构建函数
- Set是通过Hash表进行构建的,非常的快速高效,一起看看源码
private static <E> ImmutableSet<E> construct(int n, Object... elements) {
.....
//只有一个或者两个的前面处理了...
//这里是set通过hash寻址进行处理,非常的高效
//寻找元素几乎为O(1),这个方法去重复值也是不错哦!
int tableSize = chooseTableSize(n);
//这个是hash使用的table
Object[] table = new Object[tableSize];
//这个是和平常取模一样的,这里使用的 &来处理,计算机很高效
int mask = tableSize - 1;
int hashCode = 0;//计算Hash的总和
int uniques = 0;
//标志去重后数组的大小
for (int i = 0; i < n; i++) {
Object element = checkElementNotNull(elements[i], i);
int hash = element.hashCode();
//MurmurHash3 获取hash的值,比较的快速的方法
for (int j = Hashing.smear(hash); ; j++) {
int index = j & mask;
Object value = table[index];
//判断当前的洞有人没得,没得占坑成功
if (value == null) {
// Came to an empty slot. Put the element here.
//保存的数据还是在原来的空间
elements[uniques++] = element;
//标志当前位置有人了
table[index] = element;
hashCode += hash;
break;
} else if (value.equals(element)) {
//存在了这个元素,所以返回
break;
}
}
}
//这个方法是为了将后面的不要的值全部设置为null
//方便后面去查找的时候Hash是否存在
Arrays.fill(elements, uniques, n, null);
if (uniques == 1) {
E element = (E) elements[0];
return new SingletonImmutableSet<E>(element, hashCode);
} else if (tableSize != chooseTableSize(uniques)) {
// Resize the table when the array includes
//too many duplicates.
// when this happens, we have already made a copy
//重新构造hashtable的大小,寻找减少负担!
return construct(uniques, elements);
} else {
Object[] uniqueElements =
(uniques < elements.length) ?
Arrays.copyOf(elements, uniques) : elements;
//复制元素,相等就不复制了吧!
//下面这里将当前的hashtable,hashcode,mask,
//和唯一的数组构建一个ImmutableSet的子类
return new RegularImmutableSet<E>(uniqueElements,
hashCode, table, mask);
}
}
- 计算装载因子,这个还是以前在数据结构中看到过
- 装载因子的作用就是减少寻找坑的次数!
//DESIRED_LOAD_FACTOR 装载因子 0.7
//CUTOFF最大的
//最高位highestOneBit其他为0
//这里就是要达到装载因子大于setSize
//根据评估装载因子在这里重新hash最少的代价
static int chooseTableSize(int setSize) {
// Correct the size for open
//addressing to match desired load factor.
if (setSize < CUTOFF) {
// Round up to the next highest power of 2.
int tableSize = Integer.highestOneBit(setSize - 1) << 1;
while (tableSize * DESIRED_LOAD_FACTOR < setSize) {
tableSize <<= 1;
}
return tableSize;
}
(正规的处理)RegularImmutableSet 创建真正的Set以前的都是处理基本的工作
ImmutableSet.Indexed这个类处理只有一个元素的,还处理了一些基本的操作
对于ImmutableSet。
final class RegularImmutableSet<E> extends ImmutableSet.Indexed<E> {
//空
static final RegularImmutableSet<Object>
EMPTY = new RegularImmutableSet<Object>
(ObjectArrays.EMPTY_ARRAY,
0,
null,
0);
//看清楚了额,私用的,哈哈!
private final transient Object[] elements;
@VisibleForTesting final transient Object[] table;
private final transient int mask;
private final transient int hashCode;
RegularImmutableSet(Object[] elements, int hashCode, Object[] table, int mask) {
this.elements = elements;
this.table = table;
this.mask = mask;
this.hashCode = hashCode;
}
//这里就是Table表的作用哦!make
//这里差不多接近o(1)
@Override
public boolean contains(@Nullable Object target) {
Object[] table = this.table;
if (target == null || table == null) {
return false;
}
for (int i = Hashing.smearedHash(target); ; i++) {
i &= mask;
Object candidate = table[i];
if (candidate == null) {
return false;
} else if (candidate.equals(target)) {
return true;
}
}
}
//这里就是需要传递elements的作用
@Override
public int size() {
return elements.length;
}
@Override E get(int i) {
return (E) elements[i];
}
//这个方法是私有的哦,给父类调用的!
@Override
ImmutableList<E> createAsList() {
return (table == null) ? ImmutableList.<E>of()
:new RegularImmutableAsList<E>(this, elements);
}
@Override
public int hashCode() {
return hashCode;
}
}
ImmutableSet.asList
- 延迟初始化的ImmutableList asList
@LazyInit
@RetainedWith
private transient ImmutableList<E> asList;
@Override
public ImmutableList<E> asList() {
ImmutableList<E> result = asList;
//懂了吧!这个也是一个模板方法,留给子类自己去实现
return (result == null) ?
asList = createAsList() : result;
}
- 一个Map怎么和一个List打交道?
- 诶,为啥这里需要搞个代理类?直接使用不好?这个我不清楚啦!
- 不知道你看到没有,都是私有的数据,不会泄露出去的!安装可靠的!
class RegularImmutableAsList<E> extends ImmutableAsList<E> {
private final ImmutableCollection<E> delegate;
private final ImmutableList< ? extends E> delegateList;
RegularImmutableAsList(ImmutableCollection<E> delegate, ImmutableList< ? extends E> delegateList) {
this.delegate = delegate;
this.delegateList = delegateList;
}
RegularImmutableAsList(ImmutableCollection<E> delegate, Object[] array) {
this(delegate, ImmutableList.<E>asImmutableList(array));
}
@Override
ImmutableCollection<E> delegateCollection() {
return delegate;
}
ImmutableList<? extends E> delegateList() {
return delegateList;
}
@Override
public UnmodifiableListIterator<E> listIterator(int index) {
return (UnmodifiableListIterator<E>) delegateList.listIterator(index);
}
@Override
public void forEach(Consumer<? super E> action) {
delegateList.forEach(action);
}
@Override
int copyIntoArray(Object[] dst, int offset) {
return delegateList.copyIntoArray(dst, offset);
}
@Override
public E get(int index) {
return delegateList.get(index);
}
}
总结
看了整个源码,只能说别人构造的不错,参考了很多的JDK的实现哈哈!不过代码的质量确实高的可怕!
看到了怎么使用Hash进行处理,整个比较的厉害啦!