什么时候用数组,什么时候用对象?

When to use array and when to use object?

我最近读了很多关于对象和数组的文章,但出于某种原因它们有相反的信息/事实。我需要你的帮助来一劳永逸地学习:什么时候最好使用数组,什么时候使用对象?

显而易见的原因是当您需要特定顺序时使用数组。还有什么?这个例子怎么样?


请注意,此示例中我需要的数字要小得多(以千为单位)


我为这个问题写的代码,它有一些我永远不会做的事情,但是为了这个问题:

var x = [];
var y = [];
var z = [];

var clickedX = [];
var clickedY = [];
var clickedZ = [];

var count = 100;

for ( var a = 0; a < count; a++ ) {

    //For the sake of this example: random int 1, 2 or 3
    var type = Math.floor(Math.random() * 3) + 1;

    createDiv( type );
}


function createDiv( thisType ) {

    var self = this;
    var div = self.div;

    var type = thisType;

    if( !div ) {

        div = this.div = document.createElement( 'div' );
        div.className = 'marker';

        //Push to "right" array
        if( type == '1' ) {

            x.push( self );
        }
        else if ( type == '2' ) {

            y.push( self );
        }
        else {

            z.push( self );
        }

        //Attach click event
        div.onclick = function() {

            // X
            var indexX = x.indexOf( self );

            if( indexX > -1 ) { 

                //Push to new array
                clickedX.push( self );

                //Remove from old array
                x.splice( indexX, 1 );
            }

            // Y
            var indexY = y.indexOf( self );

            if( indexY > -1 ) { 

                //Push to new array
                clickedY.push( self );

                //Remove from old array
                y.splice( indexY, 1 );
            }

            // Z
            var indexZ = z.indexOf( self );

            if( indexZ > -1 ) { 

                //Push to new array
                clickedZ.push( self );

                //Remove from old array
                z.splice( indexZ, 1 );
            }
        }; // onclick
    } // if( !div )
} // createDiv()

我目前正在处理的数据示例:

// Data Im dealing with

objects = { 

    { objects1: [
                    0: { object : [4-5 assigned values (int, string, array)] }, 
                    1: { object : [4-5 assigned values (int, string, array)] },
                    //etc
                ] 
    }, 
    { objects2: [
                    0: {object}, 
                    1: {object},
                    //etc
                ] 
    } 
}

// 1. I need to iterate through every "object" in both "objects1" and "objects2"
// 2. I need to iterate through "array" in every "object" in "objects1"


for( /* "object" count in "objects1" */ ) {

    //Do something for each "object" in "objects1"

    for( /* items in "array" count */ ) {

        //Do something for each item in "array"
    }
}

// AND

for( /* "object" count in "objects2" */ ) {

    //Do something for each "object" in "objects2"
}

数组

基本上,当您有一个结果数据列表并且要将其作为一个集合使用时,您需要使用一个数组。

它很容易添加迭代获取计数检查为了存在.
很难删除 数组中的项目。

此外,它存储订单
比如你有10个整数,你要找最小的一个的索引,那么用数组就顺理成章了。

对象

使用对象,当你的项目有键(特别是,non-integer)并且你要一个一个地使用它们时。

方便添加删除检查是否存在属性。 但是,通过对象属性迭代不太方便,并且获取它的计数是绝对不合适的。

也无法存储物品的订单

时间复杂度

回答你关于推送、检查存在和删除的问题:

  • 属性 设置和数组推送都需要 O(1) 时间,但我坚信第一个慢一点
  • 检查是否存在需要相同的 O(1) 时间
  • 删除一个元素不是数组设计的目的 - 它需要移动所有项目并花费 O(n) 时间,而您可以为 O(1)
  • 删除一个 属性

错误的用法示例

有一些非常糟糕的用法。如果你有以下情况,那么你用错了数组或对象。

  1. 给数组分配一个随机值:

    var a = [];
    a[1000] = 1;
    

    它将数组的 length 属性 更改为 1001,如果您尝试输出此数组,您将得到以下内容:

    [undefined,undefined,undefined,undefined... 1]
    

    如果不是故意的,那绝对没用。

  2. 为对象分配结果值

     var a = {};
    
     for (var i = 0; i < 10; i++)
     {
         a[i] = 1;
     }
    

您的代码

谈到您的代码,有很多方法可以正确地做到这一点 - 只需选择一个。我会按以下方式进行:

var items = [];

for (var i = 0; i < count; i++)
{
    var o = {
        type: Math.floor(Math.random() * 3) + 1,
        isClicked: false
    };

    var div = document.createElement('div');
    div.className = 'marker';

    div.onclick = function() {
        o.isClicked = true;
    };

    o.div = div;
    items.push(o);
}

function GetDivs(type, isClicked)
{
    var result = items;
    if (type) 
        result = result.filter(function(x) { return x.type === type; });
    if (isClicked !== undefined) 
        result = result.filter(function(x) { return x.isClicked === isClicked; });
    return result;
}

然后,您将能够得到任何您想要的物品:

var all = GetItems(0); // or even GetDivs()
var allClicked = GetItems(0, true);
var allNotClicked = GetItems(0, false);

var divsTypeA = GetItems(1);
var clickedDivsTypeB = GetItems(2, true);
var notClickedDivsTypeC = GetItems(3, false);

使用这种用法,您根本不需要删除任何项目 - 您只需将它们标记为已点击或未点击,这需要 O(1) 时间。

jQuery

如果您使用 jQuery 和数据 HTML 属性,那么您根本不需要使用数组或对象:

