经常会遇到一些场景,比如把一个很大的对象保存于数组中,数组长度很长,遍历次数又多,消耗的时间比较久。

这个对象内有个ID字段是GUID或者UID,反正能保证它唯一。

作为都一名老程序员首先想到的是先排序,再二分法。瞬间优化提上去。但是javascript的字符串比较是个痛点,排序需要比较,二分查找的时候又需要比较。当然,如果主键是整数,建议还是排序再按二分法。另外Javascript有个很好的内置对象,叫做Map,可以很好的管理按主键快事查询,用来代替常常需要遍历的数组也是不错的选择。

于是我另辟蹊径做了个尝试,有些心得记录一下。

菜鸟做法

首先看看在大数组中常规的遍历效果:

var f = function(id) {
            for (var i = _this.instances.length - 1; i >= 0; i--) {
                if (_this.instances[i].id == id)
                    return _this.instances[i];
            }
            return null;
        }

       
         instance = f(obj.Ii);

这是在instances这个1.5万长度的数组里面找出一个id匹配的对象。耗时看一下:

Java根据id查询这个id下的所有子数据 根据id数组查询数据库_i++

进阶做法(Map)

别说话,看示例代码

let myMap = new Map();
// 添加键
myMap.set(key1, obj1);
myMap.set(key2, obj2);
myMap.set(key3, obj3);
 
myMap.size; // 3
 
// 读取值
myMap.get(key1);    // obj1

测算的效率如下,确实很666

 

Java根据id查询这个id下的所有子数据 根据id数组查询数据库_二分法_02

野蛮人做法

想了个小主意,用id的长字符串作为识别特征,把数组整理成树形表。比如ID的长度有36个字符,可以使用倒序读取字符,把字符作为key组建一个树表,如下图,理论上每一层最多可能有a..z+0..9,有36个节点。如果配置为5层,理论上如果足够随机,36^5=60466176个对象,随机落在每个叶子节点的数量平均只有1个。根据ID找到目标的遍历次数最小代价为5次比较,最高为36*5=180次比较。比起直接遍历平均需要7500次比较是快了许多。

Java根据id查询这个id下的所有子数据 根据id数组查询数据库_i++_03

好了,直接上代码。第一次构建此类,测试一下性能: 

function QmodelList(key1, obj1, level) {
    this.key = key1;
    this.obj = obj1;
    this.heepList = [];
    this.lv = level;
}

QmodelList.prototype = Object.create(QmodelList.prototype);
QmodelList.prototype.constructor = QmodelList;

QmodelList.prototype.add = function(obj, id) {
    this.addObj(obj, id, id.length - 1, this.lv - 1);
    return this;

};
QmodelList.prototype.find = function(id) {
    return this.findObj(id, id.length - 1);;
};
QmodelList.prototype.addObj = function(obj, id, keyindex, level) {
    var find = -1;
    var key = id[keyindex];
    
    for (var i = 0; i < this.heepList.length; i++) {
         if (this.heepList[i].key === key) {
             find = i;
             break;
         }
     }
    var o;
    if (find === -1) {
         o = new QmodelList(key, null, level);
         this.heepList.push(o);
     } else {
         o = this.heepList[i];
     }
    if (level === 0) {
        o.heepList.push(new QmodelList(id, obj));
    } else
        o.addObj(obj, id, keyindex - 1, o.lv - 1);
    return this;
};

QmodelList.prototype.findObj = function(id, keyindex) {
    var find = -1;
    var key = id[keyindex];
     for (var i = 0; i < this.heepList.length; i++) {
         if (this.heepList[i].key === key) {
             find = i;
             break;
         }
     }
    var o;
     if (find === -1) {
         return null;
     } else {
         o = this.heepList[find];
     }
    if (o.lv === 0) {
        for (var i = 0; i < o.heepList.length; i++) {
            if (o.heepList[i].key === id) {
                return o.heepList[i];
            }
        }
    } else
        return o.findObj(id, keyindex - 1, o.lv - 1);

}

当 instanceqm=new QmodelList(null,null,5),查找的效率如下,还不如Map算了!

Java根据id查询这个id下的所有子数据 根据id数组查询数据库_i++_04

 

勇士做法

成功了叫勇士,不成功叫莽夫!

在叶子节点,使用一个数组去存储长度上限为36的对象并不是很好的办法。为啥呢?通过for去查找一个36长度的数组,效率还是有些影响,正如上面所说,很不走运的时候,极限比较次数仍然需要180次。这些可以再次改造,大家用过javascript的都知道,比如你有一个对象obj,你可以这样赋值然后使用:obj.a={key:0,value:1},然后,obj就已经有属性a可以使用了。当obj的属性达到36个,从obj里面找一个属性更快,还是在一个数组[]里面循环对比出一个结果更快?这个需要看下测试结果:

废话不说,先上改造后的代码:

function QmodelList(key1, obj1, level) {
    this.key = key1;
    this.obj = obj1;
    this.heepList = [];
    this.lv = level;
}

QmodelList.prototype = Object.create(QmodelList.prototype);
QmodelList.prototype.constructor = QmodelList;

QmodelList.prototype.add = function(obj, id) {
    this.addObj(obj, id, id.length - 1, this.lv - 1);
    return this;

};
QmodelList.prototype.find = function(id) {
    return this.findObj(id, id.length - 1);;
};
QmodelList.prototype.addObj = function(obj, id, keyindex, level) {
    var key = id[keyindex];
    if (this[key] == null)
        this[key] = new QmodelList(key, null, level);
    var o = this[key];

    if (level === 0) {
        o.heepList.push(new QmodelList(id, obj));
    } else
        o.addObj(obj, id, keyindex - 1, o.lv - 1);
    return this;
};

QmodelList.prototype.findObj = function(id, keyindex) {
    
    var key = id[keyindex];   
    var o = this[key];   
    if (o.lv === 0) {
        for (var i = 0; i < o.heepList.length; i++) {
            if (o.heepList[i].key === id) {
                return o.heepList[i];
            }
        }
    } else
        return o.findObj(id, keyindex - 1, o.lv - 1);

}

同样,当 instanceqm=new QmodelList(null,null,5),查找的效率如下:加入的数组长度为1.5万。

Java根据id查询这个id下的所有子数据 根据id数组查询数据库_i++_05

又提升了几毫秒,666...,实际上因为随机的关系,每一层叶子,通常没有达到36个,就我这个案例而言,很多叶子到了第4层已经剩下1个对象了,找起来还是很快的。