优化内部组件索引机制(更改为SparseSet索引)减少用户切换索引成本
修复内部系统初始化逻辑 - 不应该再onInitialize中初始内部entities,移动到initialize中 ci跳过cocos项目避免ci失败 soa开放更多安全类型接口
This commit is contained in:
392
packages/core/tests/ECS/Utils/ComponentSparseSet.test.ts
Normal file
392
packages/core/tests/ECS/Utils/ComponentSparseSet.test.ts
Normal file
@@ -0,0 +1,392 @@
|
||||
import { ComponentSparseSet } from '../../../src/ECS/Utils/ComponentSparseSet';
|
||||
import { Entity } from '../../../src/ECS/Entity';
|
||||
import { Component } from '../../../src/ECS/Component';
|
||||
|
||||
// 测试组件类
|
||||
class PositionComponent extends Component {
|
||||
constructor(public x: number = 0, public y: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class VelocityComponent extends Component {
|
||||
constructor(public dx: number = 0, public dy: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class HealthComponent extends Component {
|
||||
constructor(public health: number = 100, public maxHealth: number = 100) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class RenderComponent extends Component {
|
||||
constructor(public visible: boolean = true) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
describe('ComponentSparseSet', () => {
|
||||
let componentSparseSet: ComponentSparseSet;
|
||||
let entity1: Entity;
|
||||
let entity2: Entity;
|
||||
let entity3: Entity;
|
||||
|
||||
beforeEach(() => {
|
||||
componentSparseSet = new ComponentSparseSet();
|
||||
|
||||
// 创建测试实体
|
||||
entity1 = new Entity('entity1', 1);
|
||||
entity1.addComponent(new PositionComponent(10, 20));
|
||||
entity1.addComponent(new VelocityComponent(1, 2));
|
||||
|
||||
entity2 = new Entity('entity2', 2);
|
||||
entity2.addComponent(new PositionComponent(30, 40));
|
||||
entity2.addComponent(new HealthComponent(80, 100));
|
||||
|
||||
entity3 = new Entity('entity3', 3);
|
||||
entity3.addComponent(new VelocityComponent(3, 4));
|
||||
entity3.addComponent(new HealthComponent(50, 100));
|
||||
entity3.addComponent(new RenderComponent(true));
|
||||
});
|
||||
|
||||
describe('基本实体操作', () => {
|
||||
it('应该能添加实体', () => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
|
||||
expect(componentSparseSet.size).toBe(1);
|
||||
expect(componentSparseSet.getAllEntities()).toContain(entity1);
|
||||
});
|
||||
|
||||
it('应该能移除实体', () => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
|
||||
componentSparseSet.removeEntity(entity1);
|
||||
|
||||
expect(componentSparseSet.size).toBe(1);
|
||||
expect(componentSparseSet.getAllEntities()).not.toContain(entity1);
|
||||
expect(componentSparseSet.getAllEntities()).toContain(entity2);
|
||||
});
|
||||
|
||||
it('应该处理重复添加实体', () => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity1);
|
||||
|
||||
expect(componentSparseSet.size).toBe(1);
|
||||
});
|
||||
|
||||
it('应该处理移除不存在的实体', () => {
|
||||
componentSparseSet.removeEntity(entity1);
|
||||
|
||||
expect(componentSparseSet.size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('单组件查询', () => {
|
||||
beforeEach(() => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
componentSparseSet.addEntity(entity3);
|
||||
});
|
||||
|
||||
it('应该能查询Position组件', () => {
|
||||
const entities = componentSparseSet.queryByComponent(PositionComponent);
|
||||
|
||||
expect(entities.size).toBe(2);
|
||||
expect(entities.has(entity1)).toBe(true);
|
||||
expect(entities.has(entity2)).toBe(true);
|
||||
expect(entities.has(entity3)).toBe(false);
|
||||
});
|
||||
|
||||
it('应该能查询Velocity组件', () => {
|
||||
const entities = componentSparseSet.queryByComponent(VelocityComponent);
|
||||
|
||||
expect(entities.size).toBe(2);
|
||||
expect(entities.has(entity1)).toBe(true);
|
||||
expect(entities.has(entity2)).toBe(false);
|
||||
expect(entities.has(entity3)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该能查询Health组件', () => {
|
||||
const entities = componentSparseSet.queryByComponent(HealthComponent);
|
||||
|
||||
expect(entities.size).toBe(2);
|
||||
expect(entities.has(entity1)).toBe(false);
|
||||
expect(entities.has(entity2)).toBe(true);
|
||||
expect(entities.has(entity3)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该能查询Render组件', () => {
|
||||
const entities = componentSparseSet.queryByComponent(RenderComponent);
|
||||
|
||||
expect(entities.size).toBe(1);
|
||||
expect(entities.has(entity3)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('多组件AND查询', () => {
|
||||
beforeEach(() => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
componentSparseSet.addEntity(entity3);
|
||||
});
|
||||
|
||||
it('应该能查询Position+Velocity组件', () => {
|
||||
const entities = componentSparseSet.queryMultipleAnd([PositionComponent, VelocityComponent]);
|
||||
|
||||
expect(entities.size).toBe(1);
|
||||
expect(entities.has(entity1)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该能查询Position+Health组件', () => {
|
||||
const entities = componentSparseSet.queryMultipleAnd([PositionComponent, HealthComponent]);
|
||||
|
||||
expect(entities.size).toBe(1);
|
||||
expect(entities.has(entity2)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该能查询Velocity+Health组件', () => {
|
||||
const entities = componentSparseSet.queryMultipleAnd([VelocityComponent, HealthComponent]);
|
||||
|
||||
expect(entities.size).toBe(1);
|
||||
expect(entities.has(entity3)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该能查询三个组件', () => {
|
||||
const entities = componentSparseSet.queryMultipleAnd([
|
||||
VelocityComponent,
|
||||
HealthComponent,
|
||||
RenderComponent
|
||||
]);
|
||||
|
||||
expect(entities.size).toBe(1);
|
||||
expect(entities.has(entity3)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该处理不存在的组合', () => {
|
||||
const entities = componentSparseSet.queryMultipleAnd([
|
||||
PositionComponent,
|
||||
VelocityComponent,
|
||||
HealthComponent,
|
||||
RenderComponent
|
||||
]);
|
||||
|
||||
expect(entities.size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('多组件OR查询', () => {
|
||||
beforeEach(() => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
componentSparseSet.addEntity(entity3);
|
||||
});
|
||||
|
||||
it('应该能查询Position或Velocity组件', () => {
|
||||
const entities = componentSparseSet.queryMultipleOr([PositionComponent, VelocityComponent]);
|
||||
|
||||
expect(entities.size).toBe(3);
|
||||
expect(entities.has(entity1)).toBe(true);
|
||||
expect(entities.has(entity2)).toBe(true);
|
||||
expect(entities.has(entity3)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该能查询Health或Render组件', () => {
|
||||
const entities = componentSparseSet.queryMultipleOr([HealthComponent, RenderComponent]);
|
||||
|
||||
expect(entities.size).toBe(2);
|
||||
expect(entities.has(entity1)).toBe(false);
|
||||
expect(entities.has(entity2)).toBe(true);
|
||||
expect(entities.has(entity3)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该处理单个组件的OR查询', () => {
|
||||
const entities = componentSparseSet.queryMultipleOr([RenderComponent]);
|
||||
|
||||
expect(entities.size).toBe(1);
|
||||
expect(entities.has(entity3)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('组件检查', () => {
|
||||
beforeEach(() => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
});
|
||||
|
||||
it('应该能检查实体是否有组件', () => {
|
||||
expect(componentSparseSet.hasComponent(entity1, PositionComponent)).toBe(true);
|
||||
expect(componentSparseSet.hasComponent(entity1, VelocityComponent)).toBe(true);
|
||||
expect(componentSparseSet.hasComponent(entity1, HealthComponent)).toBe(false);
|
||||
|
||||
expect(componentSparseSet.hasComponent(entity2, PositionComponent)).toBe(true);
|
||||
expect(componentSparseSet.hasComponent(entity2, HealthComponent)).toBe(true);
|
||||
expect(componentSparseSet.hasComponent(entity2, VelocityComponent)).toBe(false);
|
||||
});
|
||||
|
||||
it('应该处理不存在的实体', () => {
|
||||
expect(componentSparseSet.hasComponent(entity3, PositionComponent)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('位掩码操作', () => {
|
||||
beforeEach(() => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
});
|
||||
|
||||
it('应该能获取实体的组件位掩码', () => {
|
||||
const mask1 = componentSparseSet.getEntityMask(entity1);
|
||||
const mask2 = componentSparseSet.getEntityMask(entity2);
|
||||
|
||||
expect(mask1).toBeDefined();
|
||||
expect(mask2).toBeDefined();
|
||||
expect(mask1).not.toEqual(mask2);
|
||||
});
|
||||
|
||||
it('应该处理不存在的实体', () => {
|
||||
const mask = componentSparseSet.getEntityMask(entity3);
|
||||
expect(mask).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('遍历操作', () => {
|
||||
beforeEach(() => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
});
|
||||
|
||||
it('应该能遍历所有实体', () => {
|
||||
const entities: Entity[] = [];
|
||||
const masks: any[] = [];
|
||||
const indices: number[] = [];
|
||||
|
||||
componentSparseSet.forEach((entity, mask, index) => {
|
||||
entities.push(entity);
|
||||
masks.push(mask);
|
||||
indices.push(index);
|
||||
});
|
||||
|
||||
expect(entities.length).toBe(2);
|
||||
expect(masks.length).toBe(2);
|
||||
expect(indices).toEqual([0, 1]);
|
||||
expect(entities).toContain(entity1);
|
||||
expect(entities).toContain(entity2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('工具方法', () => {
|
||||
it('应该能检查空状态', () => {
|
||||
expect(componentSparseSet.isEmpty).toBe(true);
|
||||
|
||||
componentSparseSet.addEntity(entity1);
|
||||
expect(componentSparseSet.isEmpty).toBe(false);
|
||||
});
|
||||
|
||||
it('应该能清空数据', () => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
|
||||
componentSparseSet.clear();
|
||||
|
||||
expect(componentSparseSet.size).toBe(0);
|
||||
expect(componentSparseSet.isEmpty).toBe(true);
|
||||
});
|
||||
|
||||
it('应该提供内存统计', () => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
|
||||
const stats = componentSparseSet.getMemoryStats();
|
||||
|
||||
expect(stats.entitiesMemory).toBeGreaterThan(0);
|
||||
expect(stats.masksMemory).toBeGreaterThan(0);
|
||||
expect(stats.mappingsMemory).toBeGreaterThan(0);
|
||||
expect(stats.totalMemory).toBe(
|
||||
stats.entitiesMemory + stats.masksMemory + stats.mappingsMemory
|
||||
);
|
||||
});
|
||||
|
||||
it('应该能验证数据结构完整性', () => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
componentSparseSet.addEntity(entity2);
|
||||
componentSparseSet.removeEntity(entity1);
|
||||
|
||||
expect(componentSparseSet.validate()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('边界情况', () => {
|
||||
it('应该处理空查询', () => {
|
||||
componentSparseSet.addEntity(entity1);
|
||||
|
||||
const andResult = componentSparseSet.queryMultipleAnd([]);
|
||||
const orResult = componentSparseSet.queryMultipleOr([]);
|
||||
|
||||
expect(andResult.size).toBe(0);
|
||||
expect(orResult.size).toBe(0);
|
||||
});
|
||||
|
||||
it('应该处理未注册的组件类型', () => {
|
||||
class UnknownComponent extends Component {}
|
||||
|
||||
componentSparseSet.addEntity(entity1);
|
||||
|
||||
const entities = componentSparseSet.queryByComponent(UnknownComponent);
|
||||
expect(entities.size).toBe(0);
|
||||
});
|
||||
|
||||
it('应该正确处理实体组件变化', () => {
|
||||
// 添加实体
|
||||
componentSparseSet.addEntity(entity1);
|
||||
expect(componentSparseSet.hasComponent(entity1, PositionComponent)).toBe(true);
|
||||
|
||||
// 移除组件后重新添加实体
|
||||
entity1.removeComponentByType(PositionComponent);
|
||||
componentSparseSet.addEntity(entity1);
|
||||
|
||||
expect(componentSparseSet.hasComponent(entity1, PositionComponent)).toBe(false);
|
||||
expect(componentSparseSet.hasComponent(entity1, VelocityComponent)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('性能测试', () => {
|
||||
it('应该处理大量实体操作', () => {
|
||||
const entities: Entity[] = [];
|
||||
|
||||
// 创建大量实体
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const entity = new Entity(`entity${i}`, i);
|
||||
entity.addComponent(new PositionComponent(i, i));
|
||||
|
||||
if (i % 2 === 0) {
|
||||
entity.addComponent(new VelocityComponent(1, 1));
|
||||
}
|
||||
if (i % 3 === 0) {
|
||||
entity.addComponent(new HealthComponent(100, 100));
|
||||
}
|
||||
|
||||
entities.push(entity);
|
||||
componentSparseSet.addEntity(entity);
|
||||
}
|
||||
|
||||
expect(componentSparseSet.size).toBe(1000);
|
||||
|
||||
// 查询性能测试
|
||||
const positionEntities = componentSparseSet.queryByComponent(PositionComponent);
|
||||
expect(positionEntities.size).toBe(1000);
|
||||
|
||||
const velocityEntities = componentSparseSet.queryByComponent(VelocityComponent);
|
||||
expect(velocityEntities.size).toBe(500);
|
||||
|
||||
const healthEntities = componentSparseSet.queryByComponent(HealthComponent);
|
||||
expect(healthEntities.size).toBeGreaterThan(300);
|
||||
|
||||
// AND查询
|
||||
const posVelEntities = componentSparseSet.queryMultipleAnd([PositionComponent, VelocityComponent]);
|
||||
expect(posVelEntities.size).toBe(500);
|
||||
});
|
||||
});
|
||||
});
|
||||
241
packages/core/tests/ECS/Utils/SparseSet.test.ts
Normal file
241
packages/core/tests/ECS/Utils/SparseSet.test.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
import { SparseSet } from '../../../src/ECS/Utils/SparseSet';
|
||||
|
||||
describe('SparseSet', () => {
|
||||
let sparseSet: SparseSet<number>;
|
||||
|
||||
beforeEach(() => {
|
||||
sparseSet = new SparseSet<number>();
|
||||
});
|
||||
|
||||
describe('基本操作', () => {
|
||||
it('应该能添加元素', () => {
|
||||
expect(sparseSet.add(1)).toBe(true);
|
||||
expect(sparseSet.add(2)).toBe(true);
|
||||
expect(sparseSet.size).toBe(2);
|
||||
});
|
||||
|
||||
it('应该防止重复添加', () => {
|
||||
expect(sparseSet.add(1)).toBe(true);
|
||||
expect(sparseSet.add(1)).toBe(false);
|
||||
expect(sparseSet.size).toBe(1);
|
||||
});
|
||||
|
||||
it('应该能移除元素', () => {
|
||||
sparseSet.add(1);
|
||||
sparseSet.add(2);
|
||||
|
||||
expect(sparseSet.remove(1)).toBe(true);
|
||||
expect(sparseSet.size).toBe(1);
|
||||
expect(sparseSet.has(1)).toBe(false);
|
||||
expect(sparseSet.has(2)).toBe(true);
|
||||
});
|
||||
|
||||
it('应该处理移除不存在的元素', () => {
|
||||
expect(sparseSet.remove(99)).toBe(false);
|
||||
});
|
||||
|
||||
it('应该能检查元素存在性', () => {
|
||||
sparseSet.add(42);
|
||||
|
||||
expect(sparseSet.has(42)).toBe(true);
|
||||
expect(sparseSet.has(99)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('索引操作', () => {
|
||||
it('应该返回正确的索引', () => {
|
||||
sparseSet.add(10);
|
||||
sparseSet.add(20);
|
||||
sparseSet.add(30);
|
||||
|
||||
expect(sparseSet.getIndex(10)).toBe(0);
|
||||
expect(sparseSet.getIndex(20)).toBe(1);
|
||||
expect(sparseSet.getIndex(30)).toBe(2);
|
||||
});
|
||||
|
||||
it('应该能根据索引获取元素', () => {
|
||||
sparseSet.add(100);
|
||||
sparseSet.add(200);
|
||||
|
||||
expect(sparseSet.getByIndex(0)).toBe(100);
|
||||
expect(sparseSet.getByIndex(1)).toBe(200);
|
||||
expect(sparseSet.getByIndex(999)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('移除中间元素后应该保持紧凑性', () => {
|
||||
sparseSet.add(1);
|
||||
sparseSet.add(2);
|
||||
sparseSet.add(3);
|
||||
|
||||
// 移除中间元素
|
||||
sparseSet.remove(2);
|
||||
|
||||
// 最后一个元素应该移动到中间
|
||||
expect(sparseSet.getByIndex(0)).toBe(1);
|
||||
expect(sparseSet.getByIndex(1)).toBe(3);
|
||||
expect(sparseSet.size).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('遍历操作', () => {
|
||||
beforeEach(() => {
|
||||
sparseSet.add(10);
|
||||
sparseSet.add(20);
|
||||
sparseSet.add(30);
|
||||
});
|
||||
|
||||
it('应该能正确遍历', () => {
|
||||
const items: number[] = [];
|
||||
const indices: number[] = [];
|
||||
|
||||
sparseSet.forEach((item, index) => {
|
||||
items.push(item);
|
||||
indices.push(index);
|
||||
});
|
||||
|
||||
expect(items).toEqual([10, 20, 30]);
|
||||
expect(indices).toEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
it('应该能映射元素', () => {
|
||||
const doubled = sparseSet.map(x => x * 2);
|
||||
expect(doubled).toEqual([20, 40, 60]);
|
||||
});
|
||||
|
||||
it('应该能过滤元素', () => {
|
||||
const filtered = sparseSet.filter(x => x > 15);
|
||||
expect(filtered).toEqual([20, 30]);
|
||||
});
|
||||
|
||||
it('应该能查找元素', () => {
|
||||
const found = sparseSet.find(x => x > 15);
|
||||
expect(found).toBe(20);
|
||||
|
||||
const notFound = sparseSet.find(x => x > 100);
|
||||
expect(notFound).toBeUndefined();
|
||||
});
|
||||
|
||||
it('应该能检查存在性', () => {
|
||||
expect(sparseSet.some(x => x > 25)).toBe(true);
|
||||
expect(sparseSet.some(x => x > 100)).toBe(false);
|
||||
});
|
||||
|
||||
it('应该能检查全部条件', () => {
|
||||
expect(sparseSet.every(x => x > 0)).toBe(true);
|
||||
expect(sparseSet.every(x => x > 15)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('数据获取', () => {
|
||||
beforeEach(() => {
|
||||
sparseSet.add(1);
|
||||
sparseSet.add(2);
|
||||
sparseSet.add(3);
|
||||
});
|
||||
|
||||
it('应该返回只读数组副本', () => {
|
||||
const array = sparseSet.getDenseArray();
|
||||
expect(array).toEqual([1, 2, 3]);
|
||||
|
||||
// 尝试修改应该不影响原数据
|
||||
expect(() => {
|
||||
(array as any).push(4);
|
||||
}).not.toThrow();
|
||||
|
||||
expect(sparseSet.size).toBe(3);
|
||||
});
|
||||
|
||||
it('应该能转换为数组', () => {
|
||||
const array = sparseSet.toArray();
|
||||
expect(array).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('应该能转换为Set', () => {
|
||||
const set = sparseSet.toSet();
|
||||
expect(set).toEqual(new Set([1, 2, 3]));
|
||||
});
|
||||
});
|
||||
|
||||
describe('工具方法', () => {
|
||||
it('应该能检查空状态', () => {
|
||||
expect(sparseSet.isEmpty).toBe(true);
|
||||
|
||||
sparseSet.add(1);
|
||||
expect(sparseSet.isEmpty).toBe(false);
|
||||
});
|
||||
|
||||
it('应该能清空数据', () => {
|
||||
sparseSet.add(1);
|
||||
sparseSet.add(2);
|
||||
|
||||
sparseSet.clear();
|
||||
|
||||
expect(sparseSet.size).toBe(0);
|
||||
expect(sparseSet.isEmpty).toBe(true);
|
||||
expect(sparseSet.has(1)).toBe(false);
|
||||
});
|
||||
|
||||
it('应该提供内存统计', () => {
|
||||
sparseSet.add(1);
|
||||
sparseSet.add(2);
|
||||
|
||||
const stats = sparseSet.getMemoryStats();
|
||||
|
||||
expect(stats.denseArraySize).toBeGreaterThan(0);
|
||||
expect(stats.sparseMapSize).toBeGreaterThan(0);
|
||||
expect(stats.totalMemory).toBe(stats.denseArraySize + stats.sparseMapSize);
|
||||
});
|
||||
|
||||
it('应该能验证数据结构完整性', () => {
|
||||
sparseSet.add(1);
|
||||
sparseSet.add(2);
|
||||
sparseSet.add(3);
|
||||
sparseSet.remove(2);
|
||||
|
||||
expect(sparseSet.validate()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('性能场景', () => {
|
||||
it('应该处理大量数据操作', () => {
|
||||
const items = Array.from({ length: 1000 }, (_, i) => i);
|
||||
|
||||
// 批量添加
|
||||
for (const item of items) {
|
||||
sparseSet.add(item);
|
||||
}
|
||||
expect(sparseSet.size).toBe(1000);
|
||||
|
||||
// 批量移除偶数
|
||||
for (let i = 0; i < 1000; i += 2) {
|
||||
sparseSet.remove(i);
|
||||
}
|
||||
expect(sparseSet.size).toBe(500);
|
||||
|
||||
// 验证只剩奇数
|
||||
const remaining = sparseSet.toArray().sort((a, b) => a - b);
|
||||
for (let i = 0; i < remaining.length; i++) {
|
||||
expect(remaining[i] % 2).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
it('应该保持O(1)访问性能', () => {
|
||||
// 添加大量元素
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
sparseSet.add(i);
|
||||
}
|
||||
|
||||
// 随机访问应该很快
|
||||
const start = performance.now();
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const randomItem = Math.floor(Math.random() * 1000);
|
||||
sparseSet.has(randomItem);
|
||||
sparseSet.getIndex(randomItem);
|
||||
}
|
||||
const duration = performance.now() - start;
|
||||
|
||||
// 应该在很短时间内完成
|
||||
expect(duration).toBeLessThan(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user