massping
Version:
Mass send http requests for test web application
434 lines (393 loc) • 11.7 kB
JavaScript
/**
* @file lib/collection.js
* @description common collections
*/
/**
* 基于Map的计数器类,支持键值计数操作
* @class
* @example
* const counter = new Counter();
* counter.add('apple', 3);
* counter.inc('apple');
* console.log(counter.get('apple')); // 输出 4
*/
export class Counter {
/**
* 创建计数器实例
* @constructor
*/
constructor() {
/**
* 存储计数的内部Map
* @private
* @type {Map<any, number>}
*/
this.data = new Map();
}
/**
* 增加指定键的计数值
* @param {any} [key=null] - 要计数的键(默认null)
* @param {number} [value=1] - 要增加的值(默认1)
* @returns {number} 增加后的计数值
*/
add(key = null, value = 1) {
if (this.data.has(key)) {
this.data.set(key, this.data.get(key) + value);
} else {
this.data.set(key, value);
}
return this.data.get(key);
}
/**
* 将指定键的计数值增加1(add的快捷方法)
* @param {any} [key=null] - 要计数的键(默认null)
* @returns {number} 增加后的计数值
*/
inc(key = null) {
return this.add(key, 1);
}
/**
* 获取指定键的当前计数值
* @param {any} key - 要查询的键
* @returns {number} 计数值(键不存在时返回0)
*/
get(key) {
return this.data.get(key) || 0;
}
/**
* 直接设置指定键的计数值
* @param {any} key - 要设置的键
* @param {number} value - 要设置的计数值
*/
set(key, value) {
this.data.set(key, value);
}
/**
* 检查键是否存在
* @param {any} key - 要检查的键
* @returns {boolean} 键是否存在
*/
has(key) {
return this.data.has(key);
}
/**
* 删除指定键的计数
* @param {any} key - 要删除的键
* @returns {boolean} 是否成功删除
*/
delete(key) {
return this.data.delete(key);
}
/**
* 重置计数器,清除所有计数
*/
clear() {
this.data.clear();
}
}
/**
* 循环数组(环形缓冲区)实现类,支持代理访问和迭代操作
* @class
* @example
* const arr = new RotatingArray(3);
* arr[0] = 'a'; // 实际存储位置: [0]
* arr.push('b'); // 存储位置: [1,0] (cur=1)
* arr.push('c'); // 存储位置: [2,1,0] (cur=2)
* arr.push('d'); // 覆盖最旧元素: [2(d),1,0] -> cur=0
* console.log(arr[0]); // 输出 'd' (最新元素)
*/
export class RotatingArray {
/**
* 创建循环数组实例
* @constructor
* @param {number} [length=0] - 数组固定长度
* @param {any} [fill=null] - 数组初始化填充值
* @returns {Proxy} 代理对象支持类数组访问
*/
constructor(length = 0, fill = null) {
/**
* 内部存储数组
* @private
* @type {Array}
*/
this.data = new Array(length).fill(fill);
/**
* 数组固定长度
* @type {number}
*/
this.length = length;
/**
* 当前指针位置(指向下一个写入位置)
* @private
* @type {number}
*/
this.cur = 0;
return new Proxy(this, {
get: (target, prop) => {
if (typeof target[prop] === 'function') {
return target[prop].bind(target);
}
const index = parseInt(prop, 10);
if (!isNaN(index)) {
return target.get(index);
}
return target[prop];
},
set: (target, prop, value) => {
const index = parseInt(prop, 10);
if (!isNaN(index)) {
target.set(prop, value);
return true;
}
return target[prop] = value;
}
});
}
/**
* 计算实际存储索引(处理循环偏移)
* @private
* @param {number} index - 逻辑索引
* @returns {number} 实际存储索引
*/
rawIndex(index) {
return (this.cur + index + this.length) % this.length;
}
/**
* 获取指定逻辑索引处的值
* @param {number} index - 要获取的逻辑索引(0=最新元素)
* @returns {any} 索引对应的值
*/
get(index) {
return this.data[this.rawIndex(index)];
}
/**
* 设置指定逻辑索引处的值
* @param {number} index - 要设置的逻辑索引
* @param {any} value - 要设置的值
*/
set(index, value) {
this.data[this.rawIndex(index)] = value;
}
/**
* 向数组头部添加新元素(覆盖最旧元素)
* @param {any} value - 要添加的值
*/
push(value) {
this.set(0, value);
this.cur = (this.cur + 1) % this.data.length;
}
/**
* 实现迭代器协议,支持for...of循环
* @returns {Iterator} 数组迭代器
*/
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.length) {
return {
value: this.get(index++),
done: false
};
}
return { done: true };
}
};
}
/**
* 获取前n个最新元素(按时间顺序从新到旧)
* @param {number} n - 要获取的元素数量
* @returns {Array} 包含最新元素的数组
*/
head(n) {
if (n <= 0) return [];
const result = [];
for (let i = 0; i < n; i++) {
result.push(this.get(i));
}
return result;
}
/**
* 获取后n个最旧元素(按时间顺序从旧到新)
* @param {number} n - 要获取的元素数量
* @returns {Array} 包含最旧元素的数组
*/
tail(n) {
if (n <= 0) return [];
const result = [];
for (let i = this.length - n; i < this.length; i++) {
result.push(this.get(i));
}
return result;
}
}
export class LinkedList {
static Node = class Node {
constructor(value, prev = null, next = null, list) {
this.value = value;
this.prev = prev;
this.next = next;
this.list = list;
}
}
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
append(value) {
const newNode = new LinkedList.Node(value);
newNode.list = this
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.prev = this.tail;
this.tail.next = newNode;
this.tail = newNode;
}
this.length++;
return newNode;
}
prepend(value) {
const newNode = new LinkedList.Node(value);
newNode.list = this
if (!this.head) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
this.length++;
return newNode;
}
insert(value, node, beNext = true) {
if (node) {
if (beNext) {
// 在参考节点后插入
const newNode = new LinkedList.Node(value, node, node.next, this);
if (node.next) node.next.prev = newNode;
else this.tail = newNode;
node.next = newNode;
this.length++;
return newNode;
} else {
// 在参考节点前插入
const newNode = new LinkedList.Node(value, node.prev, node, this);
if (node.prev) node.prev.next = newNode;
else this.head = newNode;
node.prev = newNode;
this.length++;
return newNode;
}
}
return this.append(value)
}
/**
* 链表的随机访问
* @param {number} pos
* @returns {LinkedList.Node}
*/
getNode(pos) {
let current = this.head
let dir = 'next'
let steps = pos
if (pos < 0) {
current = this.tail
dir = 'prev'
steps = -pos - 1
}
for (let i = 0; i < steps && current; i++) {
current = current[dir]
}
return current
}
/**
* O(1) 复杂度的删除操作
* @param {LinkedList.Node} node
* @returns
*/
remove(node) {
if (node.list === this) {
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
node.prev = null;
node.next = null;
this.length--;
return node.value;
}
if (node instanceof LinkedList.Node) {
return node.value;
}
let type = typeof node
type = type === 'object' ? type.constructor : type
throw TypeError(`<${type}>${node} not is a LinkedList.Node. `)
}
/**
* 遍历链表的部分节点
* @param {number|number[]} limit - 限制条件:
* - 如果为正数:遍历开头的limit个节点
* - 如果为负数:遍历末尾的-limit个节点
* - 如果是数组[headLimit, tailLimit]:同时遍历开头headLimit个和末尾-tailLimit个节点
* @param {Function} callback - 遍历回调函数
*/
forLimit(limit, callback) {
if (limit instanceof Array) {
let [lh, lt] = limit
// 计算末尾还可以遍历的实际数量,避免与开头部分重叠 (lt 始终为负数)
lt = Math.max(-(this.length - Math.min(lh, this.length)), lt)
this.forLimit(lh, callback);
this.forLimit(lt, callback);
return;
}
if (limit < 0) {
// 负数:遍历末尾部分
let current = this.getNode(limit)
for (let i = limit; i < 0 && current; i--) {
callback(current.value, i, this);
current = current.next;
}
} else {
// 正数:遍历开头部分
let current = this.head;
for (let i = 0; i < limit && current; i++) {
callback(current.value, i, this);
current = current.next;
}
}
}
[Symbol.iterator]() {
let current = this.head;
return {
next: () => {
if (current) {
const value = current.value;
current = current.next;
return { value, done: false };
}
return { done: true };
}
};
}
toString(limit = 5) {
if (this.length === 0) return '[]'
let lh, lt
if (limit < this.length) {
lh = Math.floor(limit / 2);
lt = -limit + lh;
} else {
lh = limit
lt = 0
}
let heads = [];
let tails = [];
this.forLimit([lh, lt], (v, i) => {
i < 0 ? tails.push(v) : heads.push(v)
})
return '[ ' + [...heads].join(', ') + (tails.length ? ' ... ' : '') + [...tails].join(', ') + ' ]';
}
}