JavaScript에서 배열 대 객체 효율성


145

수천 개의 객체가있는 모델이 있습니다. ID가 있으면 저장하고 단일 객체를 검색하는 가장 효율적인 방법이 무엇인지 궁금합니다. ID는 긴 숫자입니다.

이것이 제가 생각한 두 가지 옵션입니다. 옵션 1에서는 증가하는 인덱스가있는 간단한 배열입니다. 옵션 2에서 그것은 연관 배열이며 아마도 차이가 있다면 객체 일 것입니다. 내 질문은 주로 단일 객체를 검색해야하지만 때로는 반복하여 정렬해야 할 때 어느 것이 더 효율적인지입니다.

비 연관 배열이있는 옵션 1 :

var a = [{id: 29938, name: 'name1'},
         {id: 32994, name: 'name1'}];
function getObject(id) {
    for (var i=0; i < a.length; i++) {
        if (a[i].id == id) 
            return a[i];
    }
}

연관 배열이있는 옵션 2 :

var a = [];  // maybe {} makes a difference?
a[29938] = {id: 29938, name: 'name1'};
a[32994] = {id: 32994, name: 'name1'};
function getObject(id) {
    return a[id];
}

최신 정보:

좋아, 두 번째 옵션에서 배열을 사용하는 것은 의문의 여지가 없습니다. 따라서 두 번째 옵션의 선언 줄은 실제로 다음 var a = {};과 같아야합니다. 단지 질문은 주어진 ID를 가진 객체를 검색 할 때 더 나은 성능을 나타내는 것입니다 : 배열 또는 id가 키인 객체.

또한 목록을 여러 번 정렬 해야하는 경우 답변이 변경됩니까?



정렬 된 컬렉션이 항상 필요합니까? 그렇다면 배열과 다른 옵션은 없습니다 (현재와 같은 색인을 사용하지는 않지만).
Jon

@Jon 실제로, 나는한다. "현재 당신처럼"은 무슨 뜻입니까?
Moshe Shaham 2016 년

1
@MosheShaham : 배열 (0)부터 시작하는 연속 인덱스가 있어야합니다. 배열을 사용하는 경우 다른 작업을 수행하지 마십시오.
Jon

이 벤치 마크가 귀하의 질문의 첫 번째 부분에 답할 것이라고 생각합니다 : jsben.ch/#/Y9jDP
EscapeNetscape

답변:


143

짧은 버전 : 배열은 대부분 객체보다 빠릅니다. 그러나 100 % 올바른 해결책은 없습니다.

2017 업데이트-테스트 및 결과

var a1 = [{id: 29938, name: 'name1'}, {id: 32994, name: 'name1'}];

var a2 = [];
a2[29938] = {id: 29938, name: 'name1'};
a2[32994] = {id: 32994, name: 'name1'};

var o = {};
o['29938'] = {id: 29938, name: 'name1'};
o['32994'] = {id: 32994, name: 'name1'};

for (var f = 0; f < 2000; f++) {
    var newNo = Math.floor(Math.random()*60000+10000);
    if (!o[newNo.toString()]) o[newNo.toString()] = {id: newNo, name: 'test'};
    if (!a2[newNo]) a2[newNo] = {id: newNo, name: 'test' };
    a1.push({id: newNo, name: 'test'});
}

테스트 설정 시험 결과

원본 게시물-설명

귀하의 질문에 약간의 오해가 있습니다.

Javascript에는 연관 배열이 없습니다. 배열과 객체 만.

이들은 배열입니다 :

var a1 = [1, 2, 3];
var a2 = ["a", "b", "c"];
var a3 = [];
a3[0] = "a";
a3[1] = "b";
a3[2] = "c";

이것은 또한 배열입니다.

var a3 = [];
a3[29938] = "a";
a3[32994] = "b";

모든 배열에는 지속적인 인덱싱이 있기 때문에 기본적으로 구멍이있는 배열입니다. 구멍이없는 어레이보다 느립니다. 그러나 배열을 수동으로 반복하는 것이 훨씬 느립니다 (대부분).

이것은 객체입니다.

var a3 = {};
a3[29938] = "a";
a3[32994] = "b";

다음은 세 가지 가능성에 대한 성능 테스트입니다.

조회 배열 대 홀리 배열 대 개체 성능 테스트

