您的位置:首页 > Web前端 > JavaScript

javascript实现原生的Java版本的HashMap及LinkedHashMap

2013-06-03 11:36 711 查看
javascript实现原生的HashMap及LinkedHashMap

用面象对象的方式去使用javascript,性能比较差,但功能完善。

也许在使用javascript时,应该要更多的使用该语言本身的特性。

/**
 * email:collonn@126.com
 * QQ:195358385
 */
var scriptjava = {
log : {
enable : false,
consoleEnable : function() {
return typeof console == 'undefined' ? false : true;
},
info : function(message) {
if (scriptjava.log.enable && scriptjava.log.consoleEnable()) {
console.log(message);
} else if(scriptjava.log.enable) {
alert(message);
}
},
warn : function(message) {
if (scriptjava.log.enable) {
console.warn(message);
}
}
},
util : {}
}

/*
 * null: return null '': return 0
 */
scriptjava.util.hashCode = function(str) {
var hash = 0;

if (arguments.length == 0) {
throw new Error('number of arguments must > 1');
}

// null: return null '': return 0
if (str == null || str == '') {
return hash;
}

// conver number to string
if (typeof str == 'number') {
str = new String(str);
}

for ( var i = 0; i < str.length; i++) {
hash = 31 * hash + str.charCodeAt(i);
}

return hash;
}

/**
 * --------------------------- scriptjava.util.HashMap define start ---------------------------
 */
scriptjava.util.HashMap = function(configObj) {
this._capacityDefault = 16;

// config property default
this._capacity = this._capacityDefault;
this._loadFactor = 0.75;
this._size = 0;
this._maxLoop = 100;

// config property custom
if (arguments.length == 1 && typeof configObj == 'object') {
for (property in configObj) {
this['_' + property] = configObj[property];
}
}

// config property other
this._maxSize = parseInt(this._capacity * this._loadFactor);
this._table = new Array(this._capacity);
}
/*
 * the key-value element of HashMap this method will be overwrite by LinkedHashMap
 */
scriptjava.util.HashMap.prototype.Entry = function(key, value) {
this.key = key, this.value = value, this.next = null
}

/*
 * this method will be overwrite by LinkedHashMap
 */
scriptjava.util.HashMap.prototype._createEntry = function(key, value){
return new this.Entry(key, value);
}

/*
 * this method will be overwrite by LinkedHashMap
 */
scriptjava.util.HashMap.prototype._addEntry = function(entry){

}

/*
 * this method will be overwrite by LinkedHashMap
 */
scriptjava.util.HashMap.prototype._recordAccess = function(entry){

}

/*
 * this method will be overwrite by LinkedHashMap
 */
scriptjava.util.HashMap.prototype._addEntry = function(entry){

}

//Returns an Array of the keys contained in this map.
scriptjava.util.HashMap.prototype.keySet = function(){
scriptjava.log.info('HashMap.keySet()...');
var keyAry = new Array(this._size);
for ( var i = 0; i < this._table.length; i++) {
if (this._table[i] == undefined) {
continue;
}
var entryTemp = this._table[i];
while (entryTemp != null) {
keyAry.push(entryTemp.key);
entryTemp = entryTemp.next;
}
}
return keyAry;
}

/*
 * Removes all of the mappings from this map.
 * The map will be empty after this call returns.
 */
scriptjava.util.HashMap.prototype.clear = function(key, value) {
// for ( var i = 0; i < this._table.length; i++) {
// delete this._table[i];
// }
scriptjava.log.info('HashMap.clear()...');
this._capacity = this._capacityDefault;
this._table = new Array(this._capacity);
this._maxSize = parseInt(this._capacity * this._loadFactor);
this._size = 0;

if(this._head != undefined){
this._head = null;
}

if(this._tail != undefined){
this._tail = null;
}
}

/*
 * Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.
 */
scriptjava.util.HashMap.prototype.put = function(key, value) {
var returnEntry = null;
var findByKey = false;

// rebuild map when need
if (this._size == this._maxSize) {
scriptjava.log.warn('WARNNING, Map is nearly full, size:' + this._size + ', maxSize:' + this._maxSize + ', capacity:' + this._capacity);
this._resize();
}

var hash = this._reHash(scriptjava.util.hashCode(key));
var indexOfKey = this._indexFor(hash, this._capacity);
scriptjava.log.info('Map.put(' + key + ',' + value + '),' + 'indexOfKey:' + indexOfKey);

var entryNew = this._createEntry(key, value);
returnEntry = entryNew;
// put the new entry on the right index of table
if (this._table[indexOfKey] == undefined) {
this._table[indexOfKey] = entryNew;
this._addEntry(entryNew);
} else {
// if key already exists, then update the value of this key
var alreadyExists = false;
var entryTemp = this._table[indexOfKey];
while (entryTemp != null) {
if (entryTemp.key == key) {
alreadyExists = true;
findByKey = true;
entryTemp.value = value;
returnEntry = entryTemp;
break;
} else {
entryTemp = entryTemp.next;
}
}

if (!alreadyExists) {
entryNew.next = this._table[indexOfKey];
this._table[indexOfKey] = entryNew;
this._addEntry(entryNew);
}
}

if (!findByKey) {
this._size++;
}

//the put() opretion will add new or update value, so, will always execute this._recordAccess()
if(this._accessOrderPut != undefined && this._accessOrderPut == true){
this._recordAccess(entryNew);
}
return returnEntry;
}

