Files
esengine/packages/core/src/ECS/Utils/SparseSet.ts

311 lines
7.9 KiB
TypeScript
Raw Normal View History

/**
*
*
* O(1)
* 使访
*
* @template T
*
* @example
* ```typescript
* const sparseSet = new SparseSet<Entity>();
*
* sparseSet.add(entity1);
* sparseSet.add(entity2);
*
* if (sparseSet.has(entity1)) {
* sparseSet.remove(entity1);
* }
*
* sparseSet.forEach((entity, index) => {
* console.log(`Entity at index ${index}: ${entity.name}`);
* });
* ```
*/
export class SparseSet<T> {
/**
*
*
*
*/
private _dense: T[] = [];
/**
*
*
* O(1)
*/
private _sparse = new Map<T, number>();
/**
*
*
* @param item
* @returns false表示元素已存在
*/
public add(item: T): boolean {
if (this._sparse.has(item)) {
return false; // 元素已存在
}
const index = this._dense.length;
this._dense.push(item);
this._sparse.set(item, index);
return true;
}
/**
*
*
* 使swap-and-pop技术保持数组紧凑性
* 1.
* 2.
* 3.
*
* @param item
* @returns false表示元素不存在
*/
public remove(item: T): boolean {
const index = this._sparse.get(item);
if (index === undefined) {
return false; // 元素不存在
}
const lastIndex = this._dense.length - 1;
// 如果不是最后一个元素,则与最后一个元素交换
if (index !== lastIndex) {
const lastItem = this._dense[lastIndex]!;
this._dense[index] = lastItem;
this._sparse.set(lastItem, index);
}
// 移除最后一个元素
this._dense.pop();
this._sparse.delete(item);
return true;
}
/**
*
*
* @param item
* @returns
*/
public has(item: T): boolean {
return this._sparse.has(item);
}
/**
*
*
* @param item
* @returns undefined
*/
public getIndex(item: T): number | undefined {
return this._sparse.get(item);
}
/**
*
*
* @param index
* @returns undefined
*/
public getByIndex(index: number): T | undefined {
return this._dense[index];
}
/**
*
*/
public get size(): number {
return this._dense.length;
}
/**
*
*/
public get isEmpty(): boolean {
return this._dense.length === 0;
}
/**
*
*
*
*
*
* @param callback
*/
public forEach(callback: (item: T, index: number) => void): void {
for (let i = 0; i < this._dense.length; i++) {
callback(this._dense[i]!, i);
}
}
/**
*
*
* @param callback
* @returns
*/
public map<U>(callback: (item: T, index: number) => U): U[] {
const result: U[] = [];
for (let i = 0; i < this._dense.length; i++) {
result.push(callback(this._dense[i]!, i));
}
return result;
}
/**
*
*
* @param predicate
* @returns
*/
public filter(predicate: (item: T, index: number) => boolean): T[] {
const result: T[] = [];
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i]!, i)) {
result.push(this._dense[i]!);
}
}
return result;
}
/**
*
*
* @param predicate
* @returns undefined
*/
public find(predicate: (item: T, index: number) => boolean): T | undefined {
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i]!, i)) {
return this._dense[i];
}
}
return undefined;
}
/**
*
*
* @param predicate
* @returns
*/
public some(predicate: (item: T, index: number) => boolean): boolean {
for (let i = 0; i < this._dense.length; i++) {
if (predicate(this._dense[i]!, i)) {
return true;
}
}
return false;
}
/**
*
*
* @param predicate
* @returns
*/
public every(predicate: (item: T, index: number) => boolean): boolean {
for (let i = 0; i < this._dense.length; i++) {
if (!predicate(this._dense[i]!, i)) {
return false;
}
}
return true;
}
/**
*
*
*
*/
public getDenseArray(): readonly T[] {
return [...this._dense];
}
/**
* 使
*
*
* 使
*/
public getDenseArrayUnsafe(): readonly T[] {
return this._dense;
}
/**
*
*/
public clear(): void {
this._dense.length = 0;
this._sparse.clear();
}
/**
*
*/
public toArray(): T[] {
return [...this._dense];
}
/**
* Set
*/
public toSet(): Set<T> {
return new Set(this._dense);
}
/**
* 使
*/
public getMemoryStats(): {
denseArraySize: number;
sparseMapSize: number;
totalMemory: number;
} {
const denseArraySize = this._dense.length * 8; // 估计每个引用8字节
const sparseMapSize = this._sparse.size * 16; // 估计每个Map条目16字节
return {
denseArraySize,
sparseMapSize,
totalMemory: denseArraySize + sparseMapSize
};
}
/**
*
*
*
*/
public validate(): boolean {
// 检查大小一致性
if (this._dense.length !== this._sparse.size) {
return false;
}
// 检查映射关系的正确性
for (let i = 0; i < this._dense.length; i++) {
const item = this._dense[i]!;
const mappedIndex = this._sparse.get(item);
if (mappedIndex !== i) {
return false;
}
}
// 检查稀疏映射中的所有项都在密集数组中
for (const [item, index] of this._sparse) {
if (index >= this._dense.length || this._dense[index] !== item) {
return false;
}
}
return true;
}
}