Smashing Magazine에서 이러한 주제에 대한 훌륭한 자료 : 빠른 메모리 효율적인 JavaScript 작성


1
@Moshe Javascript의 성능에 대한 모든 논의가 이루어져야합니다. : P
죽음

9
이것은 실제로 작업중인 데이터의 크기와 데이터에 따라 다릅니다. 아주 작은 데이터 세트와 작은 객체는 배열에서 훨씬 더 잘 수행됩니다. 조회에 대해 객체를 맵으로 사용하는 큰 데이터 세트에서 말하는 경우 객체가 더 효율적입니다. jsperf.com/array-vs-object-performance/35
f1v

5
f1v에 동의하지만, 개정 (35)는 테스트에서 결함을 가지고 if (a1[i].id = id) result = a1[i];해야합니다 if (a1[i].id === id) result = a1[i];테스트 http://jsperf.com/array-vs-object-performance/37 교정하는 그
찰스 번

1
http://jsperf.com/array-vs-object-performance/71을 참조하십시오 . 5000에 비해 약 93 개 개체의 데이터 하위 집합 (데이터 생성을 위해 반복해야하지만 배열에 구멍이 필요함)이 있습니다. 외부 루프는 개체 배열 (중간 및 끝에서 시작)에서 흩어지는 ID입니다. 누락 된 ID도 추가 했으므로 배열 조회가 모든 요소를 ​​통과해야합니다. 홀리 배열, 키별 개체, 수동 배열 f1v가 언급했듯이 실제로 데이터의 크기와 수동 배열 검색을위한 데이터의 위치에 따라 다릅니다.
Charles Byrne

4
이 답변은이 게시물의 jsPerf 결론을 요약하여 개선 할 수 있습니다. 특히 jsPerf 결과가 질문에 대한 실제 답변이기 때문에. 나머지는 추가입니다. 이것은 jsPerf가 다운되었을 때 (지금처럼) 더 관련성이 있습니다. meta.stackexchange.com/questions/8231/…
Jeff

23

배열과 객체는 매우 다르게 작동하거나 (적어도 예상해야 함) 실제로 성능 문제는 아닙니다. 0..n객체는 임의의 키를 임의의 값에 매핑하는 반면, 배열에는 연속적인 인덱스가 있습니다. 경우 당신이 특정 키를 제공 할 수있는 유일한 선택은 개체입니다. 키를 신경 쓰지 않으면 배열입니다.

배열에 임의의 (숫자) 키를 설정하려고하면 실제로는 성능 손실이 발생합니다 . 행동 적으로 배열은 그 사이의 모든 인덱스를 채 웁니다.

> foo = [];
  []
> foo[100] = 'a';
  "a"
> foo
  [undefined, undefined, undefined, ..., "a"]

(배열에는 실제로 99 개의 undefined 포함되어 있지 않지만 어떤 시점에서 배열을 반복 하기 때문에 이런 식으로 작동합니다 .)

두 옵션의 리터럴은 사용 방법을 매우 명확하게해야합니다.

var arr = ['foo', 'bar', 'baz'];     // no keys, not even the option for it
var obj = { foo : 'bar', baz : 42 }; // associative by its very nature

특정 키를 제공하고 싶지 않습니다. 나는 무엇을 더 잘하고 싶은지 알고 싶습니다. 두 번째 옵션에서는 배열이 문제가 아닙니다. 그러나 객체와 비 연관 배열은 어떻습니까?
Moshe Shaham 2016 년

1
@Moshe Javascript에는 비 연관 배열과 같은 것이 없습니다. 키 (숫자 또는 문자열)가 필요한 경우 객체를 사용하십시오. (순서대로) 목록이 필요하면 배열을 사용하십시오. 기간. 퍼포먼스는 토론에 참여하지 않습니다. 퍼포먼스가 중요하고 어떤 방식 으로든 키를 사용할 수 있다면 어떤 것이 더 잘 작동하는지 시도하십시오.
deceze

5
그러나 배열에서 루프를 통해 객체를 검색하거나 id가 키인 "associative"객체에서 객체를 검색하는 것이 더 나은 성능을 알고 싶습니다. 질문이 명확하지 않은 경우 죄송합니다.
Moshe Shaham

2
@Moshe 객체 또는 배열에서 키로 무언가에 액세스하면 원하는 것을 찾으려고 컨테이너를 반복하는 것보다 항상 훨씬 빠릅니다. 배열이나 객체에서 키로 항목에 액세스하는 차이는 무시할 수 있습니다. 루핑은 어느 쪽이든 분명히 나쁘다.
deceze

