1. Arraylist 소스 코드 분석 (JDK7)
ArrayList는 내부적으로 동적 객체 배열을 유지합니다. Arraylist의 동적 추가 및 삭제는이 그룹 쌍의 동적 추가 및 삭제입니다.
1. Arraylist 구성 및 초기화
ArrayList Instance // ArrayList 기본 용량 개인 정적 최종 최종 최종 int default_capacity = 10; // 기본 빈 개체 배열, 빈 배열 최종 객체를 정의하는 데 사용됩니다.
배열리스트 생성자 :
매개 변수 생성자 없음 : 즉, 빈 개체를 구성합니다 []
public arraylist () {super (); this.elementData = empty_elementData;} 용량 크기 구성을 지정합니다.
public arraylist (int initialcapacity) {super (); if (InitialCapacity <0) New ImperalArgumentException을 던지십시오 ( "불법 용량 :"+ 초기 범위); this.elementData = 새 개체 [초기 커피];} 컬렉션 인터페이스를 구현하는 수집 구조를 지정합니다.
public arraylist (collection <? extends e> c) {elementData = c.toArray (); 크기 = ElementData.length; // c.toArray는 (잘못된) return object [] (6260652 참조) if (elementData.getClass ()! = Object []. class) emEctData = arrays.copyof (ElementData, size, object []. class);};또한 컬렉션의 역할과 Java-Collection-Framwork가 목록, 세트 및 기타 인터페이스를 직접 사용하는 대신 컬렉션 인터페이스를 설계하는 이유를 설명합니다.
2. Arraylist의 용량 할당 메커니즘
ArrayList의 용량 캡 : ArrayList 용량은 상한이며 이론은 Integer.max_value -8 크기 용량을 할당 할 수 있습니다. 그러나 할당 할 수있는 양은 스택 설정에 따라 다르며 VM 매개 변수를 설정해야합니다.
비공개 정적 최종 int max_array_size = integer.max_value -8;
ADD 메소드를 호출 할 때 볼륨을 연장하십시오
public boolean add (e e) {enseerecapacitysinternal (size + 1); // MODCOUNT를 증가시킵니다 !! ElementData [size ++] = e; 진실을 반환하십시오. }ensurecapacity internal (int) 메소드는 실제로 최소 확장 크기를 결정합니다.
Private void ensureCapacityInternal (int mincapacity) {if (elementData == empty_elementData) {minCapacity = math.Max (default_capacity, mincapacity); } restexplicitCapicacity (mincapacity); } private void inricexplicitCapicacity (int mincapacity) {modcount ++; // 오버 플로우에 민감한 코드 if (minCapacity -ElementData.length> 0) grow (mincapacity); } ModCount 정보 : ModCount는 Abstract Class AbstractList에 정의되어 있습니다. 소스 코드 주석은 기본적으로 사용을 설명합니다. 반복자를 사용하여 트래버스를 사용하면 목록의 요소에 구조적 변경이 있는지 확인하는 데 사용됩니다 (목록 요소 수의 수가 변경됨). 주로 다중 스레드 환경에서 하나의 스레드가 반복되는 것을 방지하고 다른 스레드 가이 목록의 구조를 수정하는 것을 방지합니다.
성장 방법은 실제 확장 방법입니다
Private Void Grow (int mincapacity) {// 오버 플로우에 민감한 코드 int OldCapacity = ElementData.length; int newCapacity = OldCapacity + (OldCapacity >> 1); if (newCapacity -MinCapacity <0) newCapacity = mincapacity; if (newCapacity -max_array_size> 0) newCapacity = hugecapacity (mincapacity); // mincapacity는 일반적으로 크기에 가깝기 때문에 이것은 다음과 같습니다. elementData = arrays.copyof (elementData, newCapacity); }얼마나 많은 용량이 확장되는지에 대한 hugecapacity 방법도 있습니다.
개인 정적 int hugecapacity (int mincapacity) {if (mincapacity <0) // 오버 플로우 던지기 새 OutofMemoryError (); return (mincapacity> max_array_size)? integer.max_value : max_array_size; } 요약 :
각 확장에는 배열 사본이 동반되므로 한 번에 올바른 용량을 제공하면 성능이 약간 향상됩니다.
다음 수치는 내가 요약 한 전체 확장 프로세스입니다.
3. Arraylist 반복자
Arraylist와 Listitr의 두 가지 주요 반복자가 있지만 JDK1.8에는 ArrayListSpliterator도 추가됩니다. ITR과 Listitr의 소스 코드 분석을 각각 배우겠습니다.
(1) ITR : 뒤로 갈 수 있습니다
개인 클래스 ITR은 반복자 <e> {int cursor를 구현합니다. // int lastret = -1을 반환 할 다음 요소의 색인; // 마지막 요소의 색인이 반환되었습니다. -1이없는 경우 // expectionModCount는 modCount int exploymodcount = modcount의 사본 인 경우; public boolean hasnext () {return cursor! = size; } @SuppressWarnings ( "선택 취소") public e next () {checkforcomodification (); // 현재 위치 기록 int i = 커서; if (i> = size) 새 nosuchelementException ()을 던지십시오. Object [] ElementData = ArrayList.this.elementData; if (i> = ElementData.length) Throw New ConcurrentModificationException (); // 다음 요소의 위치 cursor = i + 1; return (e) ElementData [lastret = i]; } // 반복자의 제거 메소드를 사용하여 공개 void remove () {if (lastret <0) 새 불법 스테이트 렉스크 (); CheckforComodification (); {// 내부 클래스가 OUTER Class arrayList를 호출하는 방법에 유의하십시오. this.remove (lastret); // 제거 후 각 포인터 커서의 위치를 다시 조정해야합니다. = lastret; lastret = -1; 예상 modCount = modCount; } catch (indexOutOfBoundSexception ex) {throw new concurrentModificationException (); }} Final void checkforcomodification () {if (modCount! = excliteModCount) 새 concurrentModificationException (); }} 소스 코드에서 ITR ITERATOR가 전방 반복자임을 알 수 있습니다. 이는 배열 목록에서 요소를 얻는 다음 방법을 제공합니다.
Checkforcomodification은 Java-Collection-Framwork에서 FAIL-FAST ERROR DETECTION 메커니즘입니다. 다중 스레드 환경에서 동일한 세트에서의 작동은 실패한 메커니즘을 트리거하고 동시 모형 지출 예외를 던질 수 있습니다.
ITR 반복자는 예상 ModCount Record ModCount의 사본을 정의합니다. AdrrayList가 추가, 제거 및 명확한 메소드와 같은 구조를 변경하기 위해 작업을 수행하면 MODCOUNT의 값이 변경됩니다.
ITR 소스 코드를 통해 다음 번 호출 및 제거 메소드를 호출하면 FAIL-FAST 검사가 트리거됩니다. 현재 다른 스레드가 세트 구조를 변경하는 동안 세트 구조를 변경하는 작업을 수행하는 작업을 수행 할 때 예외가 발생하는 경우 세트를 가로 지릅니다.
(2) Listitr : 전방 및 후진 트래버스를 지원합니다. Listitr의 소스 코드를 살펴 보겠습니다.
Private Class Listitr 확장 ITR 구현 ListIterator <e> {Listitr (int index) {super (); 커서 = 색인; } public boolean hasprevious () {return cursor! = 0; } public int nextIndex () {return cursor; } public int previousIndex () {return cursor -1; } @SuppressWarnings ( "확인되지 않은") public e previous () {checkforcomodification (); // ArrayList의 이전 요소의 위치 int i = 커서 -1; if (i <0) 새 nosuchelementException ()을 던지십시오. Object [] ElementData = ArrayList.this.elementData; if (i> = ElementData.length) Throw New ConcurrentModificationException (); 커서 = i; return (e) ElementData [lastret = i]; } // 세트 메소드 가이 반복자 공개 void set (e e)에 추가됩니다. {if (lastret <0) Throw New OrgalStateException (); CheckforComodification (); try {arraylist.this.set (lastret, e); } catch (indexOutOfBoundSexception ex) {throw new concurrentModificationException (); }} //이 반복자는 추가 메소드를 추가하여 public void add (e e) {checkforcomodification (); {int i = 커서; arraylist.this.add (i, e); // 포인터 위치에 설명 Cursor = i + 1; lastret = -1; 예상 modCount = modCount; } catch (indexOutOfBoundSexception ex) {throw new concurrentModificationException (); }}}Listitr의 구현은 기본적으로 ITR과 동일하며, 이전에 통과 할 수있는 방법과 추가 및 설정 방법을 추가합니다.
(3) java.util.concurrent의 copyonwritearraylist를 사용하여 빠른짜리 문제를 해결하십시오
CopyonwritearRaylist는 스레드-안전합니다. 자세한 내용은 메소드 추가 소스 코드를 살펴 보겠습니다.
Public Boolean Add (E E) {Final ReintrantLock Lock = this.lock; lock.lock (); {object [] elements = getArray (); int len = elements.length; Object [] newElements = arrays.copyof (요소, len + 1); NewElements [len] = e; setArray (NewElements); 진실을 반환하십시오. } 마침내 {lock.unlock (); }} CopyonWriteAreRayList는 쓰기에 복사 된 배열리스트입니다. 데이터 작성 작업을 시작할 때 Array.copyof는 새로운 배열이며, 이는 읽기 작업에 영향을 미치지 않습니다.
이 비용은 메모리를 잃고 성능 문제를 가져 오는 것입니다. COPYONWRITEARRAYLIST가 작성되면 메모리에서 복사 객체가 생성되고 원래 객체가 여전히 존재합니다.
CopyonWriteAreRayList는 데이터의 일관성을 실시간으로 보장 할 수 없으며 결과 일관성 만 보장 할 수 있습니다. 더 많은 것을 읽고 더 많이 쓰고 동시 상황에서 덜 쓰는 캐시와 같은 시나리오에 적합합니다.
(4) 기타 방법 배열리스트 소스 코드 :
개인 메소드 BatchRemove (Collection <?> C, 부울 보완), 즉 배치 제거 작업
Private Boolean BatchRemove (Collection <?> C, 부울 보완) {// 최종 사용 이유는 아래에 언급되어 있습니다. int r = 0, w = 0; 부울 수정 = 거짓; 목록의 요소를 통해 {// 고음성을 시도하고 (; r <size; r ++) if (c.contains (elementData [r]) == 보완) 요소 data [w ++] = ElementData [r]; } 마침내 {// 시도에서 예외가 발생하면 데이터 일관성을 확인하고 다음과 같은 복사 작업을 수행합니다. W += 크기 -R; } // 사용하지 않은 요소를 청소하고 (w! = size)를 재활용하도록 GC에 알림 (int i = w; i <size; i ++) elementData [i] = null; modcount += size -w; 크기 = w; 수정 = true; }} 리턴 수정; } 최종에 의해 수정 된 변수는 나중에 데이터의 일관성을 유지하기위한 동일한 참조를 나타냅니다.
이 방법에서 Collection C에서 요소를 유지하려면 보완 값이 참입니다. C에서 요소를 제거하려면 보완 값이 False입니다. 이것은 각각 retainall 및 removeall 방법이됩니다.
스왑 : Arraylist의 두 위치를 스왑하십시오
2. Linkedlist 소스 코드 분석 (JDK7)
LinkedList는 링크 된 목록입니다. 주문 테이블과 비교하여 링크 된 목록은 데이터를 저장하기 위해 연속 메모리 장치를 사용할 필요가 없습니다. 컨테이너 구조를 수정하여 움직이는 요소의 문제를 줄이고 순차적 접근은 비교적 효율적입니다.
1. 노드의 정의
JDK의 LinkedList는 양방향 링크 목록으로, 각 노드는 각각 이전 노드와 다음 노드에 대한 정보를 저장합니다. 그 정의는 다음과 같습니다.
개인 정적 클래스 노드 <e> {e 항목; Node <e> 다음; 노드 <e> 이전; 노드 <e> (노드 <e> prev, e 요소, 노드 <e> next) {this.item = element; this.next = 다음; this.prev = 이전; }}2. Linkedlist 구성 및 초기화
멤버 : 3 멤버 변수는 LinkedList로 유지 관리되어 링크 된 목록, 전임자 및 노드의 후속에 노드 수를 기록합니다.
과도 int size = 0; 과도 노드 <e> 먼저; 과도 노드 <e> 마지막;
생성자 : 기본 생성자는 빈 링크드리스트를 구성하는 것입니다.
public linkedlist () {}또는 다른 컨테이너를 기반으로 구성하고 나중에 주문한 링크 목록을 작성하기 위해 생성자를 작성합니다.
public linkedlist (collection <? extends e> c) {this (); addall (c);}여기에 약간의 추가가 있습니다. 일반 수정 자의 차이는? 슈퍼 T 및 확장 T는 슈퍼 T와 제네릭에서 T의 차이에 대한이 기사를 참조하십시오.
3. Linkedlist의 구조적 작동
헤더 삽입 방법 : 즉, 링크 된 목록의 헤더에 요소를 삽입하십시오.
개인 void linkfirst (e e) {Final Node <e> f = 첫 번째; 최종 노드 <e> newnode = new Node <> (null, e, f); 첫 번째 = 뉴 노드; // 빈 링크 목록인지 판단합니다. (f == null) last = newnode; else f.prev = newnode; 크기 ++; 모드 카운트 ++; } 테일 삽입 방법 : 즉, 링크 된 목록의 끝에 요소를 삽입하십시오.
void linklast (e e) {최종 노드 <e> l = 마지막; 최종 노드 <e> newnode = new Node <> (l, e, null); 마지막 = 뉴 노드; if (l == null) first = newnode; else l.next = newnode; 크기 ++; 모드 카운트 ++; } 현재 노드에 삽입하기 전에 : 현재 노드의 전면 드라이브를 찾으십시오.
void linkbefore (e e, node <e> succ) {// 노드가 비어 있지 않은지 결정하십시오. 물론 최종 노드 <e> pred = succ.prev; 최종 노드 <e> newnode = new Node <> (예측, e, succ); succ.prev = newnode; // 현재 노드가 첫 번째 노드인지 확인 if (pred == null) first = newNode; else pred.next = newnode; 크기 ++; 모드 카운트 ++; } 헤더 삭제 방법 : 링크 된 목록의 첫 번째 노드 삭제
private e unlinkfirst (node <e> f) {// assert f == first && f! = null; 최종 E 요소 = F.Item; 최종 노드 <E> 다음 = F.Next; f.item = null; f.next = null; // 먼저 GC를 도와주세요 = 다음; if (next == null) last = null; else next.prev = null; 크기--; 모드 카운트 ++; 리턴 요소; } 테일 삭제 방법 : 링크 된 목록의 마지막 노드 삭제
private e unlinklast (node <e> l) {// l == last and l! = null final e element = l.item을 확인하십시오. 최종 노드 <e> prev = l.prev; l.item = null; l.prev = null; // GC 마지막 = 이전; if (prev == null) first = null; else prev.next = null; 크기--; 모드 카운트 ++; 리턴 요소; }4. 목록 인터페이스와 Deque 사이의 일관성을 유지하십시오
List Interface를 사용하면 첨자를 사용하여 컨테이너에 대한 임의의 액세스를 구현할 수 있으며 이와 같은 어레이에 대한 임의의 액세스를 쉽게 구현할 수 있습니다. 링크 된 목록의 경우 JDK는 링크 된 목록의 노드 수를 논리적으로 사용하여 무작위 액세스의 구현을 제공합니다.
노드 <e> 노드 (int index) {// index if (index <(size >> 1)) {node <e> x = first; for (int i = 0; i <index; i ++) x = x.next; 반환 x; } else {node <e> x = 마지막; for (int i = size-1; i> index; i-) x = x.prev; 반환 x; }} 인덱스는 상반기의 수입니다. 처음부터 검색합니다. 인덱스는 후반의 수에 속하며 끝까지 검색됩니다. 양방향 링크 된 목록의 특성을 최대한 활용하십시오.
따라서 (int index, t t), get (int), set (int) 및 기타 방법을 추가 할 수 있습니다.
LinkedList는 Deque 인터페이스를 구현합니다. 즉, LinkedList는 이중 엔드 큐 컨테이너 방법을 구현합니다. 다음은 API 요약입니다.
5. Linkedlist Traversal
LinkedList는 양방향 링크 목록이므로 자연스럽게 앞뒤로 이동할 수 있습니다. ArrayList와 마찬가지로 LinkedList는 멀티 스레딩 컨테이너 작동과 관련하여 빠른 문제가 발생합니다.
Fail-Fast의 문제는 이전 기사에서 설명되었으므로 여기서는 이야기하지 않을 것입니다.
반복자와 관련하여 LinkedList에는 ListIterator 양방향 반복자가 있으며 내림차순 반대 반복자가 있습니다. 모두 매우 간단합니다. 소스 코드는 분석되지 않습니다
요소를 가로 지르는 경우 무작위 액세스 비용이 비교적 높습니다.
3. Linkedlist, Arraylist, Vector Summary
1. Linkedlist 및 Arraylist
ArrayList는 동적 배열을 기반으로 한 데이터 구조를 구현하며 LinkedList는 링크 된 목록을 기반으로 한 데이터 구조를 기반으로합니다.
무작위로 액세스하기 위해 Get and Set의 경우 ArrayList는 LinkedList가 포인터를 이동하기 때문에 LinkedList보다 기분이 좋습니다.
새롭고 삭제 된 작업을 위해 Add and Remaint의 경우 LinedList는 ArrayList가 데이터를 이동해야하므로 더 나은 이점이 있습니다. 이것은 실제 상황에 따라 다릅니다. 단일 데이터 만 삽입 또는 삭제되면 Arraylist의 속도는 LinkedList의 속도보다 낫습니다. 그러나 데이터가 배치에 무작위로 삽입되면 LinkedList의 속도는 ArrayList보다 훨씬 낫습니다. ArrayList가 데이터를 삽입 할 때마다 삽입 지점과 모든 데이터를 이후로 이동해야합니다.
2. Arraylist 및 벡터
벡터는 스레드 동기이므로 스레드 안전이지만 ArrayList는 스레드 -asyn이므로 안전하지 않습니다. 스레드 안전 계수를 고려하지 않으면 ArrayList가 일반적으로 더 효율적입니다.
세트의 요소 수가 현재 세트 어레이의 길이보다 큰 경우 벡터 성장 속도는 현재 배열 길이의 100%이고 배열리스트 성장률은 현재 배열 길이의 50%입니다. 세트에서 비교적 많은 양의 데이터가있는 데이터를 사용하는 경우 벡터를 사용하면 특정 장점이 있습니다.
지정된 위치에서 데이터를 찾는 경우 벡터 및 ArrayList에서 사용하는 시간은 0 (1) 모두 동일하며 현재 벡터 및 ArrayList를 사용할 수 있습니다. 지정된 위치에서 데이터를 이동하는 데 소요 된 시간이 총 길이 인 0 (ni) n이면 지정된 위치에서 데이터를 이동하는 데 0 (1)이 걸리고 지정된 위치에서 데이터를 쿼리하는 데 소요 된 시간은 0 (i)입니다.
Arraylist 및 Vector는 배열을 사용하여 데이터를 저장합니다. 이 배열의 요소 수는 요소를 추가하고 삽입하기 위해 실제 저장된 데이터보다 큽니다. 둘 다 직접 일련 번호 인덱스 요소를 허용합니다. 그러나 배열 요소 및 기타 메모리 작업을 이동하도록 데이터를 삽입해야하므로 인덱스 데이터가 빠르고 느리게 데이터를 삽입해야합니다. 벡터는 동기화 된 방법 (스레드 안전)을 사용하므로 성능은 ArrayList보다 나쁩니다. LinkedList는 양방향 링크 목록을 사용하여 데이터를 저장합니다. 일련 번호에 따른 인덱싱 데이터는 전방 또는 뒤로 이동이 필요하지만 데이터를 삽입 할 때는이 항목의 전면 및 후면 항목 만 기록되므로 여러도를 삽입하는 것이 더 빠릅니다!