Java의 ArrayList 및 LinkedList 스트리밍 및 성능 분석
14733 단어 javaarraylistlinkedlist두루 다니다
본고를 통해 당신은 List의 다섯 가지 역행 방식과 각자의 성능과foreach 및 Iterator의 실현을 이해하고 Array List와Linked List의 실현에 대한 이해를 깊이 있게 할 수 있습니다.다음은 같이 봅시다.
1. List의 5가지 반복 방식
1. for each 순환
List<Integer> list = new ArrayList<Integer>();
for (Integer j : list) {
// use j
}
2. 디스플레이 호출 집합 교체기
List<Integer> list = new ArrayList<Integer>();
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();) {
iterator.next();
}
또는
List<Integer> list = new ArrayList<Integer>();
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
}
3. 아래 표의 점차적인 순환, 종료 조건은 매번size() 함수 비교 판단
List<Integer> list = new ArrayList<Integer>();
for (int j = 0; j < list.size(); j++) {
list.get(j);
}
4. 아래 표의 점차적인 순환, 종료 조건은size()와 같은 임시 변수 비교 판단
List<Integer> list = new ArrayList<Integer>();
int size = list.size();
for (int j = 0; j < size; j++) {
list.get(j);
}
5. 아래 표의 체감 순환
List<Integer> list = new ArrayList<Integer>();
for (int j = list.size() - 1; j >= 0; j--) {
list.get(j);
}
List 5가지 반복 방식의 성능 테스트 및 비교다음은 성능 테스트 코드입니다. 수량급 크기가 다른 Array List와 Linked List의 다양한 반복 방식을 출력하는 데 걸리는 시간입니다.
package cn.trinea.java.test;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* JavaLoopTest
*
* @author www.trinea.cn 2013-10-28
*/
public class JavaLoopTest {
public static void main(String[] args) {
System.out.print("compare loop performance of ArrayList");
loopListCompare(getArrayLists(10000, 100000, 1000000, 9000000));
System.out.print("\r
\r
compare loop performance of LinkedList");
loopListCompare(getLinkedLists(100, 1000, 10000, 100000));
}
public static List<Integer>[] getArrayLists(int... sizeArray) {
List<Integer>[] listArray = new ArrayList[sizeArray.length];
for (int i = 0; i < listArray.length; i++) {
int size = sizeArray[i];
List<Integer> list = new ArrayList<Integer>();
for (int j = 0; j < size; j++) {
list.add(j);
}
listArray[i] = list;
}
return listArray;
}
public static List<Integer>[] getLinkedLists(int... sizeArray) {
List<Integer>[] listArray = new LinkedList[sizeArray.length];
for (int i = 0; i < listArray.length; i++) {
int size = sizeArray[i];
List<Integer> list = new LinkedList<Integer>();
for (int j = 0; j < size; j++) {
list.add(j);
}
listArray[i] = list;
}
return listArray;
}
public static void loopListCompare(List<Integer>... listArray) {
printHeader(listArray);
long startTime, endTime;
// Type 1
for (int i = 0; i < listArray.length; i++) {
List<Integer> list = listArray[i];
startTime = Calendar.getInstance().getTimeInMillis();
for (Integer j : list) {
// use j
}
endTime = Calendar.getInstance().getTimeInMillis();
printCostTime(i, listArray.length, "for each", endTime - startTime);
}
// Type 2
for (int i = 0; i < listArray.length; i++) {
List<Integer> list = listArray[i];
startTime = Calendar.getInstance().getTimeInMillis();
// Iterator<Integer> iterator = list.iterator();
// while(iterator.hasNext()) {
// iterator.next();
// }
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();) {
iterator.next();
}
endTime = Calendar.getInstance().getTimeInMillis();
printCostTime(i, listArray.length, "for iterator", endTime - startTime);
}
// Type 3
for (int i = 0; i < listArray.length; i++) {
List<Integer> list = listArray[i];
startTime = Calendar.getInstance().getTimeInMillis();
for (int j = 0; j < list.size(); j++) {
list.get(j);
}
endTime = Calendar.getInstance().getTimeInMillis();
printCostTime(i, listArray.length, "for list.size()", endTime - startTime);
}
// Type 4
for (int i = 0; i < listArray.length; i++) {
List<Integer> list = listArray[i];
startTime = Calendar.getInstance().getTimeInMillis();
int size = list.size();
for (int j = 0; j < size; j++) {
list.get(j);
}
endTime = Calendar.getInstance().getTimeInMillis();
printCostTime(i, listArray.length, "for size = list.size()", endTime - startTime);
}
// Type 5
for (int i = 0; i < listArray.length; i++) {
List<Integer> list = listArray[i];
startTime = Calendar.getInstance().getTimeInMillis();
for (int j = list.size() - 1; j >= 0; j--) {
list.get(j);
}
endTime = Calendar.getInstance().getTimeInMillis();
printCostTime(i, listArray.length, "for j--", endTime - startTime);
}
}
static int FIRST_COLUMN_LENGTH = 23, OTHER_COLUMN_LENGTH = 12, TOTAL_COLUMN_LENGTH = 71;
static final DecimalFormat COMMA_FORMAT = new DecimalFormat("#,###");
public static void printHeader(List<Integer>... listArray) {
printRowDivider();
for (int i = 0; i < listArray.length; i++) {
if (i == 0) {
StringBuilder sb = new StringBuilder().append("list size");
while (sb.length() < FIRST_COLUMN_LENGTH) {
sb.append(" ");
}
System.out.print(sb);
}
StringBuilder sb = new StringBuilder().append("| ").append(COMMA_FORMAT.format(listArray[i].size()));
while (sb.length() < OTHER_COLUMN_LENGTH) {
sb.append(" ");
}
System.out.print(sb);
}
TOTAL_COLUMN_LENGTH = FIRST_COLUMN_LENGTH + OTHER_COLUMN_LENGTH * listArray.length;
printRowDivider();
}
public static void printRowDivider() {
System.out.println();
StringBuilder sb = new StringBuilder();
while (sb.length() < TOTAL_COLUMN_LENGTH) {
sb.append("-");
}
System.out.println(sb);
}
public static void printCostTime(int i, int size, String caseName, long costTime) {
if (i == 0) {
StringBuilder sb = new StringBuilder().append(caseName);
while (sb.length() < FIRST_COLUMN_LENGTH) {
sb.append(" ");
}
System.out.print(sb);
}
StringBuilder sb = new StringBuilder().append("| ").append(costTime).append(" ms");
while (sb.length() < OTHER_COLUMN_LENGTH) {
sb.append(" ");
}
System.out.print(sb);
if (i == size - 1) {
printRowDivider();
}
}
}
PS: in thread “main” java.lang.OutOfMemoryError: Java heap space
을 실행하면 main
함수에서 list size
의 크기를 줄입니다.이 중
getArrayLists
함수는 size
의 ArrayList를, getLinkedLists
함수는 size
의 LinkedList를 각각 되돌려줍니다.loopListCompare
함수는 위의 반복 방식 1-5로 각각list 수조 (크기가 다른list 포함) 의list를 반복합니다.print
시작 함수는 출력 보조 함수입니다.테스트 환경은 Windows 7 32비트 시스템 3.2G 듀얼 코어 CPU 4G 메모리, Java 7, Eclipse-Xms512m-Xmx512m
최종 테스트 결과는 다음과 같습니다.
compare loop performance of ArrayList
-----------------------------------------------------------------------
list size | 10,000 | 100,000 | 1,000,000 | 10,000,000
-----------------------------------------------------------------------
for each | 1 ms | 3 ms | 14 ms | 152 ms
-----------------------------------------------------------------------
for iterator | 0 ms | 1 ms | 12 ms | 114 ms
-----------------------------------------------------------------------
for list.size() | 1 ms | 1 ms | 13 ms | 128 ms
-----------------------------------------------------------------------
for size = list.size() | 0 ms | 0 ms | 6 ms | 62 ms
-----------------------------------------------------------------------
for j-- | 0 ms | 1 ms | 6 ms | 63 ms
-----------------------------------------------------------------------
compare loop performance of LinkedList
-----------------------------------------------------------------------
list size | 100 | 1,000 | 10,000 | 100,000
-----------------------------------------------------------------------
for each | 0 ms | 1 ms | 1 ms | 2 ms
-----------------------------------------------------------------------
for iterator | 0 ms | 0 ms | 0 ms | 2 ms
-----------------------------------------------------------------------
for list.size() | 0 ms | 1 ms | 73 ms | 7972 ms
-----------------------------------------------------------------------
for size = list.size() | 0 ms | 0 ms | 67 ms | 8216 ms
-----------------------------------------------------------------------
for j-- | 0 ms | 1 ms | 67 ms | 8277 ms
-----------------------------------------------------------------------
첫 번째 표는 ArrayList 비교 결과이고, 두 번째 표는 LinkedList 비교 결과입니다.시계 가로는 같은 경로 방식, 크기가 다른list 경로의 시간 소모, 세로는list 경로의 시간 소모입니다.
PS: List를 처음 훑어보는 데 약간의 시간이 걸리기 때문에
for each
의 결과는 약간의 편차가 있습니다. 테스트 코드의 몇 가지 Type 순서를 바꾸면 for each
의 시간과 for iterator
이 가깝다는 것을 알 수 있습니다.반복 방식 성능 테스트 결과 분석
1. foreach 소개
foreach는 자바 SE5.0이 도입한 기능이 강한 순환 구조로
for (Integer j : list)
은 for each int in list
으로 읽어야 한다.for (Integer j : list)
실현 거의 등가
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()) {
Integer j = iterator.next();
}
foreach 코드 작성이 간단하고 아래 표시된 초기 값과 종료 값, 경계 넘침 등에 신경 쓸 필요가 없기 때문에 오류가 발생하기 쉽습니다2. ArrayList 스트리밍 방식 결과 분석
a. ArrayList 크기가 10만 개가 되기 전에 다섯 가지 반복 방식의 시간 소모는 거의 같다
b. 10만 이후 네 번째, 다섯 번째 반복 방식은 세 가지보다 빠르고 get 방식은 Iterator 방식보다 우수하며
int size = list.size();
for (int j = 0; j < size; j++) {
list.get(j);
}
list.size()
을 임시 변수 크기로 대체하면 성능이 더욱 우수합니다.Array List의 교체기 Iterator
과 get
방법의 실현을 봅시다.
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
……
}
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
이를 통해 알 수 있듯이 get
과 Iterator
의 next
함수 역시 직접 포지셔닝 데이터를 통해 원소를 얻는 것은 단지 몇 가지 판단이 많았을 뿐이다.c. 이를 통해 알 수 있듯이 천만 크기의 Array List에서도 몇 가지 반복 방식의 차이는 50ms 정도에 불과하고 자주 사용하는 10만 정도의 시간은 거의 같다.foreach의 장점을 고려하여foreach라는 간편한 방식으로 반복할 수 있다.
3. LinkedList 스트리밍 방식 결과 분석
A. LinkedList 크기가 만 개에 가까울 때
get
방식과 Iterator
방식은 이미 두 개의 수량급이 차이가 났고 10만 시 Iterator
방식의 성능은 get 방식보다 훨씬 낫다.LinkedList의 교체기와
get
방법의 실현을 봅시다.
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned = null;
private Node<E> next;
private int nextIndex;
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
public boolean hasNext() {
return nextIndex < size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
……
}
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
위 코드에서 알 수 있듯이 LinkedList 교체기의 next
함수는next 바늘을 통해 다음 요소를 신속하게 얻고 되돌려줍니다.get 방법은 처음부터 index 아래에 표시될 때까지 반복합니다. 원소를 찾는 시간의 복잡도는 오 (n) 이고, 반복된 시간의 복잡도는 O (n2) 에 도달합니다.따라서 LinkedList의 반복에 대해foreach를 추천하고
get
방식을 사용하지 마십시오.4. ArrayList 및 LinkedList 스트리밍 방식 결과 비교 분석
위의 수량 등급을 보면 마찬가지로 foreach 순환이 반복되고 Array List와 Linked List의 시간 차이가 많지 않기 때문에 이 예시를 조금만 수정하면
list size
을 확대하면 양자가 기본적으로 하나의 수량 등급에 있다는 것을 발견할 수 있다.그러나
ArrayList get
함수가 직접 위치를 정하여 가져오는 방식의 시간 복잡도는 O(1)이고 LinkedList의 get 함수 시간 복잡도는 O(n)이다.공간 소비를 고려하여 Array List를 선택하는 것이 좋습니다.개별 삽입 삭제에는 LinkedList를 사용할 수 있습니다.
결론 총결
위의 분석을 통해 우리는 기본적으로 다음과 같이 요약할 수 있다.
이상은 이 글의 전체 내용입니다. 본고의 내용이 여러분이 자바를 공부하거나 사용할 때 도움이 되었으면 좋겠습니다. 의문이 있으면 댓글을 남겨 주십시오.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
38. Java의 Leetcode 솔루션텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.