for (var i = 0; i < count; i++)
{
    $('<div/>')
      .addClass('marker')
      .attr('data-type', Math.floor(Math.random() * 3) + 1)
      .appendTo('body')
      .click(function() {
          $(this).addClass('clicked');
      });
}

现在,您可以使用选择器查找任何项目:

var all = $('.marker');
var allClicked = $('.marker.clicked');
var allNotClicked = $('.marker:not(.clicked)');

var divsTypeA = $('.marker[data-type='1']');
var clickedDivsTypeB = $('.marker[data-type='2'].clicked');
var notClickedDivsTypeC = $('.marker[data-type='1']:not(.clicked)');

但是,如果您真的有数千条记录,最后一种方法可能会产生滞后。同时,在页面上放置 1000 个动态 div 是否是个好主意? :)

当所有其他方法都失败时 运行 测试,再次取决于您打算将数据结构用于什么,一切都是权衡,性能 ||效率。

准备等待几秒钟让测试完成。

function gimmeAHugeArray( length ){
  var arr = [];
  for( var ii = 0; ii < length; ii++ ){
    arr.push( (ii * 100000 ).toString(16) );
  }
  return arr;
}

function gimmeAHugeObject( length ){
  var obj = {};
  for( var ii = 0; ii < length; ii++ ){
    obj[ (ii * 100000 ).toString(16) ] = ii;
  }
  return obj;
}

var hugeArray = gimmeAHugeArray( 100000 );
var hugeObject = gimmeAHugeObject( 100000 );
var key = (8000 * 100000).toString(16);

console.perf(function arrayGetWithIndexOf(){
  var val = hugeArray.indexOf( key );
});

console.perf(function objectGetWithKey(){
  var val = hugeObject[ key ];
});

console.perf(function arrayIterateAllProperties(){
  var val = null;
  for( var ii = 0, ll = hugeArray.length; ii < ll; ii++ ){
    val = hugeArray[ii];
  }
}, 50);

console.perf(function objectIterateAllProperties(){
  var val = null,
      key;
  for( key in hugeObject ){
    val = hugeObject[ key ];
  }
}, 50);
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>

我将从 Solo 上面的评论中回答你的问题。

问题是如果我需要对相同的数据执行这两项操作怎么办?

我发现最好的方法(至少对我而言)是创建一个数组,它可能有空节点,但保留另一个数组来跟踪填充的 ID。

看起来像这样:

var MyArray = function()
{
    this.storage = [];
    this.ids = [];
};

MyArray.prototype.set = function(id, value)
{
    this.storage[id] = value;
    this.ids[this.ids.length] = id;
};

MyArray.prototype.get = function(id)
{
    return this.storage[id];
};

MyArray.prototype.delete = function(id)
{
    delete this.storage[id];
};

此解决方案适用于这两种方法,因为您可以在 O(1) 中删除任何内容,因为每个人都有给定的 ID,并且您可以轻松地对其进行迭代,因为您拥有所有 ID,这些值存储在.

我还创建了一个非常小的 class 供我自己使用,它的性能非常好。这是 link https://github.com/ImJustAskingDude/JavascriptFastArray . Here's a question I asked about these things: Javascript Array Performance .

请阅读我在那里写的内容,此解决方案适用于非常特殊的情况,我不知道它是否适用于您。

这就是为什么我要你阅读我提供的那些 link 中的内容,我已经写了大约 10 页的 material,尽管其中一些是纯粹的垃圾什么都不说。不管怎样,我也在这里写几个例子吧。

示例:

假设您有来自数据库的数据,来自数据库的数据非常需要具有唯一的整数 ID,但每个用户可能将它们分布在很宽的范围内,它们很可能不连续。

所以你从数据库中获取数据到对象中,就像这样:

var DBData /* well, probably something more descriptive than this stupid name */ = function(id, field1, field2, field3)
{
    this.id = id;
    this.field1 = field1;
    this.field2 = field2;
    this.fiedl3 = field3;
};

鉴于此 class,您将数据库中的所有数据写入它的实例,并将其存储在 MyArray 中。

var data = new MyArray();

// YOU CAN USE THIS, OR JUST READ FROM DOM
$.get(/* get data from DB into MyArray instance */, function(data) { /* parse it somehow */ var dbdata = new DBData(/* fields that you parsed including ID */);  data.set(ID, dbdata)});

当你想遍历所有数据时,你可以这样做:

只需创建一个函数来创建一个新数组,将所有数据都放在一个漂亮的连续块中,它会像这样:

    function hydrate(myarray, ids)
    {
        var result = [];

        var length = ids.length;

        if(!length)
        {
            return null;
        }

        var storage = myarray.storage;

        for(var i = 0; i < length; i++)
        {
            result[result.length] = storage[ids[i]];
        }

        return result;
    };

我使用这个方法,因为你可以用它select非常容易地将任何对象配置成一个连续的数组。

处​​理数组数组的一种解决方案是修改 MyArray 以专门处理它,或者制作一个版本来处理它。

一个不同的解决方案是重新考虑您的方法,也许将所有数据添加到一个 MyArray 中,但将所有

objects1: [
                    0: { object : [4-5 assigned values (int, string, array)] }, 
                    1: { object : [4-5 assigned values (int, string, array)] },
                    //etc
                ] 
    }, 
    { objects2: [
                    0: {object}, 
                    1: {object},
                    //etc
                ] 
    } 

变成漂亮的对象。

说真的,请阅读我在那些 link 中写的内容,那里有我对所有内容的推理。