/*
 * this method will be overwrite by LinkedHashMap
 */
scriptjava.util.HashMap.prototype._removeEntry = function(entry){

}

/*
 * Removes the mapping for the specified key from this map if present.
 */
scriptjava.util.HashMap.prototype.remove = function(key) {
var returnEntry = null;
var hash = this._reHash(scriptjava.util.hashCode(key));
var index = this._indexFor(hash, this._capacity);
var findByKey = false;

if (this._table[index] == undefined) {
return null;
} else {
var entryTemp = this._table[index];
var entryTempPre = null;
while (entryTemp != null) {
if (entryTemp.key == key) {
findByKey = true;
returnEntry = entryTemp;

if (entryTempPre == null && entryTemp.next == null) {
// only one entry in this entryList
delete this._table[index];
} else if (entryTempPre == null && entryTemp.next != null) {
// at least 2 entry in this entryList, and, is the head
this._table[index] = entryTemp.next;
} else if (entryTempPre != null && entryTemp.next == null) {
// at least 2 entry in this entryList, and, is the tail
entryTempPre.next = null;
} else {
// at least 3 entry in this entryList, and, is the middle
entryTempPre.next = entryTemp.next;
}

scriptjava.log.info('Map.romove(' + key + '), value:' + (returnEntry == null ? null : returnEntry.value));
this._removeEntry(entryTemp);
break;
}

entryTempPre = entryTemp;
entryTemp = entryTemp.next;
}
}

if (findByKey) {
this._size--;
}
return returnEntry;
}

/*
 * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
 */
scriptjava.util.HashMap.prototype.get = function(key) {
var hash = this._reHash(scriptjava.util.hashCode(key));
var index = this._indexFor(hash, this._capacity);

if (this._table[index] == undefined) {
return null;
} else {
var i = 0;
var valueFinal = null;
var entryTemp = this._table[index];

while (entryTemp != null) {
i++;
if (i > this._maxLoop) {
break;
scriptjava.log.warn('too mach loops, performents will be down:' + i);
}

if (entryTemp.key == key) {
valueFinal = entryTemp.value;
scriptjava.log.info('Map.get(' + key + '),' + 'value:' + valueFinal + ',loops:' + i);
this._recordAccess(entryTemp);
break;
} else {
entryTemp = entryTemp.next;
}
}

return valueFinal;
}
}

/*
 * Returns true if this map contains a mapping for the specified key.
 */
scriptjava.util.HashMap.prototype.containsKey = function(key) {
var hash = this._reHash(scriptjava.util.hashCode(key));
var index = this._indexFor(hash, this._capacity);

var contains = false;
if (this._table[index] == undefined) {
scriptjava.log.info('Map.containsKey(' + key + '), ' + contains);
return contains;
}

var entryTemp = this._table[index];
while (entryTemp != null) {
if (entryTemp.key == key) {
contains = true;
break;
} else {
entryTemp = entryTemp.next;
}
}

scriptjava.log.info('Map.containsKey(' + key + '), ' + contains);
return contains;
}

/*
 * for each element of hashmap, call an costom function
 */
scriptjava.util.HashMap.prototype.foreach = function(foreachFun) {
scriptjava.log.info('HashMap.foreach() start..............');
for ( var i = 0; i < this._table.length; i++) {
if (this._table[i] == undefined) {
continue;
}
var entryTemp = this._table[i];
while (entryTemp != null) {
foreachFun(entryTemp);
entryTemp = entryTemp.next;
}
}
scriptjava.log.info('HashMap.foreach() enddd..............');
}

/*
 * Returns the number of key-value mappings in this map.
 */
scriptjava.util.HashMap.prototype.size = function() {
scriptjava.log.info('Map.size(),' + this._size);
return this._size;
}

/*
 * Returns true if this map contains a mapping for the specified key.
 */
scriptjava.util.HashMap.prototype.isEmpty = function() {
return this._size == 0 ? true : false;
}

/*
 * Rehashes the contents of this map into a new array with a larger capacity. This method is called automatically when the number of keys in this map
 * reaches its threshold.
 */