1
@deceze — "사용자 객체를 보유하는 배열과 사용자의 객체를 얻는 방법에 대해 user_id"키를 user_id사용하여 사용자 객체에 액세스 할 수 있으므로 키를 갖는 객체 "를 기반으로 사용자 객체를 가져 오려면 루프가 필요 user_id합니까? 성능면에서 어느 것이 더 낫습니까? 이에 대한 제안은 감사합니다 :)
Rayon

13

ES6에서 가장 성능이 좋은 방법은 맵을 사용하는 것입니다.

var myMap = new Map();

myMap.set(1, 'myVal');
myMap.set(2, { catName: 'Meow', age: 3 });

myMap.get(1);
myMap.get(2);

오늘 shim ( https://github.com/es-shims/es6-shim )을 사용하여 ES6 기능을 사용할 수 있습니다 .

성능은 브라우저 및 시나리오에 따라 다릅니다. 그러나 Map가장 성능이 좋은 예 는 다음과 같습니다. https://jsperf.com/es6-map-vs-object-properties/2


참조 https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map


11
이를 백업 할 리소스가 있습니까? 지금까지 내가 관찰 한 바에 따르면 ES6 세트는 어레이보다 빠르지 만 ES6 맵은 객체와 어레이보다 느리다
Steel Brain

1
더 성능이 아니라 "의미 적"이며 문제였습니다.
AlexG

3
@AlexG는 제목이 명확하게 기술되어 있다고 확신합니다 efficiency.
Qix-모니카가

@Qix 예, 내 나쁜 : o
AlexG

8

에서 NodeJS 당신이 알고있는 경우ID 는 배열을 통해 루프가 매우 느린 비교됩니다 object[ID].

const uniqueString = require('unique-string');
const obj = {};
const arr = [];
var seeking;

//create data
for(var i=0;i<1000000;i++){
  var getUnique = `${uniqueString()}`;
  if(i===888555) seeking = getUnique;
  arr.push(getUnique);
  obj[getUnique] = true;
}

//retrieve item from array
console.time('arrTimer');
for(var x=0;x<arr.length;x++){
  if(arr[x]===seeking){
    console.log('Array result:');
    console.timeEnd('arrTimer');
    break;
  }
}

//retrieve item from object
console.time('objTimer');
var hasKey = !!obj[seeking];
console.log('Object result:');
console.timeEnd('objTimer');

그리고 결과 :

Array result:
arrTimer: 12.857ms
Object result:
objTimer: 0.051ms

탐색 ID가 배열 / 객체의 첫 번째 ID 인 경우에도 :

Array result:
arrTimer: 2.975ms
Object result:
objTimer: 0.068ms

5

나는 이것을 문자 그대로 다음 차원으로 가져 가려고 노력했다.

x 축과 y 축의 길이가 항상 같은 2 차원 배열을 사용하면 다음과 같이하는 것이 더 빠릅니다.

a) 2 차원 배열을 만들고 첫 번째 인덱스를 찾은 다음 두 번째 인덱스를 찾아서 셀을 찾습니다.

var arr=[][]    
var cell=[x][y]    

또는

b) x 및 y 좌표의 문자열 표현으로 객체를 생성 한 다음 해당 obj에서 단일 조회를 수행합니다.

var obj={}    
var cell = obj['x,y']    

결과 :
객체에서 하나의 속성 조회보다 배열에서 두 개의 숫자 인덱스 조회를 수행하는 것이 훨씬 빠르다는 것이 밝혀졌습니다.

결과는 다음과 같습니다.

http://jsperf.com/arr-vs-obj-lookup-2


3

사용법에 따라 다릅니다. 케이스가 조회 오브젝트 인 경우 매우 빠릅니다.

다음은 배열 및 객체 조회 성능을 테스트하는 Plunker 예제입니다.

https://plnkr.co/edit/n2expPWVmsdR3zmXvX4C?p=preview

당신은 그것을 볼 것입니다; 5.000 길이 배열 컬렉션 에서 5.000 개의 항목을 찾고 , milisecon 을 인수3000

그러나 대한 찾고 5.000 객체의 항목은이 5.000 속성을 가지고에만 2또는 3milisecons을

