Java에서 ArrayList보다 LinkedList를 사용하는 경우는 언제입니까?


3122

나는 항상 간단하게 사용하는 사람이었습니다.

List<String> names = new ArrayList<>();

인터페이스를 이식성 의 유형 이름으로 사용 하므로 이와 같은 질문을 할 때 코드를 다시 작성할 수 있습니다.

때해야 LinkedList이상 사용 ArrayList반대의 경우도 마찬가지?



1
LinkedList stackoverflow.com/a/42529652/2032701 의 작성자의 인용문 을 보면 문제에 대한 실질적인 이해를 얻을 수 있습니다.
Ruslan

차이점을 설명하고 성능 테스트가 포함 된 Java LinkedList vs ArrayList 게시물을 참조하십시오 .
alonana

답변:


3371

요약 ArrayList 와 함께 ArrayDeque에서 바람직하다 많은 보다 더 많은 사용 사례 LinkedList. 확실하지 않은 경우로 시작하십시오 ArrayList.


LinkedListArrayListList 인터페이스의 두 가지 구현입니다. LinkedList이중 연결 목록으로 구현합니다. ArrayList동적 크기 조정 배열로 구현합니다.

표준 링크리스트 및 배열 연산과 마찬가지로 다양한 메소드는 서로 다른 알고리즘 런타임을 갖습니다.

에 대한 LinkedList<E>

  • get(int index)O (N) (와 N / 4 평균과 같이), 그러나 O (1)index = 0또는 index = list.size() - 1(이 경우, 또한 사용 getFirst()하고 getLast()). 의 주요 장점 중 하나 LinkedList<E>
  • add(int index, E element)되고 O (N) (와 N / 4 평균과 같이), 그러나 O (1)이 때, index = 0또는 index = list.size() - 1(이 경우, 또한 사용 addFirst()하고 addLast()/ add()). 의 주요 장점 중 하나 LinkedList<E>
  • remove(int index)O (N) (와 N / 4 평균과 같이), 그러나 O (1)index = 0또는 index = list.size() - 1(이 경우, 또한 사용 removeFirst()하고 removeLast()). 의 주요 장점 중 하나 LinkedList<E>
  • Iterator.remove()O (1) . 의 주요 장점 중 하나 LinkedList<E>
  • ListIterator.add(E element)O (1) . 의 주요 장점 중 하나 LinkedList<E>

참고 : 많은 작업이 필요합니다 에는 평균 n / 4 단계 , 최상의 경우 일정한 수의 단계 (예 : 인덱스 = 0), 최악의 경우 n / 2 단계 (중간 목록)가 필요합니다.

에 대한 ArrayList<E>

  • get(int index) 이다 O (1) . 주요 장점 ArrayList<E>
  • add(E element)O (1) 상각 있지만, O (n)이 최악의 배열을 조정하여 복사되어야하므로
  • add(int index, E element) 이다 O (N) (와 N / 2 평균 단계)
  • remove(int index) 이다 O (N) (와 N / 2 평균 단계)
  • Iterator.remove() 이다 O (N) (와 N / 2 평균 단계)
  • ListIterator.add(E element) 이다 O (N) (와 N / 2 평균 단계)

참고 : 많은 작업 에는 평균 n / 2 단계 가 필요 합니다. 하며 최상의 경우에는 일정한 수의 단계 (목록 끝), 최악의 경우에는 n 단계 (목록의 시작)가 필요합니다.

LinkedList<E>반복자를 사용하여 일정한 시간에 삽입하거나 제거 할 수 있지만 요소의 순차적 액세스 만 가능합니다. 즉, 목록을 앞뒤로 걸을 수 있지만 목록에서 위치를 찾는 것은 목록의 크기에 비례하여 시간이 걸립니다. Javadoc에 따르면 "목록에 색인을 생성하는 작업은 목록의 시작 또는 끝에서 더 가까운 쪽을 통과 할 것" 이므로 이러한 방법은 O (n) ( N / 4 , 비록 평균 단계) O (1) 에 대해 index = 0.

ArrayList<E>반면에 빠른 임의 읽기 액세스를 허용하므로 일정한 시간에 모든 요소를 ​​가져올 수 있습니다. 그러나 끝을 제외하고 어디에서나 추가하거나 제거하려면 개구부를 만들거나 간격을 채우기 위해 후자의 모든 요소를 ​​이동해야합니다. 또한 기본 배열의 용량보다 많은 요소를 추가하면 새 배열 (1.5 배 크기)이 할당되고 이전 배열이 새 배열에 복사되므로ArrayList 최악의 에는 O (n) 을 더합니다 평균이지만 일정합니다.

따라서 수행하려는 작업에 따라 구현을 선택해야합니다. 두 종류의 List를 반복하는 것은 실질적으로 저렴합니다. ( ArrayList기술적으로 반복하는 것이 더 빠르지 만 실제로 성능에 민감한 작업을 수행하지 않는 한 걱정하지 않아도됩니다. 두 가지 모두 상수입니다.)

LinkedList기존 반복기를 재사용 하여 요소를 삽입하고 제거 할 때 발생 하는 주요 이점이 있습니다. 그런 다음 목록을 로컬로만 변경하여 O (1) 에서 이러한 작업을 수행 할 수 있습니다 . 배열 목록에서 나머지 배열을 이동 (즉, 복사)해야합니다. 다른 한편으로, 최악의 경우 O (n) ( n / 2 단계) LinkedList의 링크를 따르는 수단을 찾는 반면 원하는 위치에서 수학적으로 계산되고 O (1) 에서 액세스 할 수 있습니다 .ArrayList

사용 a의 또 다른 장점 LinkedList은리스트의 머리에서 추가하거나 제거 할 때 그 작업이기 때문에, 발생하는 O (1) 가있는 동안, O (n)에 대한 ArrayList. 그 ArrayDeque대신에 좋은 대안 이 될 수 있습니다LinkedList헤드에서 추가 및 제거 위한 있지만 이는 아닙니다 List.

또한 큰 목록이있는 경우 메모리 사용량도 다릅니다. LinkedList다음 및 이전 요소에 대한 포인터도 저장되므로 a의 각 요소 에는 더 많은 오버 헤드가 있습니다. ArrayLists이 오버 헤드가 없습니다. 그러나 ArrayLists요소가 실제로 추가되었는지 여부에 관계없이 용량에 할당 된만큼의 메모리를 차지하십시오.

의 기본 초기 용량 ArrayList은 매우 작습니다 (Java 1.4-1.8에서 10). 그러나 기본 구현은 배열이므로 많은 요소를 추가하면 배열의 크기를 조정해야합니다. 많은 요소를 추가 할 것임을 알 때 높은 크기 조정 비용을 피하려면 ArrayList초기 용량이 더 높은 구성 요소를 구성하십시오 .


182
삽입 비용을 언급하지 않았습니다. LinkedList에서 올바른 위치를 차지하면 삽입 비용은 O (1)이며 ArrayList에서는 O (n)까지 올라갑니다. 삽입 점을 지나는 모든 요소를 ​​이동해야합니다.
David Rodríguez-dribeas

26
Vector 사용과 관련하여 : 실제로 Vector로 넘어갈 필요는 없습니다. 이를 수행하는 방법은 선호하는 List 구현과 동기화 된 랩퍼를 제공하기 위해 synchronizedList를 호출하는 것입니다. 참조 java.sun.com/docs/books/tutorial/collections/implementations/…
Ryan Cox

69
아니요, LinkedList의 경우 위치를 알고 있더라도 get은 여전히 ​​O (n)입니다. 해당 위치에 도달하려면 기본 구현에서 해당 위치의 값에 도달하기 위해 연결된 목록의 "다음"포인터를 이동해야하기 때문입니다. 랜덤 액세스와 같은 것은 없습니다. 위치 2의 경우 포인터를 걷는 것이 저렴할 수 있지만 위치 1 백만의 경우 저렴하지 않습니다. 요점은 위치에 비례하므로 O (n)입니다.
Jonathan Tran

53
@Kevin 메모리가 "가까이"있다는 것이 중요 할 수 있습니다. 하드웨어는 인접한 메모리 블록 (동적 RAM)을 L1 또는 L2 캐시에서 더 빠른 정적 RAM으로 캐시합니다. 이론적으로 대부분의 경우, 메모리는 랜덤 액세스로 취급 될 수 있습니다. 그러나 실제로 메모리를 순차적으로 읽는 것이 임의 순서보다 약간 빠릅니다. 성능이 중요한 루프의 경우 이는 중요 할 수 있습니다. 이를 "공간 공간"또는 참조 지역이라고합니다 .
Jonathan Tran

92
O(n/2)또는 같은 것은 없습니다 O(n/4). 큰 O 표기법은 연산 이 더 큰 n으로 확장 되는 방법을 알려줍니다 . 그리고 단계를 필요로하는 연산은 단계를 필요로하는 연산과 정확히 동일하게 스케일링 되며, 이는 일정한 요약 또는 요인이 제거되는 이유이다. 그리고 둘 다 입니다. 그리고 그것은 비교 sence되지 아니하므로, 어쨌든 다른 일정 요인이 A를 하나의 다른의를 두 단지 나타낸다 선형 확장 작업을. n/2nO(n/2)O(n/4)O(n)LinkedListArrayListO(n/2)O(n/4)
Holger

630

지금까지 아무도이 목록 LinkedList보다 더 많은 "많은 것" 이라는 일반적인 합의 외에 이러한 각 목록의 메모리 풋 프린트를 다루지 않은 것으로 보입니다 .ArrayList 보이므로 N null 참조에 대해 두 목록이 얼마나 많이 차지하는지 정확하게 보여주기 위해 숫자 크 런칭을 수행했습니다.

참조는 상대 시스템에서 32 또는 64 비트 (널 (null) 인 경우에도)이므로 32 및 64 비트에 대한 4 개의 데이터 세트를 포함 LinkedLists했으며ArrayLists .

참고 :ArrayList 라인에 표시된 크기 는 트리밍 된 목록에 대한 것입니다 . 실제로 백업 어레이의 용량은ArrayList 은 일반적으로 현재 요소 수보다 큽니다.

참고 2 : (BeOnRope 덕분에) JDK6 중반부터 CompressedOops가 기본값으로 기본 설정되었으므로 64 비트 시스템의 경우 아래 값은 기본적으로 32 비트에 해당합니다 (물론 특별히 끄지 않는 한).


LinkedList 및 ArrayList 요소 수 x 바이트 그래프


결과는 특히 요소 수가 LinkedList많을 때보 다 훨씬 더 많은 것을 분명히 보여줍니다 ArrayList. 메모리가 중요한 요소 인 경우을 피하십시오 LinkedLists.

내가 사용한 공식은 내가 잘못한 것을 알려 주면 고칠 것입니다. 'b'는 32 또는 64 비트 시스템에서 4 또는 8이며 'n'은 요소 수입니다. mod의 이유는 java의 모든 객체가 모두 사용되는지 여부에 관계없이 8 바이트의 배수를 차지하기 때문입니다.

배열 목록 :

ArrayList object header + size integer + modCount integer + array reference + (array oject header + b * n) + MOD(array oject, 8) + MOD(ArrayList object, 8) == 8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8) + MOD(8 + 4 + 4 + b + (12 + b * n) + MOD(12 + b * n, 8), 8)

연결 목록 :

LinkedList object header + size integer + modCount integer + reference to header + reference to footer + (node object overhead + reference to previous element + reference to next element + reference to element) * n) + MOD(node object, 8) * n + MOD(LinkedList object, 8) == 8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n + MOD(8 + 4 + 4 + 2 * b + (8 + 3 * b) * n + MOD(8 + 3 * b, 8) * n, 8)


2
LinkedList는 단일 요소를 저장하기 위해 ArrayList만큼 많은 메모리가 필요하다는 것을 알면 매우 흥미 롭습니다. 직관적이지 않습니다! -XX : + UseCompressedOops로 예제를 실행하면 어떻게됩니까?
jontejj

215
수학 문제는 그래프가 그 영향을 크게 과장한다는 것입니다. 각각 int4 바이트 또는 8 바이트의 데이터 만 포함하는 객체를 모델링하고 있습니다 . 연결된 목록에는 기본적으로 4 개의 "단어"가 있습니다. 따라서 그래프는 연결된 목록이 배열 목록의 저장을 "5 배"사용한다는 인상을줍니다. 이것은 잘못이다. 오버 헤드는 스케일링 팩터가 아닌 추가 조정으로서 오브젝트 당 16 또는 32 바이트입니다.
Heath Hunnicutt

6
ArrayList / LinkedList / Node 객체 중 어느 것도 int 만 포함하지 않으므로 거기에서 말하는 것을 얻지 못합니다. '개체 오버 헤드'를 '개체 헤더'로 바꿔서 설명했습니다. 시스템에 관계없이 모든 개체에 8 바이트 헤더가 있으며, 그래야 LinkedList의 모든 노드 개체를 포함합니다. 가능한 한 정확하게 계산됩니다. 텔. 덧붙여, 다시보고, 나는 실제로와 ArrayList를 분할하게 LinkedList의 내 수학으로 다른 문제의 몇 가지를 찾았어요 악화 . 나는 그것을 계속 업데이트하고 있기 때문에 주저하지 말고 진실을 밝히고 정교하게하십시오.
Numeron

6
주목해야한다 CompressedOops기본값은 모든 최근의 JDK (7, 8, 몇 년 동안 6의 업데이트), 그래서 64 비트의 차이를하지 않습니다에 지금 ArrayList또는 LinkedList명시 적으로 위해 압축 죄송를 해제하지 않는 한, 크기를 몇몇 이유.
BeeOnRope 5

1
@jontejj 기본 용량 증가는 50 %이므로 ArrayList초기 용량을 지정하지 않고 채울 때 여전히 메모리보다 훨씬 적은 메모리를 사용합니다 LinkedList.
Holger

243

ArrayList당신이 원하는 것입니다. LinkedList거의 항상 (성능) 버그입니다.

LinkedList짜증나 :

  • 많은 작은 메모리 개체를 사용하므로 프로세스 전체의 성능에 영향을줍니다.
  • 많은 작은 개체는 캐시 로컬성에 좋지 않습니다.
  • 인덱스 작업에는 순회가 필요합니다. 즉, O (n) 성능을 갖습니다. 이것은 소스 코드에서 분명하지 않으므로 알고리즘 O (n) ArrayList가 사용 된 것보다 느립니다 .
  • 좋은 성능을 얻는 것은 까다 롭습니다.
  • big-O 성능이와 같더라도 ArrayList어쨌든 상당히 느려질 것입니다.
  • LinkedList아마도 잘못된 선택 일 수 있기 때문에 소스 에서 보는 것은 좋지 않습니다 .

236
죄송합니다. 당신을 표시했습니다. LinkedList는 빨리 지 않습니다. LinkedList가 사용하기에 적합한 클래스 인 상황이 있습니다. 배열 목록보다 나은 상황은 많지 않지만 존재한다고 동의합니다. 바보 같은 일을하는 사람들을 교육하십시오!
David Turner

40
이로 인해 많은 투표가 이루어졌습니다. 실제로 Java의 LinkedList를 사용해야 할 이유는 거의 없습니다. 나쁜 성능 외에도 다른 구체적인 List 클래스보다 많은 메모리를 사용합니다 (모든 노드에는 두 개의 추가 포인터가 있으며 각 노드는 추가 오버 헤드 바이트가있는 별도의 래퍼 객체입니다).
Kevin Brock

42
이것은 가장 유용한 답변 중 하나입니다. 많은 프로그래머들이 (a) 추상 데이터 유형과 구체적인 구현의 차이점, (b) 성능을 결정할 때 상수 요소와 메모리 오버 헤드의 실제 중요성을 이해하지 못하는 것은 부끄러운 일입니다.
Porculus

50
-1 : 다소 깜박 거리는보기입니다. 예, ArrayList는 매우 다양한 도구입니다. 그러나 한계가 있습니다. 이로 인해 문제가 발생할 수 있으며 LinkedList를 사용해야합니다. 물론이 솔루션은 매우 전문화 된 솔루션이며, 대부분의 경우 특수 도구로서 다용도 도구보다 성능이 뛰어납니다. 그러나 이것이 "빨리"또는 이와 비슷한 것을 의미하는 것은 아니며, 언제 사용 해야하는지 알아야합니다.
Malcolm

27
@DavidTurner : 존재하지만 Tom의 요점은 물어봐야 할 경우 ArrayList를 원한다는 것입니다.
user541686

139

약 10 년 동안 대규모 SOA 웹 서비스에서 운영 성능 엔지니어링을 수행 한 사람으로서 ArrayList보다 LinkedList의 동작을 선호합니다. LinkedList의 정상 상태 처리량은 더 나빠서 더 많은 하드웨어를 구매할 수 있지만 압력이 가해지면 ArrayList의 동작으로 인해 클러스터의 앱이 거의 동 기적으로 배열을 확장하고 큰 배열 크기의 경우 응답 성이 떨어질 수 있습니다. 압력이 가해지는 동안 앱에서 중단이 발생했습니다. 이는 치명적인 동작입니다.

마찬가지로 기본 처리량 tenured 가비지 수집기에서 응용 프로그램의 처리량을 향상시킬 수 있지만 10GB 힙이있는 Java 응용 프로그램을 가져 오면 전체 GC 중에 25 초 동안 응용 프로그램을 잠그면 SOA 응용 프로그램에서 시간 초과 및 실패가 발생할 수 있습니다 너무 자주 발생하면 SLA를 날려 버립니다. CMS 수집기는 더 많은 리소스를 사용하고 동일한 원시 처리량을 달성하지 않더라도 예측 가능하고 대기 시간이 짧기 때문에 훨씬 더 나은 선택입니다.

성능이 의미하는 전부가 처리량이고 대기 시간을 무시할 수있는 경우 ArrayList는 성능을위한 더 나은 선택 일뿐입니다. 직장에서의 경험에서 최악의 대기 시간을 무시할 수 없습니다.


8
다른 솔루션이 ArrayList의 ensureCapacity () 메소드를 사용하여 프로그래밍 방식으로 목록의 크기를 관리하지 않습니까? 내 질문은 왜 캐싱 또는 db 메커니즘에 더 잘 저장 될 수있을 때 많은 것들이 취성 데이터 구조에 저장됩니까? 나는 얼마 전 ArrayList의 악에 대해 맹세 한 인터뷰를 가졌지 만 여기에 와서 복잡도 분석이 더 나은 것으로 나타났습니다! 토론을위한 훌륭한 포인트. 감사!
ingyhere

22
10GB 힙이있는 Java 앱을 얻으면 전체 GC 중에 25 초 동안 앱을 잠글 수 있습니다 . 각 노드.
bestsss

5
그건 ... 끔찍한 해결책입니다. 대신 기본적으로 GC 정리에 의존하고 있습니다. 대신 배열 목록에서 ensureCapacity ()를 호출 할 수있을 때 엄청나게 비쌉니다.
Philip Devine

5
@Andreas : A 는 일반 참조 배열보다 LinkedList 항상 5 배의 메모리를 할당하므로 ArrayList메모리를 회수하지 않아도 2.5 배의 시간이 필요한 메모리는 여전히 훨씬 적은 메모리를 사용합니다. 큰 배열 할당은 Eden 공간을 우회하기 때문에 실제로 메모리가 충분하지 않으면 GC 동작에 영향을 미치지 않습니다.이 경우 LinkedList훨씬 더 일찍 터졌습니다.
Holger

5
@Andreas 다른 문제는 메모리 할당 방법입니다. LinkedList다음 요소에 할당하려면 사용 가능한 작은 메모리 조각 만 필요합니다. 크기 조정 된 배열을 할당하려면 ArrayList크고 연속적인 여유 공간 블록 이 필요합니다 . 힙이 조각화되면 GC는 적절한 단일 메모리 블록을 확보하기 위해 전체 힙을 다시 정렬 할 수 있습니다.
Piotr Kusmierczyk

128
Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

알고리즘 : 빅오 표기법

ArrayLists는 한 번만 읽을 수있는 많은 또는 어 펜더에는 좋지만 추가 또는 제거는 앞면 또는 가운데에는 좋지 않습니다.


42
일정한 요인에 대해 생각하지 않고 big-O 값을 직접 비교할 수 없습니다. 작은 목록 (및 대부분의 목록은 작음)의 경우 ArrayList의 O (N)이 LinkedList의 O (1)보다 빠릅니다.
Porculus

4
나는 작은 목록 성능에 신경 쓰지 않으며 어떻게 든 루프에서 사용 되지 않는 한 내 컴퓨터도 마찬가지 입니다.
Maarten Bodewes

45
LinkedList는의 중간에 실제로 삽입 할 수 없습니다 O(1). 삽입 점을 찾으려면 목록의 절반을 실행해야합니다.
Thomas Ahle

8
LinkedList : 중간 O (1)에 삽입-잘못되었습니다! LinkedList 크기의 1/10 위치에 삽입하는 것조차 ArrayList의 1/10 위치에 요소를 삽입하는 것보다 느립니다. 그리고 더 나쁜 : 수집의 끝. ArrayList의 마지막 위치 (마지막이 아닌)에 삽입하는 것이 LinkedList의 마지막 위치 (마지막이 아닌)보다 빠르다
kachanov

14
A의 @kachanov 삽입은 LinkedList 이다 O(1) 당신이 삽입 위치에 반복자가있는 경우 , 즉 ListIterator.add가정입니다 O(1)A에 대한 LinkedList.
종료-익명-무스

107

그래, 나는 이것이 고대의 질문이라는 것을 알고 있지만 내 두 센트를 던져 넣을 것이다.

LinkedList는 성능 측면에서 거의 항상 잘못된 선택입니다. LinkedList가 필요한 매우 구체적인 알고리즘이 있지만 매우 드물며, 일반적으로이 알고리즘은 일반적으로 LinkedList의 목록 중간에 요소를 빠르게 삽입하고 삭제하는 기능에 의존합니다. ListIterator와 함께.

LinkedList가 ArrayList를 능가하는 일반적인 사용 사례가 하나 있습니다. 그러나 목표가 성능 인 경우 LinkedList 대신 ArrayBlockingQueue (대기열 큐 크기의 상한을 미리 결정하고 모든 메모리를 미리 할당 할 수있는 경우) 또는이 CircularArrayList 구현을 사용하는 것도 고려해야 합니다. . (예, 2001 년부터 생성되었으므로 생성해야하지만 최근 JVM의 기사에서 인용 한 것과 비슷한 성능 비율을 얻었습니다)


39
Java 6부터 사용할 수 있습니다 ArrayDeque. docs.oracle.com/javase/6/docs/api/java/util/ArrayDeque.html
Thomas Ahle

1
ArrayDequeLinkedList모든 작업이 같은 끝에 있지 않으면 보다 느립니다 . 스택으로 사용하면 괜찮지 만 대기열을 잘 만들지는 않습니다.
제레미 목록

2
사실은 아닙니다-적어도 jdk1.7.0_60 및 다음 테스트에서 Oracle을 구현 한 경우. 나는 천만 번 반복하는 테스트를 만들었고 천만 개의 임의 정수의 Deque가 있습니다. 루프 내에서 하나의 요소를 폴링하고 상수 요소를 제공합니다. 내 컴퓨터에서 LinkedList는 ArrayDeque보다 10 배 이상 느리고 메모리를 덜 사용합니다). 그 이유는 ArrayList와 달리 ArrayDeque는 배열의 헤드에 대한 포인터를 유지하므로 헤드를 제거 할 때 모든 요소를 ​​이동할 필요가 없기 때문입니다.
Henno Vermeulen

6
ArrayDequeStack스택으로 LinkedList사용될 때보 다 더 빠르며 대기열로 사용될 때보 다 빠를 가능성이 높습니다 .
akhil_mittal 2016 년

3
akhil_mittal의 의견은 ArrayDeque문서 에서 인용 한 것 입니다.
스튜어트 마크

65

효율성 문제입니다. LinkedList요소를 추가하고 삭제하는 데 빠르지 만 특정 요소에 액세스하는 데 느립니다. ArrayList특정 요소에 액세스하는 데 빠르지 만 양쪽 끝에 추가하는 것이 느릴 수 있으며 특히 중간에 삭제하는 것이 느릴 수 있습니다.

Linked List 와 마찬가지로 Array vs ArrayList vs LinkedList vs Vector 는 더 깊이 있습니다.


54

정확하거나 잘못됨 : 테스트를 로컬에서 실행하고 스스로 결정하십시오!

편집 / 제거 빠른에 LinkedList비해 ArrayList.

ArrayListArray크기가 두 배가되어야하는에 의해 지원되는 대량 응용 프로그램에서는 더 나쁩니다.

아래는 각 작업에 대한 단위 테스트 결과입니다. 타이밍은 나노초 단위로 제공됩니다.


Operation                       ArrayList                      LinkedList  

AddAll   (Insert)               101,16719                      2623,29291 

Add      (Insert-Sequentially)  152,46840                      966,62216

Add      (insert-randomly)      36527                          29193

remove   (Delete)               20,56,9095                     20,45,4904

contains (Search)               186,15,704                     189,64,981

코드는 다음과 같습니다.

import org.junit.Assert;
import org.junit.Test;

import java.util.*;

public class ArrayListVsLinkedList {
    private static final int MAX = 500000;
    String[] strings = maxArray();

    ////////////// ADD ALL ////////////////////////////////////////
    @Test
    public void arrayListAddAll() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        arrayList.addAll(stringList);
        watch.totalTime("Array List addAll() = ");//101,16719 Nanoseconds
    }

    @Test
    public void linkedListAddAll() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);

        watch.start();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);
        watch.totalTime("Linked List addAll() = ");  //2623,29291 Nanoseconds
    }

    //Note: ArrayList is 26 time faster here than LinkedList for addAll()

    ///////////////// INSERT /////////////////////////////////////////////
    @Test
    public void arrayListAdd() {
        Watch watch = new Watch();
        List<String> arrayList = new ArrayList<String>(MAX);

        watch.start();
        for (String string : strings)
            arrayList.add(string);
        watch.totalTime("Array List add() = ");//152,46840 Nanoseconds
    }

    @Test
    public void linkedListAdd() {
        Watch watch = new Watch();

        List<String> linkedList = new LinkedList<String>();
        watch.start();
        for (String string : strings)
            linkedList.add(string);
        watch.totalTime("Linked List add() = ");  //966,62216 Nanoseconds
    }

    //Note: ArrayList is 9 times faster than LinkedList for add sequentially

    /////////////////// INSERT IN BETWEEN ///////////////////////////////////////

    @Test
    public void arrayListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX + MAX / 10);
        arrayList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        arrayList.add(insertString0);
        arrayList.add(insertString1);
        arrayList.add(insertString2);
        arrayList.add(insertString3);

        watch.totalTime("Array List add() = ");//36527
    }

    @Test
    public void linkedListInsertOne() {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(stringList);

        String insertString0 = getString(true, MAX / 2 + 10);
        String insertString1 = getString(true, MAX / 2 + 20);
        String insertString2 = getString(true, MAX / 2 + 30);
        String insertString3 = getString(true, MAX / 2 + 40);

        watch.start();

        linkedList.add(insertString0);
        linkedList.add(insertString1);
        linkedList.add(insertString2);
        linkedList.add(insertString3);

        watch.totalTime("Linked List add = ");//29193
    }


    //Note: LinkedList is 3000 nanosecond faster than ArrayList for insert randomly.

    ////////////////// DELETE //////////////////////////////////////////////////////
    @Test
    public void arrayListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.remove(searchString0);
        arrayList.remove(searchString1);
        watch.totalTime("Array List remove() = ");//20,56,9095 Nanoseconds
    }

    @Test
    public void linkedListRemove() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.remove(searchString0);
        linkedList.remove(searchString1);
        watch.totalTime("Linked List remove = ");//20,45,4904 Nanoseconds
    }

    //Note: LinkedList is 10 millisecond faster than ArrayList while removing item.

    ///////////////////// SEARCH ///////////////////////////////////////////
    @Test
    public void arrayListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> stringList = Arrays.asList(strings);
        List<String> arrayList = new ArrayList<String>(MAX);

        arrayList.addAll(stringList);
        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        arrayList.contains(searchString0);
        arrayList.contains(searchString1);
        watch.totalTime("Array List addAll() time = ");//186,15,704
    }

    @Test
    public void linkedListSearch() throws Exception {
        Watch watch = new Watch();
        List<String> linkedList = new LinkedList<String>();
        linkedList.addAll(Arrays.asList(strings));

        String searchString0 = getString(true, MAX / 2 + 10);
        String searchString1 = getString(true, MAX / 2 + 20);

        watch.start();
        linkedList.contains(searchString0);
        linkedList.contains(searchString1);
        watch.totalTime("Linked List addAll() time = ");//189,64,981
    }

    //Note: Linked List is 500 Milliseconds faster than ArrayList

    class Watch {
        private long startTime;
        private long endTime;

        public void start() {
            startTime = System.nanoTime();
        }

        private void stop() {
            endTime = System.nanoTime();
        }

        public void totalTime(String s) {
            stop();
            System.out.println(s + (endTime - startTime));
        }
    }


    private String[] maxArray() {
        String[] strings = new String[MAX];
        Boolean result = Boolean.TRUE;
        for (int i = 0; i < MAX; i++) {
            strings[i] = getString(result, i);
            result = !result;
        }
        return strings;
    }

    private String getString(Boolean result, int i) {
        return String.valueOf(result) + i + String.valueOf(!result);
    }
}

1
ArrayList를 두 배로 늘릴 필요는 없습니다. 먼저 소스를 확인하십시오.
Danubian Sailor

예제에 결함이 있음에 유의해야합니다. 평균 25 바이트 인 요소의 크기 인 18 + [2, 12] 바이트 ( "true0false", "true500000false") 사이의 문자열에서 제거하고 있습니다. 중간에. 요소 바이트 크기가 증가함에 따라 링크 된 목록의 성능이 향상되고 목록 크기가 증가하면 연속 배열 (목록)의 성능이 향상됩니다. 가장 중요한 것은 문자열에서 .equals ()를 수행하는 것입니다. 이는 저렴한 작업이 아닙니다. 대신 정수를 사용하면 차이가 있다고 생각합니다.
Centril

2
"... 대용량 응용 프로그램에서 더 나쁩니다 ": 이것은 오해입니다. LinkedList모든 요소에 대해 5 개의 필드가있는 노드 오브젝트가 있으므로 훨씬 더 많은 메모리 오버 헤드가 있습니다. 많은 시스템에서 20 바이트의 오버 헤드가 발생합니다. 요소 당 평균 메모리 오버 헤드 ArrayList는 1.5 워드이므로 최악의 경우 6 바이트, 8 바이트가됩니다.
Lii

1
나는 당신의 벤치 마크의 더 나은 버전을 수행 한 결과, 여기 - ArrayList에 대한 APPEND -에 - 엔드 성능이 당신을 위해 인위적으로 낮은, 오퍼레이션과 addAll가 정확히 초기 크기의 스토리지 배열을 제공하기 때문에, 최초의 삽입은 항상 트리거 있도록 arraycopy. 또한 여기에는 데이터가 수집되기 전에 JIT 컴파일을 허용하는 예열주기가 포함됩니다.
BobMcGee

4
Java 8부터 @BillK를 사용하면 반복기를 통해 반복 및 제거하는 것과 비교하여 removeIf(element -> condition)적절한 위치에 사용할 수 있습니다 ArrayList. 모든 개별 요소에 대해 전체 나머지를 이동할 필요가 없으므로 반복자를 통해 제거 하는 것보다 훨씬 빠릅니다 . 이론 상으로는 O (1) LinkedList과 같이 이것이 특정 시나리오에 따라 성능이 더 나빠지 LinkedList지만 단일 노드 만 제거하려면 여러 개의 메모리 액세스가 필요하며, 이는 ArrayList많은 수의 요소를 제거 할 때 필요한 수를 쉽게 초과 할 수 있습니다. .
Holger

50

ArrayList본질적으로 배열입니다. LinkedList이중 연결 목록으로 구현됩니다.

get매우 분명하다. 인덱스를 사용하여 임의 액세스를 허용 ArrayList하므로 O (1) for ArrayList입니다. LinkedList인덱스를 먼저 찾아야하므로 O (n) for 입니다. 참고 : add및의 버전이 다릅니다 remove.

LinkedList추가 및 제거 속도가 빠르지 만 가져 오기 속도가 느립니다. 간단히 말해 다음과 같은 LinkedList경우에 선호됩니다.

  1. 요소에 대한 많은 랜덤 액세스가 없습니다
  2. 많은 추가 / 제거 작업이 있습니다

=== ArrayList ===

  • 추가 (E e)
    • ArrayList의 끝에 추가
    • 메모리 크기 조정 비용이 필요합니다.
    • O (n) 최악, O (1) 상각
  • 추가 (int index, E element)
    • 특정 인덱스 위치에 추가
    • 이동 및 가능한 메모리 크기 조정 비용 필요
    • 의 위에)
  • 제거 (INT 인덱스)
    • 지정된 요소를 제거
    • 이동 및 가능한 메모리 크기 조정 비용 필요
    • 의 위에)
  • 제거 (오브젝트 o)
    • 이 목록에서 지정된 요소의 첫 항목을 제거하십시오.
    • 먼저 요소를 검색 한 다음 메모리 크기 조정 및 이동
    • 의 위에)

=== LinkedList의 ===

  • 추가 (E e)

    • 목록의 끝에 추가
    • O (1)
  • 추가 (int index, E element)

    • 지정된 위치에 삽입
    • 위치를 먼저 찾아야합니다
    • 의 위에)
  • 없애다()
    • 목록의 첫 번째 요소를 제거
    • O (1)
  • 제거 (INT 인덱스)
    • 지정된 인덱스를 가진 요소를 제거
    • 요소를 먼저 찾아야합니다
    • 의 위에)
  • 제거 (오브젝트 o)
    • 지정된 요소의 첫 항목을 제거
    • 요소를 먼저 찾아야합니다
    • 의 위에)

여기에서 도면이다 programcreek.com는 ( addremove제 1 타입은, 즉,리스트의 마지막 요소를 추가하고 목록에서 지정된 위치에있는 요소를 삭제한다.)

여기에 이미지 설명을 입력하십시오


3
"LinkedList가 추가 / 제거보다 빠릅니다". 잘못되었습니다. stackoverflow.com/a/7507740/638670
Nerrve

49

LinkedList의 저자 Joshua Bloch :

누구든지 실제로 LinkedList를 사용합니까? 나는 그것을 썼고 결코 사용하지 않았다.

링크 : https://twitter.com/joshbloch/status/583813919019573248

다른 답변만큼 유익하지 않은 답변에 대해 유감스럽게 생각하지만 가장 흥미롭고 설명이 필요하다고 생각했습니다.


34

ArrayList무작위로 액세스 할 수 있지만 LinkedList요소를 확장하고 제거 하는 것이 실제로 저렴합니다. 대부분의 경우 ArrayList괜찮습니다.

큰 목록을 작성하고 병목 현상을 측정하지 않으면 차이에 대해 걱정할 필요가 없습니다.


15
LinkedList는 요소를 추가하기에 저렴하지 않습니다. LinkedList에 추가하는 것보다 ArrayList에 백만 개의 요소를 추가하는 것이 거의 항상 빠릅니다. 그리고 실제 코드의 목록은 대부분 백만 요소가 아닙니다.
Porculus

10
어느 시점에서든 LinkedList에 항목을 추가하는 비용을 알고 있습니다. 당신이하지 않는 ArrayList (일반적으로). 백만 개의 항목을 포함하는 ArrayList에 단일 항목을 추가하는 데 시간이 오래 걸릴 있습니다. 공간을 미리 할당하지 않으면 O (n) 연산에 두 배의 저장 공간이 추가됩니다. LinkedList에 항목을 추가하는 것은 O (1)입니다. 나의 마지막 진술은 유효하다.
더스틴

4
ArrayList에 단일 항목을 추가하는 것은 1 백만 또는 10 억에 관계없이 O (1)입니다. LinkedList에 항목을 추가하는 것도 O (1)입니다. "추가"는 끝에 추가하는 것을 의미합니다.
kachanov

구현과 다르게 읽었을 것입니다. 내 경험상 10 억 개의 요소 배열을 복사하는 것이 1 백만 개의 요소 배열을 복사하는 것보다 시간이 오래 걸립니다.
더스틴

6
@kachanov 당신은 더스틴을 오해해야합니다. 10 억 개의 항목으로 구성된 배열을 선언하지 않은 한 결국 배열의 크기를 조정해야합니다. O를 얻습니다 (1)
Stan R.

29

TL; DR 은 최신 컴퓨터 아키텍처로 인해 ArrayList거의 모든 사용 사례에서 훨씬 더 효율적이므로 LinkedList매우 독특하고 극단적 인 경우를 제외하고는 피해야합니다.


이론적으로 LinkedList는 O (1) add(E element)

또한 목록 중간에 요소를 추가하는 것이 매우 효율적입니다.

LinkedList는 캐시 적대적 데이터 구조 이므로 실습은 매우 다릅니다 . 성능 POV LinkedList에서 캐시 친화적 인 것보다 성능이 더 좋은 경우는 거의 없습니다 ArrayList.

다음은 임의의 위치에 요소를 삽입하는 벤치 마크 테스트 결과입니다. 보다시피, 배열 목록은 훨씬 더 효율적이지만 이론적으로 목록 중간에있는 각 삽입물 은 배열 의 n 개 이후 요소를 "이동"해야합니다 (낮은 값이 더 낫습니다).

여기에 이미지 설명을 입력하십시오

차세대 하드웨어 (더 크고 효율적인 캐시)에서 작업-결과는 더욱 결정적입니다.

여기에 이미지 설명을 입력하십시오

LinkedList는 동일한 작업을 수행하는 데 훨씬 더 많은 시간이 걸립니다. 출처 소스 코드

이에 대한 두 가지 주요 이유가 있습니다.

  1. 주로 -노드가 LinkedList메모리에 무작위로 흩어져 있습니다. RAM ( "Random Access Memory")은 실제로 무작위가 아니며 캐시하기 위해 메모리 블록을 가져와야합니다. 이 작업에는 시간이 걸리고 이러한 페치가 자주 발생하면 캐시의 메모리 페이지를 항상 교체해야합니다.-> 캐시 누락-> 캐시가 효율적이지 않습니다. ArrayList요소는 연속 메모리에 저장됩니다. 이는 현대 CPU 아키텍처가 최적화하는 것과 정확히 같습니다.

  2. 보조 LinkedList 포인터가 뒤로 / 앞으로 포인터를 유지하는 데 필요합니다 ArrayList.

btw 인 DynamicIntArray 는 사용자 정의 ArrayList 구현입니다.Int 객체가 아닌 (primitive type)을 이므로 모든 데이터가 실제로 인접하게 저장되므로 훨씬 더 효율적입니다.

기억해야 할 핵심 요소는 메모리 블록 페치 비용이 단일 메모리 셀 액세스 비용보다 중요하다는 것입니다. 따라서 1MB의 순차 메모리 리더가 다른 메모리 블록에서이 양의 데이터를 읽는 것보다 최대 x400 배 더 빠릅니다.

Latency Comparison Numbers (~2012)
----------------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy             3,000   ns        3 us
Send 1K bytes over 1 Gbps network       10,000   ns       10 us
Read 4K randomly from SSD*             150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
Disk seek                           10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from disk    20,000,000   ns   20,000 us   20 ms  80x memory, 20X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

출처 : 모든 프로그래머가 알아야 할 지연 시간

요점을 더 명확하게하기 위해 목록의 시작 부분에 요소를 추가하는 기준을 확인하십시오. 이론 상으로는 LinkedList실제로 빛을 발 ArrayList해야하며 열악한 또는 더 나쁜 경우 결과를 제시 해야하는 유스 케이스입니다.

여기에 이미지 설명을 입력하십시오

참고 : 이것은 C ++ Std lib의 벤치 마크이지만 이전 경험에서는 C ++ 및 Java 결과가 매우 유사하다는 것을 보여주었습니다. 소스 코드

다시, 이론을 변경하고 실제로 만들기 - 메모리의 연속 대량 복사하는 것은 현대의 CPU에 의해 최적화 된 작업입니다 ArrayList/ Vector훨씬 더 효율적


크레딧 : 여기에 게시 된 모든 벤치 마크는 Kjell Hedström에 의해 작성되었습니다 . 그의 블로그 에서 더 많은 데이터를 찾을 수 있습니다


나는 독특하거나 극단적 인 대기열을 부르지 않을 것입니다! fifo 대기열은 ArrayList 대신 LinkedList에서 훨씬 쉽게 구현됩니다. 실제로 자신의 시작을 추적하고 중지하고 자신의 재 할당을 수행해야하기 때문에 ArrayList의 악몽입니다. ​​배열을 사용할 수도 있지만 링크 된 목록은 fifo입니다. Java 구현에 대해서는 잘 모르겠지만 LinkedList는 대기열 및 대기열 제거 작업 모두에 대해 O (1)을 수행 할 수 있습니다 (제거를 위해 tail 요소에 대한 특수 포인터가 필요합니다. .)
Bill K

24

코드가있는 경우 add(0)remove(0)하는을 사용 LinkedList하며 예뻐입니다 addFirst()removeFirst()방법. 그렇지 않으면을 사용하십시오 ArrayList.

물론 구아바ImmutableList 는 가장 친한 친구입니다.


3
작은 목록의 경우 ArrayList.add (0)은 여전히 ​​LinkedList.addFirst ()보다 항상 빠릅니다.
Porculus

1
@Porculus 나는 작은 목록의 경우 ArrayList.add (0)이 더 빠를 것이라는이 주장을 끊임없이 듣고 있습니다.이 작은 것이 얼마나 작습니까? 10 개 원소, 천만개?
garg10may

1
@ garg10may small은 10보다 작습니다.
Jesse Wilson

@Porculus small은 ArrayList의 내부 배열의 최대 용량보다 작음을 의미합니다.
Janac Meena

21

나는 이것이 오래된 게시물이라는 것을 알고 있지만 솔직히 아무도 그 LinkedList구현을 언급했다고 믿을 수 없다 Deque. Deque(및 Queue) 의 방법을 살펴보십시오 . 당신이 공정한 비교를 원하는 경우, 실행 시도 LinkedListArrayDeque와 기능에 대한-기능 비교를.


18

다음은 모두 빅 - 오 표기법 ArrayListLinkedList도는CopyOnWrite-ArrayList :

배열 목록

get                 O(1)
add                 O(1)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

연결 목록

get                 O(n)
add                 O(1)
contains            O(n)
next                O(1)
remove              O(1)
iterator.remove     O(1)

CopyOnWrite-ArrayList

get                 O(1)
add                 O(n)
contains            O(n)
next                O(1)
remove              O(n)
iterator.remove     O(n)

이를 바탕으로 무엇을 선택해야하는지 결정해야합니다. :)


9
>>>> ArrayList add-> O (1) <-tru가 아닙니다. 어떤 경우에는 ArrayList가 더 많은 요소를 추가하기 위해 커져야합니다
kachanov

1
LinkedList remove는 O (1)이 아니므로 제거 할 요소를 검색해야하므로 최악의 경우 O (n) 및 평균 O (n / 2)
garg10may

LinkedList.add()여기에있는 대부분의 답변이 그렇게 말하지만, 둘 다 아닙니다 .
user207421

18

매개 변수 아래에서 LinkedList와 ArrayList wrt를 비교해 보겠습니다.

1. 구현

ArrayList 는 목록 인터페이스의 크기 조정이 가능한 배열 구현입니다.

LinkedList 는 목록 인터페이스의 이중 연결 목록 구현입니다.


2. 성능

  • get (int index) 또는 검색 작업

    ArrayList get (int index) 연산은 상수 시간, 즉 O (1)에서 실행되는 동안

    LinkedList get (int index) 작업 런타임은 O (n)입니다.

    ArrayList 가 LinkedList보다 빠른 이유는 ArrayList 가 내부적으로 배열 데이터 구조를 사용하기 때문에 요소에 인덱스 기반 시스템을 사용하기 때문입니다.

    LinkedList 는 지정된 요소 인덱스에서 노드를 검색하기 위해 시작 또는 끝 (둘 중 더 가까운 쪽)을 반복하므로 요소에 대한 인덱스 기반 액세스를 제공하지 않습니다.

  • insert () 또는 add (Object) 연산

    LinkedList의 삽입 은 일반적으로 ArrayList에 비해 빠릅니다. LinkedList에서 추가 또는 삽입은 O (1) 연산입니다.

    ArrayList에 있는 동안 배열이 전체 최악의 경우 배열 크기를 조정하고 새 배열에 요소를 복사하는 추가 비용이 발생하여 ArrayList O (n)에서 추가 작업을 런타임으로 수행합니다. 그렇지 않으면 O (1)입니다 .

  • 제거 (int) 작업

    LinkedList의 제거 작업은 일반적으로 ArrayList와 동일합니다 (예 : O (n)).

    에서 LinkedList의 두 오버로드 제거 방법이 있습니다. 하나는 목록의 헤드를 제거하고 상수 시간 O (1)에서 실행되는 매개 변수가없는 remove ()입니다. LinkedList의 다른 오버로드 된 remove 메소드는 remove (int) 또는 remove (Object)로, Object 또는 int를 매개 변수로 제거합니다. 이 메소드는 오브젝트를 찾을 때까지 LinkedList를 순회하고 원래 목록에서 링크를 해제합니다. 따라서이 메소드 런타임은 O (n)입니다.

    반면에 ArrayList를 제거 (int)에있어서, 갱신 된 새로운 배열 이전 배열 요소를 복사하는 것을 포함 따라서 그 런타임은 O (N)이다.


3. 역 반복자

DownedIterator ()를 사용하여 LinkedList 를 역방향으로 반복 할 수 있습니다.

ArrayList 에는 내려가는 Iterator ()가 없으므로 ArrayList를 거꾸로 반복하려면 자체 코드를 작성해야합니다.


4. 초기 용량

생성자가 오버로드되지 않으면 ArrayList 는 초기 용량 10의 빈 목록을 작성하지만

LinkedList 는 초기 용량없이 빈 목록 만 구성합니다.


5. 메모리 오버 헤드

LinkedList 의 노드는 다음 및 이전 노드의 주소를 유지해야하므로 LinkedList의 메모리 오버 헤드는 ArrayList에 비해 더 높습니다. 동안

에서는 ArrayList를 각 인덱스 만 실제 객체 (데이터)를 보유하고있다.


출처


18

나는 일반적으로 특정 목록에서 수행 할 작업의 시간 복잡성에 따라 다른 것을 사용합니다.

|---------------------|---------------------|--------------------|------------|
|      Operation      |     ArrayList       |     LinkedList     |   Winner   |
|---------------------|---------------------|--------------------|------------|
|     get(index)      |       O(1)          |         O(n)       | ArrayList  |
|                     |                     |  n/4 steps in avg  |            |
|---------------------|---------------------|--------------------|------------|
|      add(E)         |       O(1)          |         O(1)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     | O(n) in worst case  |                    |            |
|---------------------|---------------------|--------------------|------------|
|    add(index, E)    |       O(n)          |         O(n)       | LinkedList |
|                     |     n/2 steps       |      n/4 steps     |            |
|                     |---------------------|--------------------|            |
|                     |                     |  O(1) if index = 0 |            |
|---------------------|---------------------|--------------------|------------|
|  remove(index, E)   |       O(n)          |         O(n)       | LinkedList |
|                     |---------------------|--------------------|            |
|                     |     n/2 steps       |      n/4 steps     |            |
|---------------------|---------------------|--------------------|------------|
|  Iterator.remove()  |       O(n)          |         O(1)       | LinkedList |
|  ListIterator.add() |                     |                    |            |
|---------------------|---------------------|--------------------|------------|


|--------------------------------------|-----------------------------------|
|              ArrayList               |            LinkedList             |
|--------------------------------------|-----------------------------------|
|     Allows fast read access          |   Retrieving element takes O(n)   |
|--------------------------------------|-----------------------------------|
|   Adding an element require shifting | o(1) [but traversing takes time]  |
|       all the later elements         |                                   |
|--------------------------------------|-----------------------------------|
|   To add more elements than capacity |
|    new array need to be allocated    |
|--------------------------------------|

삽입 / 제거 앞 / 뒤가 모두 O (1)이기 때문에 ArrayDeque는 배열을 향하여 조금 더 균형을 맞 춥니 다. 연결 목록에서 여전히이기는 유일한 것은 순회하는 동안 추가 / 제거하는 것입니다 (반복자 작업).
Bill K

14

위의 다른 좋은 주장 외에도 인터페이스를 구현 하는 반면 인터페이스 는 ArrayList구현하는 것을 알 수 RandomAccess있습니다 .LinkedListQueue

따라서 효율성과 행동의 차이와 함께 약간 다른 문제를 해결합니다 (방법 목록 참조).


10

목록에서 더 많은 작업을 수행 할 작업에 따라 다릅니다.

ArrayList색인화 된 값에 액세스하는 것이 더 빠릅니다. 개체를 삽입하거나 삭제할 때 훨씬 더 나쁩니다.

자세한 내용을 보려면 배열과 연결된 목록의 차이점에 대해 설명하는 기사를 읽으십시오.


2
더 많은 것을 읽지 않으려면 코드를 작성하십시오. ArrayList 구현이 LinkedList보다 삽입 및 삭제가 빠르다는 것을 알 수 있습니다.
kachanov

8

배열 목록은 본질적으로 항목 등을 추가하는 메소드가있는 배열입니다 (대신 일반 목록을 사용해야합니다). 인덱서를 통해 액세스 할 수있는 항목의 모음입니다 (예 : [0]). 한 항목에서 다음 항목으로의 진행을 의미합니다.

연결된 목록은 한 항목에서 다음 항목으로의 진행을 지정합니다 (항목 a-> 항목 b). 배열 목록과 동일한 효과를 얻을 수 있지만 연결된 목록은 이전 항목을 따라야 할 항목을 절대적으로 나타냅니다.


8

Java Tutorials-List 구현을 참조하십시오 .


2
안녕하세요 @chharvey, 링크 답변 만 6 개의 공감대를 받습니까? 링크를 지원할 수있는 포인트를 추가하십시오. 오라클이 링크를 변경하면 어떻게됩니까?

7

링크 된 목록의 중요한 기능 (다른 답변에서 읽지 않은)은 두 목록의 연결입니다. 배열을 사용하면 연결된 목록이있는 O (n) (+ 일부 재 할당의 오버 헤드)이며 이것은 O (1) 또는 O (2) ;-)입니다.

중요 : Java의 경우 LinkedList이는 사실이 아닙니다! Java에 링크 된 목록에 빠른 연결 방법이 있습니까?를 참조하십시오 .


2
방법 것입니다? 이것은 링크 된리스트 데이터 구조에는 해당되지만 Java LinkList 오브젝트에는 해당되지 않습니다. next한 목록에서 두 번째 목록의 첫 번째 노드를 가리킬 수는 없습니다 . 유일한 방법은 addAll()요소를 순차적으로 추가하는 것입니다 add(). 각 요소를 반복하고 호출하는 것보다 낫습니다 . O (1)에서이 작업을 빠르게 수행하려면 org.apache.commons.collections.collection.CompositeCollection과 같은 합성 클래스가 필요하지만 모든 종류의 List / Collection에서 작동합니다.
Kevin Brock

예, 맞습니다. 그에 따라 답변을 편집했습니다. 그러나 LinkedList로 그것을 수행하는 방법에 대한이 답변을보십시오 : stackoverflow.com/questions/2494031/…
Karussell

7

ArrayList 및 LinkedList에는 고유 한 장단점이 있습니다.

ArrayList는 다음 노드에 대한 포인터를 사용하는 LinkedList와 비교하여 연속 메모리 주소를 사용합니다. 따라서 ArrayList에서 요소를 찾으려면 LinkedList로 n 반복을 수행하는 것보다 빠릅니다.

반면에, LinkedList에서의 삽입과 삭제는 포인터를 변경하기 만하면되기 때문에 훨씬 쉽습니다. ArrayList는 삽입이나 삭제를 위해 shift 연산을 사용합니다.

앱에서 자주 검색 작업을 수행하는 경우 ArrayList를 사용하십시오. 자주 삽입하고 삭제하는 경우 LinkedList를 사용하십시오.


6

응답을 읽었지만 의견을 듣기 위해 공유하려는 ArrayList에 대해 항상 LinkedList를 사용하는 시나리오가 있습니다.

DB에서 얻은 데이터 목록을 반환하는 메서드를 사용할 때마다 항상 LinkedList를 사용합니다.

내 근거는 내가 얻는 결과의 수를 정확히 알 수 없기 때문에 (메모리 용량과 실제 요소 수의 차이가있는 ArrayList에서와 같이) 메모리 낭비가 없으며, 시도하는 데 시간이 낭비되지 않는다는 것입니다 용량을 복제하십시오.

ArrayList까지는 배열의 중복을 최소화하기 위해 항상 초기 용량의 생성자를 사용해야한다는 데 동의합니다.


5

ArrayList그리고 LinkedList모두 구현 List interface 및 그 방법과 결과는 거의 동일하다. 그러나 요구 사항에 따라 다른 것보다 더 나은 차이점이 거의 없습니다.

ArrayList 대 LinkedList

1) Search: ArrayList검색 작업은 검색 작업에 비해 매우 빠릅니다 LinkedList. get(int index)의는 ArrayList의 성능을 제공 O(1)하면서 LinkedList성능입니다 O(n).

Reason: ArrayList배열 데이터 구조를 암시 적으로 사용하므로 목록에서 요소를 더 빠르게 검색 할 수 있으므로 요소에 대한 색인 기반 시스템을 유지 관리합니다. 다른쪽에 LinkedList는 요소를 검색하기 위해 모든 요소를 ​​통과해야하는 이중 연결 목록이 구현되어 있습니다.

2) Deletion: LinkedList제거 작업은 O(1)성능을 ArrayList제공 하는 동시에 가변 성능을 제공합니다. O(n)최악의 경우 (첫 번째 요소를 제거하는 동안) 및 O(1)최상의 경우 (마지막 요소를 제거하는 동안).

결론 : LinkedList 요소 삭제는 ArrayList에 비해 빠릅니다.

이유 : LinkedList의 각 요소는 목록의 두 인접 요소를 가리키는 두 개의 포인터 (주소)를 유지합니다. 따라서 제거하려면 제거 할 노드의 두 인접 노드 (요소)에서 포인터 위치 만 변경하면됩니다. ArrayList에있는 동안 모든 요소는 제거 된 요소로 생성 된 공간을 채우기 위해 이동해야합니다.

3) Inserts Performance: LinkedListadd 메소드는 최악의 경우 O(1)성능을 ArrayList제공합니다 O(n). 이유는 제거에 대해 설명 된 것과 같습니다.

4) Memory Overhead: ArrayList인덱스 및 요소 데이터를 LinkedList유지하면서 요소 데이터와 인접 노드에 대한 두 개의 포인터를 유지

따라서 LinkedList에서 메모리 소비가 비교적 높습니다.

이 클래스들 사이에는 다음과 같은 유사점이 거의 없습니다.

  • ArrayList와 LinkedList는 모두 List 인터페이스의 구현입니다.
  • 둘 다 요소 삽입 순서를 유지합니다. 즉, ArrayList 및 LinkedList 요소를 표시하는 동안 결과 집합은 요소가 List에 삽입 된 순서와 동일한 순서를 갖습니다.
  • 이 두 클래스는 동기화되지 않으며 Collections.synchronizedList 메소드를 사용하여 명시 적으로 동기화 할 수 있습니다.
  • iteratorlistIterator이러한 클래스에 의해 반환은 fail-fast(리스트가 구조적으로 반복자의 작성 후에 이외 방법으로 변경되면 iterator’s자신의 제거 또는 추가 방법, 반복자 것이다 ).throwConcurrentModificationException

LinkedList를 언제 사용하고 언제 ArrayList를 사용합니까?

  • 위에서 설명한 것처럼 삽입 및 제거 작업은 (O(1))LinkedList비해 우수한 성능 을 제공 합니다 ArrayList(O(n)).

    따라서 응용 프로그램에 빈번한 추가 및 삭제가 필요한 경우 LinkedList가 최선의 선택입니다.

  • 검색 ( get method) 작업은 빠르지 Arraylist (O(1))만 빠르지 않습니다LinkedList (O(n))

    따라서 추가 및 제거 작업이 적고 검색 작업 요구 사항이 더 많으면 ArrayList가 가장 좋습니다.


5

ArrayList의 get (i) 작업이 LinkedList보다 빠릅니다.
ArrayList : List 인터페이스의 크기 조정 가능 배열 구현
LinkedList : List 및 Deque 인터페이스의 이중 연결 목록 구현

목록으로 색인을 생성하는 작업은 목록을 처음 또는 끝에서, 지정된 색인에 가까운 쪽을 순회합니다.


5

1) 기본 데이터 구조

ArrayList와 LinkedList의 첫 번째 차이점은 ArrayList는 Array에 의해 지원되고 LinkedList는 LinkedList에 의해 지원된다는 사실입니다. 이로 인해 성능에 차이가 생길 수 있습니다.

2) LinkedList는 Deque를 구현합니다.

ArrayList와 LinkedList의 또 다른 차이점은 LinkedList는 List 인터페이스와 별도로 add () 및 poll () 및 기타 여러 Deque 함수에 대한 첫 번째 작업을 제공하는 Deque 인터페이스도 구현한다는 것입니다. 3) ArrayList에 요소 추가 ArrayList에 요소를 추가하는 것은 Array의 크기 조정을 트리거하지 않으면 O (1) 연산입니다.이 경우 O (log (n))가됩니다. 반면에 LinkedList는 탐색이 필요하지 않으므로 O (1) 조작입니다.

4) 위치에서 요소 제거

특정 인덱스에서 요소를 제거하기 위해 (예 : remove (index)를 호출하여) ArrayList는 복사 작업을 수행하여 O (n)에 가깝게 만드는 반면 LinkedList는 해당 지점으로 이동하여 O (n / 2)로 만듭니다. 근접성에 따라 어느 방향에서나 이동할 수 있기 때문입니다.

5) ArrayList 또는 LinkedList를 반복

반복은 LinkedList 및 ArrayList 둘 다에 대한 O (n) 연산입니다. 여기서 n은 요소의 수입니다.

6) 위치에서 요소 검색

get (index) 연산은 ArrayList에서 O (1)이고 LinkedList에서 O (n / 2)는 해당 항목까지 통과해야하기 때문에 필요합니다. 그러나 큰 O 표기법에서 O (n / 2)는 상수를 무시하기 때문에 O (n)입니다.

7) 기억

LinkedList는 랩퍼 오브젝트 인 Entry를 사용하여 데이터를 저장하기위한 정적 중첩 클래스이며 다음과 이전의 두 노드를 저장하는 반면 ArrayList는 Array에 데이터를 저장합니다.

따라서 ArrayList의 경우 메모리가 하나의 Array에서 다른 Array로 내용을 복사 할 때 크기 조정 작업을 수행하는 경우를 제외하고 LinkedList보다 메모리 요구 사항이 적습니다.

배열이 충분히 크면 해당 시점에서 많은 메모리가 소비되고 가비지 수집이 트리거되어 응답 시간이 느려질 수 있습니다.

ArrayList와 LinkedList의 위의 모든 차이점에서 remove () 또는 get ()보다 add () 작업을 자주 수행하는 경우를 제외하고 거의 모든 경우 ArrayList가 LinkedList보다 더 나은 선택 인 것 같습니다.

연결된 목록이 내부적으로 해당 위치에 대한 참조를 유지하고 O (1) 시간에 액세스 할 수 있기 때문에 시작 또는 종료에서 요소를 추가하거나 제거하는 경우 ArrayList보다 연결 목록을 수정하는 것이 더 쉽습니다.

즉, 요소를 추가하려는 위치에 도달하기 위해 링크 된 목록을 탐색 할 필요가 없습니다.이 경우 추가는 O (n) 연산이됩니다. 예를 들어, 링크 된 목록의 중간에 요소를 삽입하거나 삭제합니다.

제 생각에는 Java의 실질적인 목적을 위해 LinkedList보다 ArrayList를 사용하십시오.


1
나는 이것이 전체 그룹의 가장 잘 알려진 대답이라고 생각합니다. 정확하고 유익한 정보입니다. 마지막 줄을 바꾸는 것이 좋습니다. 결국에는 "대기열 제외"를 추가하는 것이 좋습니다. 이는 연결 목록에 전혀 의미가없는 매우 중요한 구조입니다.
Bill K

3

여기서 본 테스트 중 하나는 테스트를 한 번만 수행합니다. 그러나 내가 주목 한 것은 이러한 테스트를 여러 번 실행해야하며 결국 시간이 수렴한다는 것입니다. 기본적으로 JVM은 예열해야합니다. 내 특정 유스 케이스의 경우 약 500 개의 항목으로 확장되는 목록에 항목을 추가 / 제거해야했습니다. 내 테스트에서 LinkedList와 빠른 나온 LinkedList약 50,000 NS에오고 ArrayList제공하거나 걸릴 ...에서 90,000 주위 NS오고. 아래 코드를 참조하십시오.

public static void main(String[] args) {
    List<Long> times = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        times.add(doIt());
    }
    System.out.println("avg = " + (times.stream().mapToLong(x -> x).average()));
}

static long doIt() {
    long start = System.nanoTime();
    List<Object> list = new LinkedList<>();
    //uncomment line below to test with ArrayList
    //list = new ArrayList<>();
    for (int i = 0; i < 500; i++) {
        list.add(i);
    }

    Iterator it = list.iterator();
    while (it.hasNext()) {
        it.next();
        it.remove();
    }
    long end = System.nanoTime();
    long diff = end - start;
    //uncomment to see the JVM warmup and get faster for the first few iterations
    //System.out.println(diff)
    return diff;
}

2

remove ()와 insert ()는 모두 ArrayList와 LinkedList에 대해 런타임 효율성이 O (n)입니다. 그러나 선형 처리 시간의 배후에있는 이유는 두 가지 매우 다른 이유 때문입니다.

ArrayList에서는 O (1)의 요소에 도달하지만 실제로 다음 항목을 모두 변경해야하기 때문에 무언가를 제거하거나 삽입하면 O (n)이됩니다.

LinkedList에서 원하는 요소에 실제로 도달하려면 O (n)이 필요합니다. 원하는 인덱스에 도달 할 때까지 맨 처음부터 시작해야하기 때문입니다. remove ()에 대한 1 개의 참조와 insert ()에 대한 2 개의 참조 만 변경하면되기 때문에 실제로 제거하거나 삽입하는 것은 일정합니다.

삽입 및 제거에 더 빠른 둘 중 어느 것이 발생하는지에 따라 다릅니다. 시작에 가까워지면 비교적 적은 수의 요소를 거쳐야하기 때문에 LinkedList가 더 빠릅니다. 우리가 끝에 가까워지면 ArrayList가 더 빠를 것입니다. 왜냐하면 우리는 일정한 시간에 도착하고 그 뒤에 나오는 몇 개의 나머지 요소 만 변경하면되기 때문입니다. 중간에서 정확하게 수행하면 n 개의 요소를 통과하는 것이 n 개의 값을 이동하는 것보다 빠르기 때문에 LinkedList가 더 빠릅니다.

보너스 : ArrayList에 대해이 두 가지 방법을 O (1)로 만드는 방법은 없지만 실제로 LinkedLists에는이를 수행하는 방법이 있습니다. 도중에 요소를 제거하고 삽입하는 전체 List를 살펴보고 싶다고 가정 해 봅시다. 일반적으로 LinkedList를 사용하여 각 요소의 맨 처음부터 시작합니다. 또한 Iterator로 작업중인 현재 요소를 "저장"할 수도 있습니다. Iterator의 도움으로 LinkedList에서 작업 할 때 remove () 및 insert ()에 대한 O (1) 효율성을 얻습니다. 그것이 LinkedList가 ArrayList보다 항상 좋은 곳이라는 것을 알고있는 유일한 성능 이점입니다.


1

ArrayList는 AbstractList를 확장하고 목록 인터페이스를 구현합니다. ArrayList는 동적 배열입니다.
기본적으로 배열

의 단점을 극복하기 위해 생성되었다고 말할 수 있습니다 . LinkedList 클래스는 AbstractSequentialList를 확장하고 List, Deque 및 Queue 인터페이스를 구현합니다.
성능
arraylist.get()은 O (1)이지만 linkedlist.get()O (n)
arraylist.add()은 O (1)이고 linkedlist.add()0 (1)
arraylist.contains()은 O (n)이며 linkedlist.contains()O (n)
arraylist.next()은 O (1)이며 linkedlist.next()O (1)
arraylist.remove()은 O (n)입니다. 반면 linkedlist.remove()에 O (1)
는 arraylist
iterator.remove()는 O (n)
이며, linkedlist
iterator.remove()는 O (1)입니다

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