세트가 Javascript에 비교적 새롭기 때문일 수 있지만 StackO 또는 다른 곳에서 Javascript에서 둘의 성능 차이에 대해 설명하는 기사를 찾을 수 없었습니다. 그렇다면 성능 측면에서 둘 사이의 차이점은 무엇입니까? 특히 제거, 추가 및 반복과 관련하여.
답변
좋아, 배열과 집합 모두에서 요소를 추가, 반복 및 제거하는 것을 테스트했습니다. 나는 10,000 개의 요소를 사용하는 “작은”테스트와 100,000 개의 요소를 사용하는 “큰”테스트를 실행했습니다. 결과는 다음과 같습니다.
컬렉션에 요소 추가
추가되는 요소의 수에 관계없이 .push
배열 방법은 .add
set 방법 보다 약 4 배 빠른 것 같습니다 .
컬렉션의 요소 반복 및 수정
테스트의이 부분에서는 for
루프를 사용하여 배열 for of
을 반복 하고 루프를 사용하여 세트를 반복했습니다. 다시 말하지만 어레이를 반복하는 것이 더 빨랐습니다. 이번에는 “작은”테스트에서는 두 배, “큰”테스트에서는 거의 4 배 더 오래 걸리므로 기하 급수적으로 보일 것입니다.
컬렉션에서 요소 제거
이제 이것이 흥미로워지는 곳입니다. 나는 for
루프 의 조합을 .splice
사용 for of
하고 배열에서 일부 요소 .delete
를 제거하고 세트에서 일부 요소를 사용 하고 제거했습니다. “작은”테스트의 경우 세트에서 항목을 제거하는 것이 약 3 배 더 빨랐지만 (2.6ms 대 7.1ms) “대형”테스트의 경우에는 항목을 배열에서 제거하는 데 1955.1ms가 소요되었지만 세트에서 제거하는 데 83.6ms가 걸렸습니다. 23 배 더 빨랐습니다.
결론
10k 요소에서 두 테스트 모두 비슷한 시간 (배열 : 16.6ms, 세트 : 20.7ms)을 실행했지만 100k 요소를 처리 할 때 세트가 확실한 승자였습니다 (배열 : 1974.8ms, 세트 : 83.6ms). 조작. 그렇지 않으면 어레이가 더 빨랐습니다. 그 이유를 정확히 말할 수 없었습니다.
배열이 생성되고 채워진 다음 일부 요소가 제거되는 집합으로 변환 된 다음 집합이 배열로 다시 변환되는 하이브리드 시나리오를 가지고 놀았습니다. 이렇게하면 배열에서 요소를 제거하는 것보다 훨씬 더 나은 성능을 제공 할 수 있지만 세트로 /에서 전송하는 데 필요한 추가 처리 시간이 세트 대신 배열을 채우는 이점보다 큽니다. 결국 세트 만 처리하는 것이 더 빠릅니다. 그럼에도 불구하고, 중복이없는 일부 빅 데이터에 대한 데이터 수집으로 어레이를 사용하기로 선택하면 하나에서 많은 요소를 제거해야하는 경우 성능 측면에서 유리할 수 있다는 것은 흥미로운 아이디어입니다. 배열을 집합으로 변환하고 제거 작업을 수행 한 다음 집합을 다시 배열로 변환합니다.
어레이 코드 :
var timer = function(name) {
var start = new Date();
return {
stop: function() {
var end = new Date();
var time = end.getTime() - start.getTime();
console.log('Timer:', name, 'finished in', time, 'ms');
}
}
};
var getRandom = function(min, max) {
return Math.random() * (max - min) + min;
};
var lastNames = ['SMITH', 'JOHNSON', 'WILLIAMS', 'JONES', 'BROWN', 'DAVIS', 'MILLER', 'WILSON', 'MOORE', 'TAYLOR', 'ANDERSON', 'THOMAS'];
var genLastName = function() {
var index = Math.round(getRandom(0, lastNames.length - 1));
return lastNames[index];
};
var sex = ["Male", "Female"];
var genSex = function() {
var index = Math.round(getRandom(0, sex.length - 1));
return sex[index];
};
var Person = function() {
this.name = genLastName();
this.age = Math.round(getRandom(0, 100))
this.sex = "Male"
};
var genPersons = function() {
for (var i = 0; i < 100000; i++)
personArray.push(new Person());
};
var changeSex = function() {
for (var i = 0; i < personArray.length; i++) {
personArray[i].sex = genSex();
}
};
var deleteMale = function() {
for (var i = 0; i < personArray.length; i++) {
if (personArray[i].sex === "Male") {
personArray.splice(i, 1)
i--
}
}
};
var t = timer("Array");
var personArray = [];
genPersons();
changeSex();
deleteMale();
t.stop();
console.log("Done! There are " + personArray.length + " persons.")
코드 설정 :
var timer = function(name) {
var start = new Date();
return {
stop: function() {
var end = new Date();
var time = end.getTime() - start.getTime();
console.log('Timer:', name, 'finished in', time, 'ms');
}
}
};
var getRandom = function (min, max) {
return Math.random() * (max - min) + min;
};
var lastNames = ['SMITH','JOHNSON','WILLIAMS','JONES','BROWN','DAVIS','MILLER','WILSON','MOORE','TAYLOR','ANDERSON','THOMAS'];
var genLastName = function() {
var index = Math.round(getRandom(0, lastNames.length - 1));
return lastNames[index];
};
var sex = ["Male", "Female"];
var genSex = function() {
var index = Math.round(getRandom(0, sex.length - 1));
return sex[index];
};
var Person = function() {
this.name = genLastName();
this.age = Math.round(getRandom(0,100))
this.sex = "Male"
};
var genPersons = function() {
for (var i = 0; i < 100000; i++)
personSet.add(new Person());
};
var changeSex = function() {
for (var key of personSet) {
key.sex = genSex();
}
};
var deleteMale = function() {
for (var key of personSet) {
if (key.sex === "Male") {
personSet.delete(key)
}
}
};
var t = timer("Set");
var personSet = new Set();
genPersons();
changeSex();
deleteMale();
t.stop();
console.log("Done! There are " + personSet.size + " persons.")
답변
관찰 :
- 집합 작업은 실행 스트림 내에서 스냅 샷으로 이해할 수 있습니다.
- 우리는 확실한 대체물이 아닙니다.
- Set 클래스 의 요소 에는 액세스 가능한 인덱스가 없습니다.
- Set 클래스 는 Array 클래스 보완으로, 기본 추가, 삭제, 검사 및 반복 작업을 적용 할 컬렉션을 저장해야하는 시나리오에서 유용합니다.
성능 테스트를 공유합니다. 콘솔을 열고 아래 코드를 복사하여 붙여 넣으십시오.
어레이 만들기 (125000)
var n = 125000;
var arr = Array.apply( null, Array( n ) ).map( ( x, i ) => i );
console.info( arr.length ); // 125000
1. 색인 찾기
Set의 has 메소드를 Array indexOf와 비교했습니다.
배열 / 같이 IndexOf (0.281ms) | 세트 / 가 (0.053ms)을
// Helpers
var checkArr = ( arr, item ) => arr.indexOf( item ) !== -1;
var checkSet = ( set, item ) => set.has( item );
// Vars
var set, result;
console.time( 'timeTest' );
result = checkArr( arr, 123123 );
console.timeEnd( 'timeTest' );
set = new Set( arr );
console.time( 'timeTest' );
checkSet( set, 123123 );
console.timeEnd( 'timeTest' );
2. 새 요소 추가
Set 및 Array 개체의 추가 및 푸시 메서드를 각각 비교합니다.
어레이 / 푸시 (1.612ms) | 설정 / 추가 (0.006ms)
console.time( 'timeTest' );
arr.push( n + 1 );
console.timeEnd( 'timeTest' );
set = new Set( arr );
console.time( 'timeTest' );
set.add( n + 1 );
console.timeEnd( 'timeTest' );
console.info( arr.length ); // 125001
console.info( set.size ); // 125001
3. 요소 삭제
요소를 삭제할 때 배열과 집합이 동일한 조건에서 시작되지 않는다는 점을 명심해야합니다. Array에는 네이티브 메서드가 없으므로 외부 함수가 필요합니다.
배열 / deleteFromArr (0.356ms) | 설정 / 제거 (0.019ms)
var deleteFromArr = ( arr, item ) => {
var i = arr.indexOf( item );
i !== -1 && arr.splice( i, 1 );
};
console.time( 'timeTest' );
deleteFromArr( arr, 123123 );
console.timeEnd( 'timeTest' );
set = new Set( arr );
console.time( 'timeTest' );
set.delete( 123123 );
console.timeEnd( 'timeTest' );
여기 에서 전체 기사 읽기
답변
내 관찰은 큰 배열에 대해 두 가지 함정을 염두에두고 Set이 항상 더 낫다는 것입니다.
a) 배열에서 세트를 생성하는 작업 for
은 미리 캐시 된 길이 의 루프 에서 수행되어야합니다 .
느림 (예 : 18ms) new Set(largeArray)
빠름 (예 : 6ms)
const SET = new Set();
const L = largeArray.length;
for(var i = 0; i<L; i++) { SET.add(largeArray[i]) }
b) 반복은 for of
루프 보다 빠르기 때문에 같은 방식으로 수행 할 수 있습니다 .
참조 https://jsfiddle.net/0j2gkae7/5/를
에 실제 비교
difference()
, intersection()
, union()
과 uniq()
40.000 요소 (+ 자신의 iteratee 동반자 등)
답변
질문의 반복 부분에 대해 최근에이 테스트를 실행 한 결과 Set가 10,000 개 항목의 배열보다 훨씬 우수한 성능을 보였습니다 (동일한 시간 내에 작업이 약 10 배 발생할 수 있음). 그리고 브라우저에 따라 비슷한 테스트를 위해 Object.hasOwnProperty를이기거나 잃었습니다.
Set과 Object는 모두 O (1)로 상각되는 것처럼 보이는 “has”메서드를 가지고 있지만 브라우저의 구현에 따라 단일 작업이 더 오래 걸리거나 더 빨라질 수 있습니다. 대부분의 브라우저는 Set.has ()보다 빠르게 Object에 키를 구현하는 것 같습니다. 키에 대한 추가 검사를 포함하는 Object.hasOwnProperty조차도 Chrome v86에서 적어도 Set.has ()보다 약 5 % 빠릅니다.
https://jsperf.com/set-has-vs-object-hasownproperty-vs-array-includes/1
업데이트 : 2020 년 11 월 11 일 : https://jsbench.me/irkhdxnoqa/2
다른 브라우저 / 환경에서 자체 테스트를 실행하려는 경우.
마찬가지로 배열과 세트에 항목을 추가하고 제거하는 벤치 마크를 추가합니다.
답변
console.time("set")
var s = new Set()
for(var i = 0; i < 10000; i++)
s.add(Math.random())
s.forEach(function(e){
s.delete(e)
})
console.timeEnd("set")
console.time("array")
var s = new Array()
for(var i = 0; i < 10000; i++)
s.push(Math.random())
s.forEach(function(e,i){
s.splice(i)
})
console.timeEnd("array")
10K 항목에 대한 세 가지 작업은 다음과 같은 이점을 제공합니다.
set: 7.787ms
array: 2.388ms