LightWeightGSet是名字节点NameNode在内存中存储全部数据块信息的类BlocksMap需要的一个重要数据结构,它是一个占用较低内存的集合的实现,它使用一个数组array存储元素,使用linked lists来解决冲突。它没有实现重新哈希分区,所以,内部的array不会改变大小。这个类不支持null元素,并且不是线程安全的。它在BlocksMap中的初始化如下:
this.blocks = new LightWeightGSet<Block, BlockInfo>(capacity)// ...省略部分代码
可见,它类似一个Key、Value集合,Key为BLock对象,Value为BlockInfo对象。
那么,对于LightWeightGSet的上述介绍该如何解释呢?既然看上去像一个Key、Value集合,那么它到底是不是一个Key、Value集合呢?而且,为什么说它占内存比较低,使用数组存储元素,又用linked lists来解决冲突呢?相信当你看完LightWeightGSet的实现,就会一目了然。我们先看下LightWeightGSet中最重要的一个成员变量,如下:
/**
* An internal array of entries, which are the rows of the hash table.
* The size must be a power of two.
* 存储元素条目的内部数组,它是哈希表中的行。
* 数组大小必须是2的幂。
* 数组元素实现了LinkedElement接口。
*/
private final LinkedElement[] entries;
entries是一个存储实现了LinkedElement接口对象的数组,实际上存储的是BlockInfo实例。它是LightWeightGSet存储元素条目的内部数组,数组中元素是哈希表中的一行,且数组的大小必须为2的幂。
先了解上述信息就行,我们再往下看,既然是一个集合,就得能够实现存取元素,LightWeightGSet肯定得对外提供了元素存取的方法,先看存,通过put()方法实现,代码如下:
@Override
public E put(final E element) {
//validate element
// 检验元素element
// 不支持null元素
if (element == null) {
throw new NullPointerException("Null element is not supported.");
}
// 元素必须实现LinkedElement接口
if (!(element instanceof LinkedElement)) {
throw new HadoopIllegalArgumentException(
"!(element instanceof LinkedElement), element.getClass()="
+ element.getClass());
}
// 将元素element强制转换成LinkedElement类型实例e
final LinkedElement e = (LinkedElement)element;
//find index
// 获取元素对应索引
// 实际上是根据block的hashCode和hash_mask的一种循环取余算法
// blockID是一个递增的序列,它在数组内的index也是在数组长度范围内递增的
final int index = getIndex(element);
//remove if it already exists
// 如果元素已经存在的话,移除
final E existing = remove(index, element);
//insert the element to the head of the linked list
// 将元素element插入到linked列表的头部
// 累加modification、size
modification++;
size++;
// 元素设置
// 将e的next元素设置为数组当前index位置的元素
e.setNext(entries[index]);
// 将e的next元素设置为数组当前index位置的元素
entries[index] = e;
// 返回之前存储的元素existing
return existing;
}
put()方法实现了LightWeightGSet存数据的功能,它接收一个E泛型element作为参数,实现逻辑如下:
1、首先校验参数,即需要存储的元素element,它必须满足不能为null和必须实现LinkedElement接口两个限制条件;
2、然后将元素element强制转换成LinkedElement类型实例e;
3、调用getIndex()方法根据元素element获取它在数组entries中对应的位置索引index;
4、如果元素存在的话,调用remove()方法,根据位置索引index和元素remove进行移除操作,并得到有可能之前存储的元素existing;
5、累加modification、size:
modification代表了数据修改量,无论增加还是删除,均会累加;
size代表了集合元素个数,增加元素时即累加,删除元素时即累减;
6、进行元素设置:
6.1、将e的next元素设置为数组当前index位置的元素,可能为null,也可能为之前存在的不等元素,但肯定不是和需要添加元素相等的元素,因为如果存在,上面就已经删除了;
6.2、将当前元素e设置为数组当前index位置的元素;
7、将当前元素e设置为数组当前index位置的元素。
通过上述添加元素过程的逻辑介绍,你是不是能体会到以下这点呢:
LightWeightGSet在内存中本质上是一个数组entries,用于存储实现了LinkedElement接口的元素。当添加元素element时,我们能够根据待添加元素element计算出它在数组entries中的位置索引index,然后根据位置索引index和元素element删除之前可能存在的相等元素,然后再进行元素设置,将数组entries中当前位置索引index处的元素设置为待添加元素element的next元素,而将待添加元素element放置到数组entries中的位置索引index处。
看到这里,你是不是恍然大悟,是不是能感受到LightWeightGSet中的数组中每个位置存储的好像是一个列表,而不是单一的一个元素?如果你能体会到这点,你就能开始领会到LightWeightGSet的真谛了。而且,我们已经能够开始回答上面我们遗留的使用一个数组array存储元素,使用linked lists来解决冲突这个疑问了。