新增hashSet
This commit is contained in:
@@ -1,17 +1,61 @@
|
||||
module es {
|
||||
export class EqualityComparer<T> implements IEqualityComparer {
|
||||
public static default<T>(){
|
||||
export class EqualityComparer<T> implements IEqualityComparer<T> {
|
||||
public static default<T>() {
|
||||
return new EqualityComparer<T>();
|
||||
}
|
||||
|
||||
protected constructor(){ }
|
||||
protected constructor() { }
|
||||
|
||||
public equals(x: T, y: T): boolean{
|
||||
if (typeof x["equals"] == 'function'){
|
||||
public equals(x: T, y: T): boolean {
|
||||
if (typeof x["equals"] == 'function') {
|
||||
return x["equals"](y);
|
||||
} else {
|
||||
return x === y;
|
||||
}
|
||||
}
|
||||
|
||||
public getHashCode(o: T): number {
|
||||
if (typeof o == 'number') {
|
||||
return this._getHashCodeForNumber(o);
|
||||
}
|
||||
|
||||
if (typeof o == 'string') {
|
||||
return this._getHashCodeForString(o);
|
||||
}
|
||||
|
||||
let hashCode = 385229220;
|
||||
this.forOwn(o, (value) => {
|
||||
if (typeof value == 'number') {
|
||||
hashCode += this._getHashCodeForNumber(value);
|
||||
} else if (typeof value == 'string') {
|
||||
hashCode += this._getHashCodeForString(value);
|
||||
} else if (typeof value == 'object') {
|
||||
this.forOwn(value, () => {
|
||||
hashCode += this.getHashCode(value);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
private _getHashCodeForNumber(n: number): number {
|
||||
return n;
|
||||
}
|
||||
|
||||
private _getHashCodeForString(s: string): number {
|
||||
let hashCode = 385229220;
|
||||
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
hashCode = (hashCode * -1521134295) ^ s.charCodeAt(i);
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
private forOwn(object, iteratee){
|
||||
object = Object(object);
|
||||
Object.keys(object).forEach((key) => iteratee(object[key], key, object));
|
||||
}
|
||||
}
|
||||
}
|
||||
17
source/src/Utils/IEqualityComparable.ts
Normal file
17
source/src/Utils/IEqualityComparable.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
module es {
|
||||
/**
|
||||
* 对象声明自己的平等方法和Hashcode的生成
|
||||
*/
|
||||
export interface IEqualityComparable {
|
||||
/**
|
||||
* 确定另一个对象是否等于这个实例
|
||||
* @param other
|
||||
*/
|
||||
equals(other: any): boolean;
|
||||
|
||||
/**
|
||||
* 生成对象的哈希码
|
||||
*/
|
||||
getHashCode(): number;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,19 @@
|
||||
module es {
|
||||
export interface IEqualityComparer {
|
||||
equals(x: any, y: any): boolean;
|
||||
/**
|
||||
* 为确定对象的哈希码和两个项目是否相等提供接口
|
||||
*/
|
||||
export interface IEqualityComparer<T> {
|
||||
/**
|
||||
* 判断两个对象是否相等
|
||||
* @param x
|
||||
* @param y
|
||||
*/
|
||||
equals(x: T, y: T): boolean;
|
||||
|
||||
/**
|
||||
* 生成对象的哈希码
|
||||
* @param value
|
||||
*/
|
||||
getHashCode(value: T): number;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ module es {
|
||||
/**
|
||||
* 用于管理一对对象的简单DTO
|
||||
*/
|
||||
export class Pair<T> implements IEquatable<Pair<T>> {
|
||||
export class Pair<T> implements IEqualityComparable {
|
||||
public first: T;
|
||||
public second: T;
|
||||
|
||||
@@ -19,5 +19,10 @@ module es {
|
||||
// 这两种方法在功能上应该是等价的
|
||||
return this.first == other.first && this.second == other.second;
|
||||
}
|
||||
|
||||
public getHashCode(): number {
|
||||
return EqualityComparer.default<T>().getHashCode(this.first) * 37 +
|
||||
EqualityComparer.default<T>().getHashCode(this.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
204
source/src/Utils/Set.ts
Normal file
204
source/src/Utils/Set.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
module es {
|
||||
export interface ISet<T> {
|
||||
add(item: T): boolean
|
||||
remove(item: T): boolean
|
||||
contains(item: T): boolean
|
||||
getCount(): number
|
||||
clear(): void
|
||||
toArray(): Array<T>
|
||||
/**
|
||||
* 从当前集合中删除指定集合中的所有元素
|
||||
* @param other
|
||||
*/
|
||||
exceptWith(other: Array<T>): void
|
||||
/**
|
||||
* 修改当前Set对象,使其只包含该对象和指定数组中的元素
|
||||
* @param other
|
||||
*/
|
||||
intersectWith(other: Array<T>): void
|
||||
/**
|
||||
* 修改当前的集合对象,使其包含所有存在于自身、指定集合中的元素,或者两者都包含
|
||||
* @param other
|
||||
*/
|
||||
unionWith(other: Array<T>): void
|
||||
isSubsetOf(other: Array<T>): boolean
|
||||
isSupersetOf(other: Array<T>): boolean
|
||||
overlaps(other: Array<T>): boolean
|
||||
setEquals(other: Array<T>): boolean
|
||||
}
|
||||
|
||||
interface IBucketsWithCount<T> {
|
||||
Buckets: Array<Array<T>>
|
||||
Count: number
|
||||
}
|
||||
|
||||
abstract class Set<T> implements ISet<T> {
|
||||
protected buckets: T[][];
|
||||
protected count: number;
|
||||
constructor(source?: Array<T>) {
|
||||
this.clear();
|
||||
if (source)
|
||||
source.forEach(value => {
|
||||
this.add(value);
|
||||
});
|
||||
}
|
||||
abstract getHashCode(item: T): number;
|
||||
abstract areEqual(value1: T, value2: T): boolean;
|
||||
|
||||
add(item: T) {
|
||||
let hashCode = this.getHashCode(item);
|
||||
let bucket = this.buckets[hashCode];
|
||||
if (bucket === undefined) {
|
||||
let newBucket = new Array<T>();
|
||||
newBucket.push(item);
|
||||
this.buckets[hashCode] = newBucket;
|
||||
this.count = this.count + 1;
|
||||
return true;
|
||||
}
|
||||
if (bucket.some((value) => this.areEqual(value,item)))
|
||||
return false;
|
||||
bucket.push(item);
|
||||
this.count = this.count + 1;
|
||||
return true;
|
||||
};
|
||||
|
||||
remove(item: T) {
|
||||
let hashCode = this.getHashCode(item);
|
||||
let bucket = this.buckets[hashCode];
|
||||
if (bucket === undefined) {
|
||||
return false;
|
||||
}
|
||||
let result = false;
|
||||
let newBucket = new Array<T>();
|
||||
bucket.forEach((value) => {
|
||||
if (!this.areEqual(value, item))
|
||||
newBucket.push(item);
|
||||
else
|
||||
result = true;
|
||||
});
|
||||
this.buckets[hashCode] = newBucket;
|
||||
if (result)
|
||||
this.count = this.count - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
contains(item: T) {
|
||||
return this.bucketsContains(this.buckets, item)
|
||||
};
|
||||
|
||||
getCount() {
|
||||
return this.count;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.buckets = new Array<Array<T>>();
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
toArray() {
|
||||
let result = new Array<T>()
|
||||
this.buckets.forEach(value => {
|
||||
value.forEach(inner => {
|
||||
result.push(inner);
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前集合中删除指定集合中的所有元素
|
||||
* @param other
|
||||
*/
|
||||
exceptWith(other: Array<T>) {
|
||||
if (other) {
|
||||
other.forEach(value => {
|
||||
this.remove(value);
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 修改当前Set对象,使其只包含该对象和指定数组中的元素
|
||||
* @param other
|
||||
*/
|
||||
intersectWith(other: Array<T>) {
|
||||
if (other) {
|
||||
let otherBuckets = this.buildInternalBuckets(other);
|
||||
this.toArray().forEach(value => {
|
||||
if (!this.bucketsContains(otherBuckets.Buckets, value))
|
||||
this.remove(value);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.clear();
|
||||
}
|
||||
}
|
||||
|
||||
unionWith(other: Array<T>) {
|
||||
other.forEach(value => {
|
||||
this.add(value);
|
||||
});
|
||||
}
|
||||
|
||||
isSubsetOf(other: Array<T>) {
|
||||
|
||||
let otherBuckets = this.buildInternalBuckets(other);
|
||||
return this.toArray().every(value => this.bucketsContains(otherBuckets.Buckets, value));
|
||||
|
||||
}
|
||||
isSupersetOf(other: Array<T>) {
|
||||
return other.every(value => this.contains(value));
|
||||
}
|
||||
|
||||
overlaps(other: Array<T>) {
|
||||
return other.some(value => this.contains(value));
|
||||
}
|
||||
|
||||
setEquals(other: Array<T>) {
|
||||
let otherBuckets = this.buildInternalBuckets(other);
|
||||
if (otherBuckets.Count !== this.count)
|
||||
return false
|
||||
return other.every(value => this.contains(value));
|
||||
}
|
||||
|
||||
private buildInternalBuckets(source: Array<T>): IBucketsWithCount<T> {
|
||||
let internalBuckets = new Array<Array<T>>();
|
||||
let internalCount = 0;
|
||||
source.forEach(item=> {
|
||||
let hashCode = this.getHashCode(item);
|
||||
let bucket = internalBuckets[hashCode];
|
||||
if (bucket === undefined) {
|
||||
let newBucket = new Array<T>();
|
||||
newBucket.push(item);
|
||||
internalBuckets[hashCode] = newBucket;
|
||||
internalCount = internalCount + 1;
|
||||
}
|
||||
else if (!bucket.some((value) => this.areEqual(value, item))) {
|
||||
bucket.push(item);
|
||||
internalCount = internalCount + 1;
|
||||
}
|
||||
});
|
||||
return { Buckets: internalBuckets, Count: internalCount };
|
||||
}
|
||||
|
||||
private bucketsContains(internalBuckets: Array<Array<T>>, item: T) {
|
||||
let hashCode = this.getHashCode(item);
|
||||
let bucket = internalBuckets[hashCode];
|
||||
if (bucket === undefined) {
|
||||
return false;
|
||||
}
|
||||
return bucket.some((value) => this.areEqual(value, item));
|
||||
}
|
||||
}
|
||||
|
||||
export class HashSet<T extends IEqualityComparable> extends Set<T> {
|
||||
constructor(source?: Array<T>) {
|
||||
super(source)
|
||||
}
|
||||
getHashCode(item: T) {
|
||||
return item.getHashCode();
|
||||
}
|
||||
areEqual(value1: T, value2: T) {
|
||||
return value1.equals(value2);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user