🚧 Optimize
This commit is contained in:
13
frontend/src/common/constant/editor.ts
Normal file
13
frontend/src/common/constant/editor.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 编辑器相关常量配置
|
||||
*/
|
||||
|
||||
// 编辑器实例管理
|
||||
export const EDITOR_CONFIG = {
|
||||
/** 最多缓存的编辑器实例数量 */
|
||||
MAX_INSTANCES: 5,
|
||||
/** 语法树缓存过期时间(毫秒) */
|
||||
SYNTAX_TREE_CACHE_TIMEOUT: 30000,
|
||||
/** 加载状态延迟时间(毫秒) */
|
||||
LOADING_DELAY: 800,
|
||||
} as const;
|
||||
265
frontend/src/common/utils/asyncManager.ts
Normal file
265
frontend/src/common/utils/asyncManager.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* 操作信息接口
|
||||
*/
|
||||
interface OperationInfo {
|
||||
controller: AbortController;
|
||||
createdAt: number;
|
||||
timeout?: number;
|
||||
timeoutId?: NodeJS.Timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步操作管理器
|
||||
* 用于管理异步操作的竞态条件,确保只有最新的操作有效
|
||||
* 支持操作超时和自动清理机制
|
||||
*
|
||||
* @template T 操作上下文的类型
|
||||
*/
|
||||
export class AsyncManager<T = any> {
|
||||
private operationSequence = 0;
|
||||
private pendingOperations = new Map<number, OperationInfo>();
|
||||
private currentContext: T | null = null;
|
||||
private defaultTimeout: number;
|
||||
|
||||
/**
|
||||
* 创建异步操作管理器
|
||||
*
|
||||
* @param defaultTimeout 默认超时时间(毫秒),0表示不设置超时
|
||||
*/
|
||||
constructor(defaultTimeout: number = 0) {
|
||||
this.defaultTimeout = defaultTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新的操作ID
|
||||
*
|
||||
* @returns 新的操作ID
|
||||
*/
|
||||
getNextOperationId(): number {
|
||||
return ++this.operationSequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始新的操作
|
||||
*
|
||||
* @param context 操作上下文
|
||||
* @param options 操作选项
|
||||
* @returns 操作ID和AbortController
|
||||
*/
|
||||
startOperation(
|
||||
context: T,
|
||||
options?: {
|
||||
excludeId?: number;
|
||||
timeout?: number;
|
||||
}
|
||||
): { operationId: number; abortController: AbortController } {
|
||||
const operationId = this.getNextOperationId();
|
||||
const abortController = new AbortController();
|
||||
const timeout = options?.timeout ?? this.defaultTimeout;
|
||||
|
||||
// 取消之前的操作
|
||||
this.cancelPreviousOperations(options?.excludeId);
|
||||
|
||||
// 创建操作信息
|
||||
const operationInfo: OperationInfo = {
|
||||
controller: abortController,
|
||||
createdAt: Date.now(),
|
||||
timeout: timeout > 0 ? timeout : undefined
|
||||
};
|
||||
|
||||
// 设置超时处理
|
||||
if (timeout > 0) {
|
||||
operationInfo.timeoutId = setTimeout(() => {
|
||||
this.cancelOperation(operationId, 'timeout');
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
// 设置当前上下文和操作
|
||||
this.currentContext = context;
|
||||
this.pendingOperations.set(operationId, operationInfo);
|
||||
|
||||
return { operationId, abortController };
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查操作是否仍然有效
|
||||
*
|
||||
* @param operationId 操作ID
|
||||
* @param context 操作上下文
|
||||
* @returns 操作是否有效
|
||||
*/
|
||||
isOperationValid(operationId: number, context?: T): boolean {
|
||||
const operationInfo = this.pendingOperations.get(operationId);
|
||||
const contextValid = context === undefined || this.currentContext === context;
|
||||
|
||||
return (
|
||||
operationInfo !== undefined &&
|
||||
!operationInfo.controller.signal.aborted &&
|
||||
contextValid
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成操作
|
||||
*
|
||||
* @param operationId 操作ID
|
||||
*/
|
||||
completeOperation(operationId: number): void {
|
||||
const operationInfo = this.pendingOperations.get(operationId);
|
||||
if (operationInfo) {
|
||||
// 清理超时定时器
|
||||
if (operationInfo.timeoutId) {
|
||||
clearTimeout(operationInfo.timeoutId);
|
||||
}
|
||||
this.pendingOperations.delete(operationId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消指定操作
|
||||
*
|
||||
* @param operationId 操作ID
|
||||
* @param reason 取消原因
|
||||
*/
|
||||
cancelOperation(operationId: number, reason?: string): void {
|
||||
const operationInfo = this.pendingOperations.get(operationId);
|
||||
if (operationInfo) {
|
||||
// 清理超时定时器
|
||||
if (operationInfo.timeoutId) {
|
||||
clearTimeout(operationInfo.timeoutId);
|
||||
}
|
||||
// 取消操作
|
||||
operationInfo.controller.abort(reason);
|
||||
this.pendingOperations.delete(operationId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消之前的操作(修复并发bug)
|
||||
*
|
||||
* @param excludeId 要排除的操作ID(不取消该操作)
|
||||
*/
|
||||
cancelPreviousOperations(excludeId?: number): void {
|
||||
// 创建要取消的操作ID数组,避免在遍历时修改Map
|
||||
const operationIdsToCancel: number[] = [];
|
||||
|
||||
for (const [operationId] of this.pendingOperations) {
|
||||
if (excludeId === undefined || operationId !== excludeId) {
|
||||
operationIdsToCancel.push(operationId);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量取消操作
|
||||
for (const operationId of operationIdsToCancel) {
|
||||
this.cancelOperation(operationId, 'superseded');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有操作
|
||||
*/
|
||||
cancelAllOperations(): void {
|
||||
// 创建要取消的操作ID数组,避免在遍历时修改Map
|
||||
const operationIdsToCancel = Array.from(this.pendingOperations.keys());
|
||||
|
||||
// 批量取消操作
|
||||
for (const operationId of operationIdsToCancel) {
|
||||
this.cancelOperation(operationId, 'cancelled');
|
||||
}
|
||||
this.currentContext = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期操作(手动清理超时操作)
|
||||
*
|
||||
* @param maxAge 最大存活时间(毫秒)
|
||||
* @returns 清理的操作数量
|
||||
*/
|
||||
cleanupExpiredOperations(maxAge: number): number {
|
||||
const now = Date.now();
|
||||
const expiredOperationIds: number[] = [];
|
||||
|
||||
for (const [operationId, operationInfo] of this.pendingOperations) {
|
||||
if (now - operationInfo.createdAt > maxAge) {
|
||||
expiredOperationIds.push(operationId);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量取消过期操作
|
||||
for (const operationId of expiredOperationIds) {
|
||||
this.cancelOperation(operationId, 'expired');
|
||||
}
|
||||
|
||||
return expiredOperationIds.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作统计信息
|
||||
*
|
||||
* @returns 操作统计信息
|
||||
*/
|
||||
getOperationStats(): {
|
||||
total: number;
|
||||
withTimeout: number;
|
||||
averageAge: number;
|
||||
oldestAge: number;
|
||||
} {
|
||||
const now = Date.now();
|
||||
let withTimeout = 0;
|
||||
let totalAge = 0;
|
||||
let oldestAge = 0;
|
||||
|
||||
for (const operationInfo of this.pendingOperations.values()) {
|
||||
const age = now - operationInfo.createdAt;
|
||||
totalAge += age;
|
||||
oldestAge = Math.max(oldestAge, age);
|
||||
|
||||
if (operationInfo.timeout) {
|
||||
withTimeout++;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
total: this.pendingOperations.size,
|
||||
withTimeout,
|
||||
averageAge: this.pendingOperations.size > 0 ? totalAge / this.pendingOperations.size : 0,
|
||||
oldestAge
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前上下文
|
||||
*
|
||||
* @returns 当前上下文
|
||||
*/
|
||||
getCurrentContext(): T | null {
|
||||
return this.currentContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前上下文
|
||||
*
|
||||
* @param context 新的上下文
|
||||
*/
|
||||
setCurrentContext(context: T | null): void {
|
||||
this.currentContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待处理操作数量
|
||||
*
|
||||
* @returns 待处理操作数量
|
||||
*/
|
||||
get pendingCount(): number {
|
||||
return this.pendingOperations.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有待处理的操作
|
||||
*
|
||||
* @returns 是否有待处理的操作
|
||||
*/
|
||||
hasPendingOperations(): boolean {
|
||||
return this.pendingOperations.size > 0;
|
||||
}
|
||||
}
|
||||
280
frontend/src/common/utils/doublyLinkedList.ts
Normal file
280
frontend/src/common/utils/doublyLinkedList.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* 双向链表节点
|
||||
*
|
||||
* @template T 节点数据的类型
|
||||
*/
|
||||
export class DoublyLinkedListNode<T> {
|
||||
public data: T;
|
||||
public prev: DoublyLinkedListNode<T> | null = null;
|
||||
public next: DoublyLinkedListNode<T> | null = null;
|
||||
|
||||
constructor(data: T) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 双向链表实现
|
||||
* 提供 O(1) 时间复杂度的插入、删除和移动操作
|
||||
*
|
||||
* @template T 链表数据的类型
|
||||
*/
|
||||
export class DoublyLinkedList<T> {
|
||||
private head: DoublyLinkedListNode<T> | null = null;
|
||||
private tail: DoublyLinkedListNode<T> | null = null;
|
||||
private _size = 0;
|
||||
|
||||
/**
|
||||
* 获取链表大小
|
||||
*
|
||||
* @returns 链表中节点的数量
|
||||
*/
|
||||
get size(): number {
|
||||
return this._size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查链表是否为空
|
||||
*
|
||||
* @returns 链表是否为空
|
||||
*/
|
||||
get isEmpty(): boolean {
|
||||
return this._size === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头节点
|
||||
*
|
||||
* @returns 头节点,如果链表为空则返回null
|
||||
*/
|
||||
get first(): DoublyLinkedListNode<T> | null {
|
||||
return this.head;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取尾节点
|
||||
*
|
||||
* @returns 尾节点,如果链表为空则返回null
|
||||
*/
|
||||
get last(): DoublyLinkedListNode<T> | null {
|
||||
return this.tail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在链表头部添加节点
|
||||
*
|
||||
* @param data 要添加的数据
|
||||
* @returns 新创建的节点
|
||||
*/
|
||||
addFirst(data: T): DoublyLinkedListNode<T> {
|
||||
const newNode = new DoublyLinkedListNode(data);
|
||||
|
||||
if (this.head === null) {
|
||||
this.head = this.tail = newNode;
|
||||
} else {
|
||||
newNode.next = this.head;
|
||||
this.head.prev = newNode;
|
||||
this.head = newNode;
|
||||
}
|
||||
|
||||
this._size++;
|
||||
return newNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在链表尾部添加节点
|
||||
*
|
||||
* @param data 要添加的数据
|
||||
* @returns 新创建的节点
|
||||
*/
|
||||
addLast(data: T): DoublyLinkedListNode<T> {
|
||||
const newNode = new DoublyLinkedListNode(data);
|
||||
|
||||
if (this.tail === null) {
|
||||
this.head = this.tail = newNode;
|
||||
} else {
|
||||
newNode.prev = this.tail;
|
||||
this.tail.next = newNode;
|
||||
this.tail = newNode;
|
||||
}
|
||||
|
||||
this._size++;
|
||||
return newNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定节点
|
||||
*
|
||||
* @param node 要删除的节点
|
||||
* @returns 被删除节点的数据
|
||||
*/
|
||||
remove(node: DoublyLinkedListNode<T>): T {
|
||||
if (node.prev) {
|
||||
node.prev.next = node.next;
|
||||
} else {
|
||||
this.head = node.next;
|
||||
}
|
||||
|
||||
if (node.next) {
|
||||
node.next.prev = node.prev;
|
||||
} else {
|
||||
this.tail = node.prev;
|
||||
}
|
||||
|
||||
// 清理节点引用,防止内存泄漏
|
||||
const data = node.data;
|
||||
node.prev = null;
|
||||
node.next = null;
|
||||
|
||||
this._size--;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除头节点
|
||||
*
|
||||
* @returns 被删除节点的数据,如果链表为空则返回undefined
|
||||
*/
|
||||
removeFirst(): T | undefined {
|
||||
if (this.head === null) {
|
||||
return undefined;
|
||||
}
|
||||
return this.remove(this.head);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除尾节点
|
||||
*
|
||||
* @returns 被删除节点的数据,如果链表为空则返回undefined
|
||||
*/
|
||||
removeLast(): T | undefined {
|
||||
if (this.tail === null) {
|
||||
return undefined;
|
||||
}
|
||||
return this.remove(this.tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将节点移动到链表头部
|
||||
*
|
||||
* @param node 要移动的节点
|
||||
*/
|
||||
moveToFirst(node: DoublyLinkedListNode<T>): void {
|
||||
if (node === this.head) {
|
||||
return; // 已经在头部
|
||||
}
|
||||
|
||||
// 从当前位置移除
|
||||
if (node.prev) {
|
||||
node.prev.next = node.next;
|
||||
}
|
||||
if (node.next) {
|
||||
node.next.prev = node.prev;
|
||||
} else {
|
||||
this.tail = node.prev;
|
||||
}
|
||||
|
||||
// 移动到头部
|
||||
node.prev = null;
|
||||
node.next = this.head;
|
||||
if (this.head) {
|
||||
this.head.prev = node;
|
||||
}
|
||||
this.head = node;
|
||||
|
||||
// 如果链表之前为空,更新尾节点
|
||||
if (this.tail === null) {
|
||||
this.tail = node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将节点移动到链表尾部
|
||||
*
|
||||
* @param node 要移动的节点
|
||||
*/
|
||||
moveToLast(node: DoublyLinkedListNode<T>): void {
|
||||
if (node === this.tail) {
|
||||
return; // 已经在尾部
|
||||
}
|
||||
|
||||
// 从当前位置移除
|
||||
if (node.prev) {
|
||||
node.prev.next = node.next;
|
||||
} else {
|
||||
this.head = node.next;
|
||||
}
|
||||
if (node.next) {
|
||||
node.next.prev = node.prev;
|
||||
}
|
||||
|
||||
// 移动到尾部
|
||||
node.next = null;
|
||||
node.prev = this.tail;
|
||||
if (this.tail) {
|
||||
this.tail.next = node;
|
||||
}
|
||||
this.tail = node;
|
||||
|
||||
// 如果链表之前为空,更新头节点
|
||||
if (this.head === null) {
|
||||
this.head = node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空链表
|
||||
*
|
||||
* @param onClear 清空时对每个节点数据的回调函数
|
||||
*/
|
||||
clear(onClear?: (data: T) => void): void {
|
||||
let current = this.head;
|
||||
while (current) {
|
||||
const next = current.next;
|
||||
|
||||
if (onClear) {
|
||||
onClear(current.data);
|
||||
}
|
||||
|
||||
// 清理节点引用,防止内存泄漏
|
||||
current.prev = null;
|
||||
current.next = null;
|
||||
|
||||
current = next;
|
||||
}
|
||||
|
||||
this.head = null;
|
||||
this.tail = null;
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将链表转换为数组
|
||||
*
|
||||
* @returns 包含所有节点数据的数组,按从头到尾的顺序
|
||||
*/
|
||||
toArray(): T[] {
|
||||
const result: T[] = [];
|
||||
let current = this.head;
|
||||
while (current) {
|
||||
result.push(current.data);
|
||||
current = current.next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历链表
|
||||
*
|
||||
* @param callback 对每个节点数据执行的回调函数
|
||||
*/
|
||||
forEach(callback: (data: T, index: number) => void): void {
|
||||
let current = this.head;
|
||||
let index = 0;
|
||||
while (current) {
|
||||
callback(current.data, index);
|
||||
current = current.next;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
frontend/src/common/utils/hashUtils.ts
Normal file
92
frontend/src/common/utils/hashUtils.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 高效哈希算法实现
|
||||
* 针对大量文本内容优化的哈希函数集合
|
||||
*/
|
||||
|
||||
/**
|
||||
* 使用优化的 xxHash32 算法生成字符串哈希值
|
||||
* 专为大量文本内容设计,性能优异
|
||||
*
|
||||
* xxHash32 特点:
|
||||
* - 极快的处理速度
|
||||
* - 优秀的分布质量,冲突率极低
|
||||
* - 对长文本友好,性能不会随长度线性下降
|
||||
* - 被广泛应用于数据库、压缩工具等
|
||||
*
|
||||
* @param content 要哈希的字符串内容
|
||||
* @returns 32位哈希值的字符串表示
|
||||
*/
|
||||
export const generateContentHash = (content: string): string => {
|
||||
return (generateContentHashInternal(content) >>> 0).toString(36);
|
||||
};
|
||||
|
||||
/**
|
||||
* 从字符串中提取 32 位整数(模拟小端序)
|
||||
*/
|
||||
function getUint32(str: string, index: number): number {
|
||||
return (
|
||||
(str.charCodeAt(index) & 0xff) |
|
||||
((str.charCodeAt(index + 1) & 0xff) << 8) |
|
||||
((str.charCodeAt(index + 2) & 0xff) << 16) |
|
||||
((str.charCodeAt(index + 3) & 0xff) << 24)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 32 位左旋转
|
||||
*/
|
||||
function rotateLeft(value: number, shift: number): number {
|
||||
return (value << shift) | (value >>> (32 - shift));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 内部哈希计算函数,返回数值
|
||||
*/
|
||||
function generateContentHashInternal(content: string): number {
|
||||
const PRIME1 = 0x9e3779b1;
|
||||
const PRIME2 = 0x85ebca77;
|
||||
const PRIME3 = 0xc2b2ae3d;
|
||||
const PRIME4 = 0x27d4eb2f;
|
||||
const PRIME5 = 0x165667b1;
|
||||
|
||||
const len = content.length;
|
||||
let hash: number;
|
||||
let i = 0;
|
||||
|
||||
if (len >= 16) {
|
||||
let acc1 = PRIME1 + PRIME2;
|
||||
let acc2 = PRIME2;
|
||||
let acc3 = 0;
|
||||
let acc4 = -PRIME1;
|
||||
|
||||
for (; i <= len - 16; i += 16) {
|
||||
acc1 = Math.imul(rotateLeft(acc1 + Math.imul(getUint32(content, i), PRIME2), 13), PRIME1);
|
||||
acc2 = Math.imul(rotateLeft(acc2 + Math.imul(getUint32(content, i + 4), PRIME2), 13), PRIME1);
|
||||
acc3 = Math.imul(rotateLeft(acc3 + Math.imul(getUint32(content, i + 8), PRIME2), 13), PRIME1);
|
||||
acc4 = Math.imul(rotateLeft(acc4 + Math.imul(getUint32(content, i + 12), PRIME2), 13), PRIME1);
|
||||
}
|
||||
|
||||
hash = rotateLeft(acc1, 1) + rotateLeft(acc2, 7) + rotateLeft(acc3, 12) + rotateLeft(acc4, 18);
|
||||
} else {
|
||||
hash = PRIME5;
|
||||
}
|
||||
|
||||
hash += len;
|
||||
|
||||
for (; i <= len - 4; i += 4) {
|
||||
hash = Math.imul(rotateLeft(hash + Math.imul(getUint32(content, i), PRIME3), 17), PRIME4);
|
||||
}
|
||||
|
||||
for (; i < len; i++) {
|
||||
hash = Math.imul(rotateLeft(hash + Math.imul(content.charCodeAt(i), PRIME5), 11), PRIME1);
|
||||
}
|
||||
|
||||
hash ^= hash >>> 15;
|
||||
hash = Math.imul(hash, PRIME2);
|
||||
hash ^= hash >>> 13;
|
||||
hash = Math.imul(hash, PRIME3);
|
||||
hash ^= hash >>> 16;
|
||||
|
||||
return hash;
|
||||
}
|
||||
157
frontend/src/common/utils/lruCache.ts
Normal file
157
frontend/src/common/utils/lruCache.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { DoublyLinkedList, DoublyLinkedListNode } from './doublyLinkedList';
|
||||
|
||||
/**
|
||||
* LRU缓存项
|
||||
*
|
||||
* @template K 键的类型
|
||||
* @template V 值的类型
|
||||
*/
|
||||
interface LruCacheItem<K, V> {
|
||||
key: K;
|
||||
value: V;
|
||||
}
|
||||
|
||||
/**
|
||||
* LRU (Least Recently Used) 缓存实现
|
||||
* 使用双向链表 + Map 实现 O(1) 时间复杂度的所有操作
|
||||
*
|
||||
* @template K 键的类型
|
||||
* @template V 值的类型
|
||||
*/
|
||||
export class LruCache<K, V> {
|
||||
private readonly maxSize: number;
|
||||
private readonly cache = new Map<K, DoublyLinkedListNode<LruCacheItem<K, V>>>();
|
||||
private readonly lru = new DoublyLinkedList<LruCacheItem<K, V>>();
|
||||
|
||||
/**
|
||||
* 创建LRU缓存实例
|
||||
*
|
||||
* @param maxSize 最大缓存大小
|
||||
*/
|
||||
constructor(maxSize: number) {
|
||||
if (maxSize <= 0) {
|
||||
throw new Error('Max size must be greater than 0');
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存值
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 缓存的值,如果不存在则返回undefined
|
||||
*/
|
||||
get(key: K): V | undefined {
|
||||
const node = this.cache.get(key);
|
||||
if (node) {
|
||||
// 将访问的节点移动到链表尾部(最近使用)
|
||||
this.lru.moveToLast(node);
|
||||
return node.data.value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缓存值
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param onEvict 当有项目被驱逐时的回调函数
|
||||
*/
|
||||
set(key: K, value: V, onEvict?: (evictedKey: K, evictedValue: V) => void): void {
|
||||
const existingNode = this.cache.get(key);
|
||||
|
||||
// 如果键已存在,更新值并移动到最近使用
|
||||
if (existingNode) {
|
||||
existingNode.data.value = value;
|
||||
this.lru.moveToLast(existingNode);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果缓存已满,移除最少使用的项
|
||||
if (this.cache.size >= this.maxSize) {
|
||||
const oldestNode = this.lru.first;
|
||||
if (oldestNode) {
|
||||
const { key: evictedKey, value: evictedValue } = oldestNode.data;
|
||||
this.cache.delete(evictedKey);
|
||||
this.lru.removeFirst();
|
||||
|
||||
if (onEvict) {
|
||||
onEvict(evictedKey, evictedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新项到链表尾部(最近使用)
|
||||
const newNode = this.lru.addLast({ key, value });
|
||||
this.cache.set(key, newNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查键是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 是否存在
|
||||
*/
|
||||
has(key: K): boolean {
|
||||
return this.cache.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定键的缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @returns 是否成功删除
|
||||
*/
|
||||
delete(key: K): boolean {
|
||||
const node = this.cache.get(key);
|
||||
if (node) {
|
||||
this.cache.delete(key);
|
||||
this.lru.remove(node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空缓存
|
||||
*
|
||||
* @param onEvict 清空时对每个项目的回调函数
|
||||
*/
|
||||
clear(onEvict?: (key: K, value: V) => void): void {
|
||||
if (onEvict) {
|
||||
this.lru.forEach(item => {
|
||||
onEvict(item.key, item.value);
|
||||
});
|
||||
}
|
||||
this.cache.clear();
|
||||
this.lru.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存大小
|
||||
*
|
||||
* @returns 当前缓存项数量
|
||||
*/
|
||||
get size(): number {
|
||||
return this.cache.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有键
|
||||
*
|
||||
* @returns 所有键的数组,按最近使用顺序排列(从最少使用到最近使用)
|
||||
*/
|
||||
keys(): K[] {
|
||||
return this.lru.toArray().map(item => item.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有值
|
||||
*
|
||||
* @returns 所有值的数组,按最近使用顺序排列(从最少使用到最近使用)
|
||||
*/
|
||||
values(): V[] {
|
||||
return this.lru.toArray().map(item => item.value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user