또한 객체 트리를 만드는 것은 큰 차이를 만들지 않습니다.


0

나는 x 항목으로 제한된 이벤트 소스에서 라이브 촛대를 보관 해야하는 것과 비슷한 문제가 발생했습니다. 각 촛불의 타임 스탬프가 열쇠로 작용하고 촛불 자체가 가치로 작용하는 물체에 보관할 수 있습니다. 다른 가능성은 각 항목이 촛불 자체 인 배열에 저장할 수 있다는 것입니다. 라이브 양초의 한 가지 문제점은 최신 업데이트가 최신 데이터를 보유하는 동일한 타임 스탬프에서 업데이트를 계속 전송하므로 기존 항목을 업데이트하거나 새 항목을 추가한다는 것입니다. 여기에 세 가지 가능성을 모두 결합하려는 훌륭한 벤치 마크가 있습니다. 아래 솔루션의 어레이는 평균 4 배 이상 빠릅니다. 자유롭게 연주하십시오

"use strict";

const EventEmitter = require("events");
let candleEmitter = new EventEmitter();

//Change this to set how fast the setInterval should run
const frequency = 1;

setInterval(() => {
    // Take the current timestamp and round it down to the nearest second
    let time = Math.floor(Date.now() / 1000) * 1000;
    let open = Math.random();
    let high = Math.random();
    let low = Math.random();
    let close = Math.random();
    let baseVolume = Math.random();
    let quoteVolume = Math.random();

    //Clear the console everytime before printing fresh values
    console.clear()

    candleEmitter.emit("candle", {
        symbol: "ABC:DEF",
        time: time,
        open: open,
        high: high,
        low: low,
        close: close,
        baseVolume: baseVolume,
        quoteVolume: quoteVolume
    });



}, frequency)

// Test 1 would involve storing the candle in an object
candleEmitter.on('candle', storeAsObject)

// Test 2 would involve storing the candle in an array
candleEmitter.on('candle', storeAsArray)

//Container for the object version of candles
let objectOhlc = {}

//Container for the array version of candles
let arrayOhlc = {}

//Store a max 30 candles and delete older ones
let limit = 30

function storeAsObject(candle) {

    //measure the start time in nanoseconds
    const hrtime1 = process.hrtime()
    const start = hrtime1[0] * 1e9 + hrtime1[1]

    const { symbol, time } = candle;

    // Create the object structure to store the current symbol
    if (typeof objectOhlc[symbol] === 'undefined') objectOhlc[symbol] = {}

    // The timestamp of the latest candle is used as key with the pair to store this symbol
    objectOhlc[symbol][time] = candle;

    // Remove entries if we exceed the limit
    const keys = Object.keys(objectOhlc[symbol]);
    if (keys.length > limit) {
        for (let i = 0; i < (keys.length - limit); i++) {
            delete objectOhlc[symbol][keys[i]];
        }
    }

    //measure the end time in nano seocnds
    const hrtime2 = process.hrtime()
    const end = hrtime2[0] * 1e9 + hrtime2[1]

    console.log("Storing as objects", end - start, Object.keys(objectOhlc[symbol]).length)
}

function storeAsArray(candle) {

    //measure the start time in nanoseconds
    const hrtime1 = process.hrtime()
    const start = hrtime1[0] * 1e9 + hrtime1[1]

    const { symbol, time } = candle;
    if (typeof arrayOhlc[symbol] === 'undefined') arrayOhlc[symbol] = []

    //Get the bunch of candles currently stored
    const candles = arrayOhlc[symbol];

    //Get the last candle if available
    const lastCandle = candles[candles.length - 1] || {};

    // Add a new entry for the newly arrived candle if it has a different timestamp from the latest one we storeds
    if (time !== lastCandle.time) {
        candles.push(candle);
    }

    //If our newly arrived candle has the same timestamp as the last stored candle, update the last stored candle
    else {
        candles[candles.length - 1] = candle
    }

    if (candles.length > limit) {
        candles.splice(0, candles.length - limit);
    }

    //measure the end time in nano seocnds
    const hrtime2 = process.hrtime()
    const end = hrtime2[0] * 1e9 + hrtime2[1]


    console.log("Storing as array", end - start, arrayOhlc[symbol].length)
}

결론 10은 여기서 한계입니다

Storing as objects 4183 nanoseconds 10
Storing as array 373 nanoseconds 10

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.