新增子模块与文件迁移
This commit is contained in:
116
source/src/Utils/Collections/FastList.ts
Normal file
116
source/src/Utils/Collections/FastList.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
module es {
|
||||
/**
|
||||
* 围绕一个数组的非常基本的包装,当它达到容量时自动扩展。
|
||||
* 注意,在迭代时应该这样直接访问缓冲区,但使用FastList.length字段。
|
||||
*
|
||||
* @tutorial
|
||||
* for( var i = 0; i <= list.length; i++ )
|
||||
* var item = list.buffer[i];
|
||||
*/
|
||||
export class FastList<T> {
|
||||
/**
|
||||
* 直接访问后备缓冲区。
|
||||
* 不要使用buffer.Length! 使用FastList.length
|
||||
*/
|
||||
public buffer: T[];
|
||||
|
||||
/**
|
||||
* 直接访问缓冲区内填充项的长度。不要改变。
|
||||
*/
|
||||
public length: number = 0;
|
||||
|
||||
constructor(size: number = 5) {
|
||||
this.buffer = new Array(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空列表并清空缓冲区中的所有项目
|
||||
*/
|
||||
public clear() {
|
||||
this.buffer.length = 0;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 和clear的工作原理一样,只是它不会将缓冲区中的所有项目清空。
|
||||
*/
|
||||
public reset() {
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将该项目添加到列表中
|
||||
* @param item
|
||||
*/
|
||||
public add(item: T) {
|
||||
if (this.length == this.buffer.length)
|
||||
this.buffer.length = Math.max(this.buffer.length << 1, 10);
|
||||
this.buffer[this.length++] = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中删除该项目
|
||||
* @param item
|
||||
*/
|
||||
public remove(item: T){
|
||||
let comp = EqualityComparer.default<T>();
|
||||
for (let i = 0; i < this.length; ++i){
|
||||
if (comp.equals(this.buffer[i], item)){
|
||||
this.removeAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中删除给定索引的项目。
|
||||
* @param index
|
||||
*/
|
||||
public removeAt(index: number){
|
||||
if (index >= this.length)
|
||||
throw new Error("index超出范围!");
|
||||
|
||||
this.length --;
|
||||
new linq.List(this.buffer).removeAt(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查项目是否在FastList中
|
||||
* @param item
|
||||
*/
|
||||
public contains(item: T){
|
||||
let comp = EqualityComparer.default<T>();
|
||||
for (let i = 0; i < this.length; ++ i){
|
||||
if (comp.equals(this.buffer[i], item))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果缓冲区达到最大,将分配更多的空间来容纳额外的ItemCount。
|
||||
* @param additionalItemCount
|
||||
*/
|
||||
public ensureCapacity(additionalItemCount: number = 1){
|
||||
if (this.length + additionalItemCount >= this.buffer.length)
|
||||
this.buffer.length = Math.max(this.buffer.length << 1, this.length + additionalItemCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数组中的所有项目
|
||||
* @param array
|
||||
*/
|
||||
public addRange(array: T[]){
|
||||
for (let item of array)
|
||||
this.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对缓冲区中的所有项目进行排序,长度不限。
|
||||
*/
|
||||
public sort(comparer: IComparer<T>){
|
||||
this.buffer.sort(comparer.compare);
|
||||
}
|
||||
}
|
||||
}
|
||||
317
source/src/Utils/Collections/FasterDictionary.ts
Normal file
317
source/src/Utils/Collections/FasterDictionary.ts
Normal file
@@ -0,0 +1,317 @@
|
||||
module es {
|
||||
/**
|
||||
* 创建这个字典的原因只有一个:
|
||||
* 我需要一个能让我直接以数组的形式对值进行迭代的字典,而不需要生成一个数组或使用迭代器。
|
||||
* 对于这个目标是比标准字典快N倍。
|
||||
* Faster dictionary在大部分操作上也比标准字典快,但差别可以忽略不计。
|
||||
* 唯一较慢的操作是在添加时调整内存大小,因为与标准数组相比,这个实现需要使用两个单独的数组。
|
||||
*/
|
||||
export class FasterDictionary<TKey, TValue> {
|
||||
public _values: TValue[];
|
||||
public _valuesInfo: FastNode[];
|
||||
public _buckets: number[];
|
||||
public _freeValueCellIndex: number = 0;
|
||||
public _collisions: number = 0;
|
||||
|
||||
constructor(size: number = 1) {
|
||||
this._valuesInfo = new Array(size);
|
||||
this._values = new Array(size);
|
||||
this._buckets = new Array(HashHelpers.getPrime(size));
|
||||
}
|
||||
|
||||
public getValuesArray(count: {value: number}): TValue[] {
|
||||
count.value = this._freeValueCellIndex;
|
||||
|
||||
return this._values;
|
||||
}
|
||||
|
||||
public get valuesArray(): TValue[] {
|
||||
return this._values;
|
||||
}
|
||||
|
||||
public get count(): number {
|
||||
return this._freeValueCellIndex;
|
||||
}
|
||||
|
||||
public add(key: TKey, value: TValue) {
|
||||
if (!this.addValue(key, value, {value: 0}))
|
||||
throw new Error("key 已经存在")
|
||||
}
|
||||
|
||||
public addValue(key: TKey, value: TValue, indexSet: {value: number}) {
|
||||
let hash = HashHelpers.getHashCode(key);
|
||||
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
|
||||
|
||||
if (this._freeValueCellIndex == this._values.length) {
|
||||
let expandPrime = HashHelpers.expandPrime(this._freeValueCellIndex);
|
||||
|
||||
this._values.length = expandPrime;
|
||||
this._valuesInfo.length = expandPrime;
|
||||
}
|
||||
|
||||
// buckets值-1表示它是空的
|
||||
let valueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
|
||||
|
||||
if (valueIndex == -1) {
|
||||
// 在最后一个位置创建信息节点,并填入相关信息
|
||||
this._valuesInfo[this._freeValueCellIndex] = new FastNode(key, hash);
|
||||
} else {
|
||||
{
|
||||
let currentValueIndex = valueIndex;
|
||||
do {
|
||||
// 必须检查键是否已经存在于字典中
|
||||
if (this._valuesInfo[currentValueIndex].hashcode == hash &&
|
||||
this._valuesInfo[currentValueIndex].key == key) {
|
||||
// 键已经存在,只需将其值替换掉即可
|
||||
this._values[currentValueIndex] = value;
|
||||
indexSet.value = currentValueIndex;
|
||||
return false;
|
||||
}
|
||||
|
||||
currentValueIndex = this._valuesInfo[currentValueIndex].previous;
|
||||
}
|
||||
while (currentValueIndex != -1); // -1表示没有更多的值与相同的哈希值的键
|
||||
}
|
||||
|
||||
this._collisions++;
|
||||
// 创建一个新的节点,该节点之前的索引指向当前指向桶的节点
|
||||
this._valuesInfo[this._freeValueCellIndex] = new FastNode(key, hash, valueIndex);
|
||||
// 更新现有单元格的下一个单元格指向新的单元格,旧的单元格 -> 新的单元格 -> 旧的单元格 <- 下一个单元格
|
||||
this._valuesInfo[valueIndex].next = this._freeValueCellIndex;
|
||||
}
|
||||
// 重要的是:新的节点总是被桶单元格指向的那个节点,所以我可以假设被桶指向的那个节点总是最后添加的值(next = -1)
|
||||
// item与这个bucketIndex将指向最后创建的值
|
||||
// TODO: 如果相反,我假设原来的那个是bucket中的那个,我就不需要在这里更新bucket了
|
||||
this._buckets[bucketIndex] = (this._freeValueCellIndex + 1);
|
||||
|
||||
this._values[this._freeValueCellIndex] = value;
|
||||
indexSet.value = this._freeValueCellIndex;
|
||||
|
||||
this._freeValueCellIndex++;
|
||||
|
||||
if (this._collisions > this._buckets.length) {
|
||||
// 我们需要更多的空间和更少的碰撞
|
||||
this._buckets = new Array(HashHelpers.expandPrime(this._collisions));
|
||||
this._collisions = 0;
|
||||
// 我们需要得到目前存储的所有值的哈希码,并将它们分布在新的桶长上
|
||||
for (let newValueIndex = 0; newValueIndex < this._freeValueCellIndex; newValueIndex++) {
|
||||
// 获取原始哈希码,并根据新的长度找到新的bucketIndex
|
||||
bucketIndex = FasterDictionary.reduce(this._valuesInfo[newValueIndex].hashcode, this._buckets.length);
|
||||
// bucketsIndex可以是-1或下一个值。
|
||||
// 如果是-1意味着没有碰撞。
|
||||
// 如果有碰撞,我们创建一个新节点,它的上一个指向旧节点。
|
||||
// 旧节点指向新节点,新节点指向旧节点,旧节点指向新节点,现在bucket指向新节点,这样我们就可以重建linkedlist.
|
||||
// 获取当前值Index,如果没有碰撞,则为-1。
|
||||
let existingValueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
|
||||
// 将bucket索引更新为共享bucketIndex的当前项目的索引(最后找到的总是bucket中的那个)
|
||||
this._buckets[bucketIndex] = newValueIndex + 1;
|
||||
if (existingValueIndex != -1) {
|
||||
// 这个单元格已经指向了新的bucket list中的一个值,这意味着有一个碰撞,出了问题
|
||||
this._collisions++;
|
||||
// bucket将指向这个值,所以新的值将使用以前的索引
|
||||
this._valuesInfo[newValueIndex].previous = existingValueIndex;
|
||||
this._valuesInfo[newValueIndex].next = -1;
|
||||
// 并将之前的下一个索引更新为新的索引
|
||||
this._valuesInfo[existingValueIndex].next = newValueIndex;
|
||||
} else {
|
||||
// 什么都没有被索引,桶是空的。我们需要更新之前的 next 和 previous 的值。
|
||||
this._valuesInfo[newValueIndex].next = -1;
|
||||
this._valuesInfo[newValueIndex].previous = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public remove(key: TKey): boolean {
|
||||
let hash = FasterDictionary.hash(key);
|
||||
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
|
||||
|
||||
// 找桶
|
||||
let indexToValueToRemove = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
|
||||
|
||||
// 第一部分:在bucket list中寻找实际的键,如果找到了,我就更新bucket list,使它不再指向要删除的单元格。
|
||||
while (indexToValueToRemove != -1) {
|
||||
if (this._valuesInfo[indexToValueToRemove].hashcode == hash &&
|
||||
this._valuesInfo[indexToValueToRemove].key == key) {
|
||||
// 如果找到了密钥,并且桶直接指向了要删除的节点
|
||||
if (this._buckets[bucketIndex] - 1 == indexToValueToRemove){
|
||||
if (this._valuesInfo[indexToValueToRemove].next != -1)
|
||||
throw new Error("如果 bucket 指向单元格,那么 next 必须不存在。");
|
||||
|
||||
// 如果前一个单元格存在,它的下一个指针必须被更新!
|
||||
//<---迭代顺序
|
||||
// B(ucket总是指向最后一个)
|
||||
// ------- ------- -------
|
||||
// 1 | | | | 2 | | | 3 | //bucket不能有下一个,只能有上一个。
|
||||
// ------- ------- -------
|
||||
//--> 插入
|
||||
let value = this._valuesInfo[indexToValueToRemove].previous;
|
||||
this._buckets[bucketIndex] = value + 1;
|
||||
}else{
|
||||
if (this._valuesInfo[indexToValueToRemove].next == -1)
|
||||
throw new Error("如果 bucket 指向另一个单元格,则 NEXT 必须存在");
|
||||
}
|
||||
|
||||
FasterDictionary.updateLinkedList(indexToValueToRemove, this._valuesInfo);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
indexToValueToRemove = this._valuesInfo[indexToValueToRemove].previous;
|
||||
}
|
||||
|
||||
if (indexToValueToRemove == -1)
|
||||
return false; // 未找到
|
||||
|
||||
this._freeValueCellIndex --; // 少了一个需要反复计算的值
|
||||
|
||||
// 第二部分
|
||||
// 这时节点指针和水桶会被更新,但_values数组会被更新仍然有要删除的值
|
||||
// 这个字典的目标是能够做到像数组一样对数值进行迭代,所以数值数组必须始终是最新的
|
||||
|
||||
// 如果要删除的单元格是列表中的最后一个,我们可以执行较少的操作(不需要交换),否则我们要将最后一个值的单元格移到要删除的值上。
|
||||
if (indexToValueToRemove != this._freeValueCellIndex){
|
||||
// 我们可以将两个数组的最后一个值移到要删除的数组中。
|
||||
// 为了做到这一点,我们需要确保 bucket 指针已经更新了
|
||||
// 首先我们在桶列表中找到指向要移动的单元格的指针的索引
|
||||
let movingBucketIndex = FasterDictionary.reduce(this._valuesInfo[this._freeValueCellIndex].hashcode, this._buckets.length);
|
||||
|
||||
// 如果找到了键,并且桶直接指向要删除的节点,现在必须指向要移动的单元格。
|
||||
if (this._buckets[movingBucketIndex] - 1 == this._freeValueCellIndex)
|
||||
this._buckets[movingBucketIndex] = (indexToValueToRemove + 1);
|
||||
|
||||
// 否则意味着有多个键具有相同的哈希值(碰撞),所以我们需要更新链接列表和它的指针
|
||||
let next = this._valuesInfo[this._freeValueCellIndex].next;
|
||||
let previous = this._valuesInfo[this._freeValueCellIndex].previous;
|
||||
|
||||
// 现在它们指向最后一个值被移入的单元格
|
||||
if (next != -1)
|
||||
this._valuesInfo[next].previous = indexToValueToRemove;
|
||||
if (previous != -1)
|
||||
this._valuesInfo[previous].next = indexToValueToRemove;
|
||||
|
||||
// 最后,实际上是移动值
|
||||
this._valuesInfo[indexToValueToRemove] = this._valuesInfo[this._freeValueCellIndex];
|
||||
this._values[indexToValueToRemove] = this._values[this._freeValueCellIndex];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public trim(){
|
||||
let expandPrime = HashHelpers.expandPrime(this._freeValueCellIndex);
|
||||
|
||||
if (expandPrime < this._valuesInfo.length){
|
||||
this._values.length = expandPrime;
|
||||
this._valuesInfo.length = expandPrime;
|
||||
}
|
||||
}
|
||||
|
||||
public clear(){
|
||||
if (this._freeValueCellIndex == 0) return;
|
||||
|
||||
this._freeValueCellIndex = 0;
|
||||
this._buckets.length = 0;
|
||||
this._values.length = 0;
|
||||
this._valuesInfo.length = 0;
|
||||
}
|
||||
|
||||
public fastClear(){
|
||||
if (this._freeValueCellIndex == 0) return;
|
||||
|
||||
this._freeValueCellIndex = 0;
|
||||
|
||||
this._buckets.length = 0;
|
||||
this._valuesInfo.length = 0;
|
||||
}
|
||||
|
||||
public containsKey(key: TKey){
|
||||
if (this.tryFindIndex(key, {value: 0})){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public tryGetValue(key: TKey): TValue {
|
||||
let findIndex = {value: 0};
|
||||
if (this.tryFindIndex(key, findIndex)){
|
||||
return this._values[findIndex.value];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public tryFindIndex(key: TKey, findIndex: {value: number}){
|
||||
// 我把所有的索引都用偏移量+1来存储,这样在bucket list中0就意味着实际上不存在
|
||||
// 当读取时,偏移量必须再偏移-1才是真实的
|
||||
// 这样我就避免了将数组初始化为-1
|
||||
let hash = FasterDictionary.hash(key);
|
||||
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
|
||||
|
||||
let valueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
|
||||
|
||||
// 即使我们找到了一个现有的值,我们也需要确定它是我们所要求的值
|
||||
while (valueIndex != -1){
|
||||
if (this._valuesInfo[valueIndex].hashcode == hash && this._valuesInfo[valueIndex].key == key){
|
||||
findIndex.value = valueIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
valueIndex = this._valuesInfo[valueIndex].previous;
|
||||
}
|
||||
|
||||
findIndex.value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public getDirectValue(index: number): TValue {
|
||||
return this._values[index];
|
||||
}
|
||||
|
||||
public getIndex(key: TKey): number {
|
||||
let findIndex = {value: 0};
|
||||
if (this.tryFindIndex(key, findIndex))
|
||||
return findIndex.value;
|
||||
|
||||
throw new Error("未找到key");
|
||||
}
|
||||
|
||||
public static updateLinkedList(index: number, valuesInfo: FastNode[]){
|
||||
let next = valuesInfo[index].next;
|
||||
let previous = valuesInfo[index].previous;
|
||||
|
||||
if (next != -1)
|
||||
valuesInfo[next].previous = previous;
|
||||
if (previous != -1)
|
||||
valuesInfo[previous].next = next;
|
||||
}
|
||||
|
||||
public static hash(key) {
|
||||
return HashHelpers.getHashCode(key);
|
||||
}
|
||||
|
||||
public static reduce(x: number, n: number) {
|
||||
if (x >= n)
|
||||
return x % n;
|
||||
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
export class FastNode {
|
||||
readonly key;
|
||||
readonly hashcode: number;
|
||||
previous: number;
|
||||
next: number;
|
||||
|
||||
constructor(key, hash: number, previousNode: number = -1) {
|
||||
this.key = key;
|
||||
this.hashcode = hash;
|
||||
this.previous = previousNode;
|
||||
this.next = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
200
source/src/Utils/Collections/LinkList.ts
Normal file
200
source/src/Utils/Collections/LinkList.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
module es {
|
||||
export class Node<T>{
|
||||
element: T;
|
||||
next: Node<T>;
|
||||
// next为可选参数,如果不传则为undefined
|
||||
constructor(element: T, next?: Node<T>) {
|
||||
this.element = element;
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
|
||||
// 定义验证函数要传的参数和返回结果
|
||||
interface equalsFnType<T> {
|
||||
(a: T, b: T): boolean;
|
||||
}
|
||||
|
||||
export function defaultEquals<T>(a: T, b: T): boolean {
|
||||
return a === b;
|
||||
}
|
||||
|
||||
export class LinkedList<T> {
|
||||
// 声明链表内需要的变量并定义其类型
|
||||
protected count: number;
|
||||
protected next: any;
|
||||
protected equalsFn: equalsFnType<T>;
|
||||
protected head: Node<T>;
|
||||
|
||||
constructor(equalsFn = defaultEquals) {
|
||||
// 初始化链表内部变量
|
||||
this.count = 0;
|
||||
this.next = undefined;
|
||||
this.equalsFn = equalsFn;
|
||||
this.head = null;
|
||||
}
|
||||
|
||||
// 链表尾部添加元素
|
||||
push(element: T) {
|
||||
// 声明结点变量,将元素当作参数传入生成结点
|
||||
const node = new Node(element);
|
||||
// 存储遍历到的链表元素
|
||||
let current;
|
||||
if (this.head == null) {
|
||||
// 链表为空,直接将链表头部赋值为结点变量
|
||||
this.head = node;
|
||||
} else {
|
||||
// 链表不为空,我们只能拿到链表中第一个元素的引用
|
||||
current = this.head;
|
||||
// 循环访问链表
|
||||
while (current.next != null) {
|
||||
// 赋值遍历到的元素
|
||||
current = current.next;
|
||||
}
|
||||
// 此时已经得到了链表的最后一个元素(null),将链表的下一个元素赋值为结点变量。
|
||||
current.next = node;
|
||||
}
|
||||
// 链表长度自增
|
||||
this.count++;
|
||||
}
|
||||
|
||||
// 移除链表指定位置的元素
|
||||
removeAt(index: number) {
|
||||
// 边界判断: 参数是否有效
|
||||
if (index >= 0 && index < this.count) {
|
||||
// 获取当前链表头部元素
|
||||
let current = this.head;
|
||||
// 移除第一项
|
||||
if (index === 0) {
|
||||
this.head = current.next;
|
||||
} else {
|
||||
// 获取目标参数上一个结点
|
||||
const previous = this.getElementAt(index - 1);
|
||||
// 当前结点指向目标结点
|
||||
current = previous.next;
|
||||
/**
|
||||
* 目标结点元素已找到
|
||||
* previous.next指向目标结点
|
||||
* current.next指向undefined
|
||||
* previous.next指向current.next即删除目标结点的元素
|
||||
*/
|
||||
previous.next = current.next;
|
||||
}
|
||||
// 链表长度自减
|
||||
this.count--;
|
||||
// 返回当前删除的目标结点
|
||||
return current.element;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 获取链表指定位置的结点
|
||||
getElementAt(index: number) {
|
||||
// 参数校验
|
||||
if (index >= 0 && index <= this.count) {
|
||||
// 获取链表头部元素
|
||||
let current = this.head;
|
||||
// 从链表头部遍历至目标结点位置
|
||||
for (let i = 0; i < index && current != null; i++) {
|
||||
// 当前结点指向下一个目标结点
|
||||
current = current.next;
|
||||
}
|
||||
// 返回目标结点数据
|
||||
return current;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 向链表中插入元素
|
||||
insert(element: T, index: number) {
|
||||
// 参数有效性判断
|
||||
if (index >= 0 && index <= this.count) {
|
||||
// 声明节点变量,将当前要插入的元素作为参数生成结点
|
||||
const node = new Node(element);
|
||||
// 第一个位置添加元素
|
||||
if (index === 0) {
|
||||
// 将节点变量(node)的下一个元素指向链表的头部元素
|
||||
node.next = this.head;
|
||||
// 链表头部元素赋值为节点变量
|
||||
this.head = node;
|
||||
} else {
|
||||
// 获取目标结点的上一个结点
|
||||
const previous = this.getElementAt(index - 1);
|
||||
// 将节点变量的下一个元素指向目标节点
|
||||
node.next = previous.next;
|
||||
/**
|
||||
* 此时node中当前结点为要插入的值
|
||||
* next为原位置处的结点
|
||||
* 因此将当前结点赋值为node,就完成了结点插入操作
|
||||
*/
|
||||
previous.next = node;
|
||||
}
|
||||
// 链表长度自增
|
||||
this.count++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据元素获取其在链表中的索引
|
||||
indexOf(element: T) {
|
||||
// 获取链表顶部元素
|
||||
let current = this.head;
|
||||
// 遍历链表内的元素
|
||||
for (let i = 0; i < this.count && current != null; i++) {
|
||||
// 判断当前链表中的结点与目标结点是否相等
|
||||
if (this.equalsFn(element, current.element)) {
|
||||
// 返回索引
|
||||
return i;
|
||||
}
|
||||
// 当前结点指向下一个结点
|
||||
current = current.next;
|
||||
}
|
||||
// 目标元素不存在
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 移除链表中的指定元素
|
||||
remove(element: T) {
|
||||
// 获取element的索引,移除索引位置的元素
|
||||
this.removeAt(this.indexOf(element));
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.head = undefined;
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
// 获取链表长度
|
||||
size() {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
// 判断链表是否为空
|
||||
isEmpty() {
|
||||
return this.size() === 0;
|
||||
}
|
||||
|
||||
// 获取链表头部元素
|
||||
getHead() {
|
||||
return this.head;
|
||||
}
|
||||
|
||||
// 获取链表中的所有元素
|
||||
toString() {
|
||||
if (this.head == null) {
|
||||
return "";
|
||||
}
|
||||
let objString = `${this.head.element}`;
|
||||
// 获取链表顶点的下一个结点
|
||||
let current = this.head.next;
|
||||
// 遍历链表中的所有结点
|
||||
for (let i = 1; i < this.size() && current != null; i++) {
|
||||
// 将当前结点的元素拼接到最终要生成的字符串对象中
|
||||
objString = `${objString}, ${current.element}`;
|
||||
// 当前结点指向链表的下一个元素
|
||||
current = current.next;
|
||||
}
|
||||
return objString;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
source/src/Utils/Collections/ListPool.ts
Normal file
56
source/src/Utils/Collections/ListPool.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
module es {
|
||||
/**
|
||||
* 可以用于列表池的简单类
|
||||
*/
|
||||
export class ListPool {
|
||||
private static readonly _objectQueue = [];
|
||||
|
||||
/**
|
||||
* 预热缓存,使用最大的cacheCount对象填充缓存
|
||||
* @param cacheCount
|
||||
*/
|
||||
public static warmCache(cacheCount: number) {
|
||||
cacheCount -= this._objectQueue.length;
|
||||
if (cacheCount > 0) {
|
||||
for (let i = 0; i < cacheCount; i++) {
|
||||
this._objectQueue.unshift([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将缓存修剪为cacheCount项目
|
||||
* @param cacheCount
|
||||
*/
|
||||
public static trimCache(cacheCount) {
|
||||
while (cacheCount > this._objectQueue.length)
|
||||
this._objectQueue.shift();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*/
|
||||
public static clearCache() {
|
||||
this._objectQueue.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果可以的话,从堆栈中弹出一个项
|
||||
*/
|
||||
public static obtain<T>(): T[] {
|
||||
if (this._objectQueue.length > 0)
|
||||
return this._objectQueue.shift();
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将项推回堆栈
|
||||
* @param obj
|
||||
*/
|
||||
public static free<T>(obj: Array<T>) {
|
||||
this._objectQueue.unshift(obj);
|
||||
obj.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
source/src/Utils/Collections/Pair.ts
Normal file
28
source/src/Utils/Collections/Pair.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
module es {
|
||||
/**
|
||||
* 用于管理一对对象的简单DTO
|
||||
*/
|
||||
export class Pair<T> implements IEqualityComparable {
|
||||
public first: T;
|
||||
public second: T;
|
||||
|
||||
constructor(first: T, second: T) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.first = this.second = null;
|
||||
}
|
||||
|
||||
public equals(other: Pair<T>): boolean {
|
||||
// 这两种方法在功能上应该是等价的
|
||||
return this.first == other.first && this.second == other.second;
|
||||
}
|
||||
|
||||
public getHashCode(): number {
|
||||
return EqualityComparer.default<T>().getHashCode(this.first) * 37 +
|
||||
EqualityComparer.default<T>().getHashCode(this.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
source/src/Utils/Collections/Pool.ts
Normal file
69
source/src/Utils/Collections/Pool.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
module es {
|
||||
/**
|
||||
* 用于池任何对象
|
||||
*/
|
||||
export class Pool<T> {
|
||||
private static _objectQueue = [];
|
||||
|
||||
/**
|
||||
* 预热缓存,使用最大的cacheCount对象填充缓存
|
||||
* @param type
|
||||
* @param cacheCount
|
||||
*/
|
||||
public static warmCache(type: any, cacheCount: number){
|
||||
cacheCount -= this._objectQueue.length;
|
||||
if (cacheCount > 0) {
|
||||
for (let i = 0; i < cacheCount; i++) {
|
||||
this._objectQueue.unshift(new type());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将缓存修剪为cacheCount项目
|
||||
* @param cacheCount
|
||||
*/
|
||||
public static trimCache(cacheCount: number){
|
||||
while (cacheCount > this._objectQueue.length)
|
||||
this._objectQueue.shift();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*/
|
||||
public static clearCache() {
|
||||
this._objectQueue.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果可以的话,从堆栈中弹出一个项
|
||||
*/
|
||||
public static obtain<T>(type: any): T {
|
||||
if (this._objectQueue.length > 0)
|
||||
return this._objectQueue.shift();
|
||||
|
||||
return new type() as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将项推回堆栈
|
||||
* @param obj
|
||||
*/
|
||||
public static free<T>(obj: T) {
|
||||
this._objectQueue.unshift(obj);
|
||||
|
||||
if (isIPoolable(obj)){
|
||||
obj["reset"]();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPoolable {
|
||||
/**
|
||||
* 重置对象以供重用。对象引用应该为空,字段可以设置为默认值
|
||||
*/
|
||||
reset();
|
||||
}
|
||||
|
||||
export var isIPoolable = (props: any): props is IPoolable => typeof (props as IPoolable)['reset'] !== 'undefined';
|
||||
}
|
||||
212
source/src/Utils/Collections/Set.ts
Normal file
212
source/src/Utils/Collections/Set.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
module es {
|
||||
export interface ISet<T> {
|
||||
add(item: T): boolean
|
||||
remove(item: T): boolean
|
||||
contains(item: T): boolean
|
||||
getCount(): number
|
||||
clear(): void
|
||||
toArray(): Array<T>
|
||||
/**
|
||||
* 从当前集合中删除指定集合中的所有元素
|
||||
* @param other
|
||||
*/
|
||||
exceptWith(other: Array<T>): void
|
||||
/**
|
||||
* 修改当前Set对象,使其只包含该对象和指定数组中的元素
|
||||
* @param other
|
||||
*/
|
||||
intersectWith(other: Array<T>): void
|
||||
/**
|
||||
* 修改当前的集合对象,使其包含所有存在于自身、指定集合中的元素,或者两者都包含
|
||||
* @param other
|
||||
*/
|
||||
unionWith(other: Array<T>): void
|
||||
isSubsetOf(other: Array<T>): boolean
|
||||
isSupersetOf(other: Array<T>): boolean
|
||||
overlaps(other: Array<T>): boolean
|
||||
setEquals(other: Array<T>): boolean
|
||||
}
|
||||
|
||||
interface IBucketsWithCount<T> {
|
||||
Buckets: Array<Array<T>>
|
||||
Count: number
|
||||
}
|
||||
|
||||
abstract class Set<T> implements ISet<T> {
|
||||
protected buckets: T[][];
|
||||
protected count: number;
|
||||
constructor(source?: Array<T>) {
|
||||
this.clear();
|
||||
if (source)
|
||||
source.forEach(value => {
|
||||
this.add(value);
|
||||
});
|
||||
}
|
||||
abstract getHashCode(item: T): number;
|
||||
abstract areEqual(value1: T, value2: T): boolean;
|
||||
|
||||
add(item: T) {
|
||||
let hashCode = this.getHashCode(item);
|
||||
let bucket = this.buckets[hashCode];
|
||||
if (bucket === undefined) {
|
||||
let newBucket = new Array<T>();
|
||||
newBucket.push(item);
|
||||
this.buckets[hashCode] = newBucket;
|
||||
this.count = this.count + 1;
|
||||
return true;
|
||||
}
|
||||
if (bucket.some((value) => this.areEqual(value,item)))
|
||||
return false;
|
||||
bucket.push(item);
|
||||
this.count = this.count + 1;
|
||||
return true;
|
||||
};
|
||||
|
||||
remove(item: T) {
|
||||
let hashCode = this.getHashCode(item);
|
||||
let bucket = this.buckets[hashCode];
|
||||
if (bucket === undefined) {
|
||||
return false;
|
||||
}
|
||||
let result = false;
|
||||
let newBucket = new Array<T>();
|
||||
bucket.forEach((value) => {
|
||||
if (!this.areEqual(value, item))
|
||||
newBucket.push(item);
|
||||
else
|
||||
result = true;
|
||||
});
|
||||
this.buckets[hashCode] = newBucket;
|
||||
if (result)
|
||||
this.count = this.count - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
contains(item: T) {
|
||||
return this.bucketsContains(this.buckets, item)
|
||||
};
|
||||
|
||||
getCount() {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.buckets = new Array<Array<T>>();
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
toArray() {
|
||||
let result = new Array<T>()
|
||||
this.buckets.forEach(value => {
|
||||
value.forEach(inner => {
|
||||
result.push(inner);
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前集合中删除指定集合中的所有元素
|
||||
* @param other
|
||||
*/
|
||||
exceptWith(other: Array<T>) {
|
||||
if (other) {
|
||||
other.forEach(value => {
|
||||
this.remove(value);
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 修改当前Set对象,使其只包含该对象和指定数组中的元素
|
||||
* @param other
|
||||
*/
|
||||
intersectWith(other: Array<T>) {
|
||||
if (other) {
|
||||
let otherBuckets = this.buildInternalBuckets(other);
|
||||
this.toArray().forEach(value => {
|
||||
if (!this.bucketsContains(otherBuckets.Buckets, value))
|
||||
this.remove(value);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
unionWith(other: Array<T>) {
|
||||
other.forEach(value => {
|
||||
this.add(value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定当前集合是否为指定集合或数组的子集
|
||||
* @param other
|
||||
*/
|
||||
isSubsetOf(other: Array<T>) {
|
||||
|
||||
let otherBuckets = this.buildInternalBuckets(other);
|
||||
return this.toArray().every(value => this.bucketsContains(otherBuckets.Buckets, value));
|
||||
|
||||
}
|
||||
/**
|
||||
* 确定当前不可变排序集是否为指定集合的超集
|
||||
* @param other
|
||||
*/
|
||||
isSupersetOf(other: Array<T>) {
|
||||
return other.every(value => this.contains(value));
|
||||
}
|
||||
|
||||
overlaps(other: Array<T>) {
|
||||
return other.some(value => this.contains(value));
|
||||
}
|
||||
|
||||
setEquals(other: Array<T>) {
|
||||
let otherBuckets = this.buildInternalBuckets(other);
|
||||
if (otherBuckets.Count !== this.count)
|
||||
return false
|
||||
return other.every(value => this.contains(value));
|
||||
}
|
||||
|
||||
private buildInternalBuckets(source: Array<T>): IBucketsWithCount<T> {
|
||||
let internalBuckets = new Array<Array<T>>();
|
||||
let internalCount = 0;
|
||||
source.forEach(item=> {
|
||||
let hashCode = this.getHashCode(item);
|
||||
let bucket = internalBuckets[hashCode];
|
||||
if (bucket === undefined) {
|
||||
let newBucket = new Array<T>();
|
||||
newBucket.push(item);
|
||||
internalBuckets[hashCode] = newBucket;
|
||||
internalCount = internalCount + 1;
|
||||
}
|
||||
else if (!bucket.some((value) => this.areEqual(value, item))) {
|
||||
bucket.push(item);
|
||||
internalCount = internalCount + 1;
|
||||
}
|
||||
});
|
||||
return { Buckets: internalBuckets, Count: internalCount };
|
||||
}
|
||||
|
||||
private bucketsContains(internalBuckets: Array<Array<T>>, item: T) {
|
||||
let hashCode = this.getHashCode(item);
|
||||
let bucket = internalBuckets[hashCode];
|
||||
if (bucket === undefined) {
|
||||
return false;
|
||||
}
|
||||
return bucket.some((value) => this.areEqual(value, item));
|
||||
}
|
||||
}
|
||||
|
||||
export class HashSet<T extends IEqualityComparable> extends Set<T> {
|
||||
constructor(source?: Array<T>) {
|
||||
super(source)
|
||||
}
|
||||
getHashCode(item: T) {
|
||||
return item.getHashCode();
|
||||
}
|
||||
areEqual(value1: T, value2: T) {
|
||||
return value1.equals(value2);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user