scriptjava.util.HashMap.prototype._resize = function() {
var capacityNew = this._capacity * 2;
var maxSizeNew = parseInt(capacityNew * this._loadFactor);
var tableNew = new Array(capacityNew);

scriptjava.log.info('rebuild map,_capacity:' + capacityNew);
scriptjava.log.info('rebuild map,_loadFactor:' + this._loadFactor);
scriptjava.log.info('rebuild map,_maxSize:' + maxSizeNew);

for ( var i = 0; i < this._table.length; i++) {
if (this._table[i] == undefined) {
continue;
}

var entryTemp = this._table[i];
while (entryTemp != null) {
var hash = this._reHash(scriptjava.util.hashCode(entryTemp.key));
var indexOfKey = this._indexFor(hash, capacityNew);
scriptjava.log.info('rebuild map, key:' + entryTemp.key + ', value:' + entryTemp.value + ', indexOfKey:' + indexOfKey);

var entryNew = new this.Entry(entryTemp.key, entryTemp.value, null);
// put the new entry on the right index of table
if (tableNew[indexOfKey] == undefined) {
tableNew[indexOfKey] = entryNew;
} else {
entryNew.next = tableNew[indexOfKey];
tableNew[indexOfKey] = entryNew;
}

entryTemp = entryTemp.next;
}
}

this._capacity = capacityNew;
this._maxSize = maxSizeNew;
this._table = tableNew;
}

scriptjava.util.HashMap.prototype._reHash = function(h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}

scriptjava.util.HashMap.prototype._indexFor = function(h, length) {
return h & (length - 1);
}
/**
 * --------------------------- scriptjava.util.HashMap define enddd ---------------------------
 */

/**
 * --------------------------- scriptjava.util.LinkedHashMap define start ---------------------------
 */
scriptjava.util.LinkedHashMap = function(configObj){
//extends from scriptjava.util.HashMap
scriptjava.util.HashMap.call(this);

this._head = null;
this._tail = null;
//when true, the get() method will influce the access order
this._accessOrder = false;
//when true, the put() method will influce the access order alse, but it always be false
this._accessOrderPut = false;

// config property custom
if (arguments.length == 1 && typeof configObj == 'object') {
for (property in configObj) {
this['_' + property] = configObj[property];
}
}
}

//extends from scriptjava.util.HashMap
scriptjava.util.LinkedHashMap.prototype = new scriptjava.util.HashMap();
scriptjava.util.LinkedHashMap.prototype.constructor = scriptjava.util.LinkedHashMap;

// overwrite Entry
scriptjava.util.LinkedHashMap.prototype.Entry = function(key, value) {
this.key = key, this.value = value, this.next = null, this.before = null, this.after = null, this.count = 0;
}

// overwrite _createEntry()
scriptjava.util.LinkedHashMap.prototype._createEntry = function(key, value){
return new this.Entry(key, value);
}

// overwrite _addEntry(), add an entry to the linkedlist tail
scriptjava.util.LinkedHashMap.prototype._addEntry = function(entry){
if(this._head == null && this._tail == null){
this._head = entry;
this._tail = entry;
}else{
entry.before = this._tail;
this._tail.after = entry;
this._tail = entry;
}
}

/*
 * remove an element from linkedlist
 */
scriptjava.util.LinkedHashMap.prototype._removeEntry = function(entry){
if(entry == this._head && entry.after == null){// only one entry
this._head = null;
this._tail = null;
}else if(entry == this._head && entry.after != null){// at least two entry, and, the head
this._head = entry.after;
}else if(entry == this._tail){// at least two entry, and, the tail
entry.before.after = null;
this._tail = entry.before;
}else{//at least three entry, and, the middle
entry.before.after = entry.after;
this._tail = entry.after;
}
}

// overwrite _createEntry()
scriptjava.util.LinkedHashMap.prototype._recordAccess = function(entry){
if(!this._accessOrder){
return;
}

//linkedlist is empty
if(this._head == null && this._tail == null){
this._head = entry;
this._tail = entry;
return;
}

if(entry == this._head){
return;
}

//the last one of linkedlist
if(entry == this._tail && entry != this._head){
entry.before.after = entry.after;
this._tail = entry.before;
entry.before = null;
entry.after = this._head;
this._head.before = entry;
this._head = entry;
return;
}

//middle of the linkedlist
if(entry != this._head && entry != this._tail){
entry.before.after = entry.after;
entry.after.before = entry.before;
entry.after = this._head;
entry.before = null;
this._head.before = entry;
this._head = entry;
return;
}
}

// overwrite _addEntry()
scriptjava.util.LinkedHashMap.prototype.foreach = function(foreachFun){
scriptjava.log.info('LinkedHashMap.foreach() start...');
var tempEntry = this._head;
while(tempEntry != null){
foreachFun(tempEntry);
tempEntry = tempEntry.after;
}
scriptjava.log.info('LinkedHashMap.foreach() enddd...');
}
/**
 * --------------------------- scriptjava.util.LinkedHashMap define enddd ---------------------------
 */

/**
 * --------------------------- scriptjava.util.ArrayList define Start ---------------------------
 */
scriptjava.util.ArrayList = function(configObj){
this._capacityDefault= 16;

// config property default
this._capacity = this._capacityDefault;
this._size = 0;

// config property custom
if (arguments.length == 1 && typeof configObj == 'object') {
for (property in configObj) {
this['_' + property] = configObj[property];
}
}
}
/**
 * --------------------------- scriptjava.util.ArrayList define enddd ---------------------------
 */
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: