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

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

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

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

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



답변

요약 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초기 용량이 더 높은 구성 요소를 구성하십시오 .


답변

지금까지 아무도이 목록 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)


답변

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

LinkedList짜증나 :

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

답변

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

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

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


답변

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는 한 번만 읽을 수있는 많은 또는 어 펜더에는 좋지만 추가 또는 제거는 앞면 또는 가운데에는 좋지 않습니다.


답변

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

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

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


답변

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

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