前言:最近对前端的兴趣越来越大,于是决定把之前量化回测的代码从java改为用ts重写,数据api使用python的tushare包提供。我在调用api的时候想要写一个函数缓存,因为有的数据量特别大,每次调用都比较耗时,在java里面我可以使用HashMap来实现,在js里面呢?我在es6里面找到了Map这个数据结构,但是一番尝试后我发现Map只能对基本的数据类型使用值作为key的存储,如果值是对象的话,key存储的是这个对象的引用。于是我决定自己实现一个简单的HashMap,目前的实现很简单,如果键是基本类型的话直接存储,如果是对象类型的话,则使用JSON.stringify将对象转化为字符串(这里见笑了,后面想办法换成比较高效的解决方法);代码测试通过后,就想把这个包发布到npm上,之前写过java包,但是发布到maven上异常的麻烦,没想到发布到npm上这么简单:
1.在npm上注册账号
2.在项目路径下npm add user
3.根据提示验证邮箱
4.npm publish
5.done.
下面介绍下我实现的代码(见笑了):
type basic = number | string
export class HashMap<K extends basic | object, V> implements Map<K, V>{
private map: Map<basic, V> = new Map();
// store key and hashcode
private keyMap: Map<basic, K> = new Map();
private readonly hashCode: (object: basic | object) => basic = object => {
if (typeof object === "number" || typeof object === "string") {
return object;
}
// todo need to develop better function for making the key to string
return JSON.stringify(object);
}
constructor(config?: {hashCode: (object: basic | object) => basic}) {
if (config !== undefined && config.hashCode && typeof config.hashCode === "function") {
this.hashCode = config.hashCode;
}
this.size = this.map.size;
}
[Symbol.iterator](): IterableIterator<[K, V]> {
return this.entries();
}
entries (): IterableIterator<[K, V]> {
const keyIterator = this.keys();
const valueIterator = this.values();
return new class implements IterableIterator<[K, V]> {
next(): IteratorResult<[K, V], any> {
return {
done: true,
value: [keyIterator.next(), valueIterator.next()]
};
}
[Symbol.iterator](): IterableIterator<[K, V]> {
return this;
}
}
}
keys(): IterableIterator<K> {
return this.keyMap.values();
}
values(): IterableIterator<V> {
return this.map.values();
}
[Symbol.toStringTag]: string;
size: number;
clear(): void {
this.map.clear();
this.size = this.map.size;
}
delete(key: K): boolean {
const hashCode = this.hashCode(key);
this.keyMap.delete(hashCode);
const deleteFlag = this.map.delete(this.hashCode(key));
this.size = this.map.size;
return deleteFlag;
}
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
this.map.forEach((value1, key1, map1) => {
// get key
const originalKey = this.keyMap.get(key1);
if (typeof originalKey === undefined) {
throw new Error(`can not find key ${key1} in HashMap's map`);
}
callbackfn(value1, originalKey!, this as unknown as Map<K, V>);
})
}
get(key: K): V | undefined{
return this.map.get(this.hashCode(key))
}
has(key: K): boolean {
const hashCode = this.hashCode(key);
return this.keyMap.has(hashCode);
}
set(key: K, value: V): this {
const hashCode = this.hashCode(key);
this.keyMap.set(hashCode, key);
this.map.set(hashCode, value);
this.size = this.map.size;
return this;
}
}
测试
import {HashMap} from "./hashmap";
test('new HashMap', () => {
const map = new HashMap();
expect(map.size).toBe(0);
})
test('set & get', () => {
const map = new HashMap();
map.set({a: 1, b: 2}, 3);
const v = map.get({a: 1, b: 2});
expect(v).toBe(3);
})
test('Symbol.iterator', () => {
const map = new HashMap();
map.set(1, 2);
for (let [k, v] of map) {
expect(k).toBe(1);
expect(v).toBe(2);
}
})
test('entries', () => {
const map = new HashMap();
map.set(1, 2);
for (let [k, v] of map.entries()) {
expect(k).toBe(1);
expect(v).toBe(2);
}
})
test('keys & values', () => {
const map = new HashMap();
map.set(1, 2);
for (let k of map.keys()) {
expect(k).toBe(1);
}
for (let v of map.values()) {
expect(v).toBe(2);
}
})
test('size', () => {
const map = new HashMap();
map.set(1, 2);
expect(map.size).toBe(1);
})
test('clear', () => {
const map = new HashMap();
map.set(1, 2);
expect(map.size).toBe(1);
map.clear();
expect(map.size).toBe(0);
})
test('delete', () => {
const map = new HashMap();
map.set(1, 2);
expect(map.size).toBe(1);
map.delete(1);
expect(map.size).toBe(0);
map.set({a: 1, b: 2}, 3);
expect(map.size).toBe(1);
map.delete({a: 1, b: 2});
expect(map.size).toBe(0);
})
test('forEach', () => {
const map = new HashMap();
map.set(1, 2);
map.set(2, 3);
map.forEach(((value, key, map) => {
expect(value === 2 || value === 3).toBe(true);
expect(key === 1 || key === 2).toBe(true);
}))
})
test('has', () => {
const map = new HashMap();
map.set(1, 2);
map.set({a: 1, b: 2}, 3);
expect(map.has(1)).toBe(true);
expect(map.has({a: 1, b: 2})).toBe(true);
})
github地址:
https://github.com/Mng12345/mng-easy-util
npm:
npm i mng-easy-util