자바 집합 시리즈 의 LinkedList 소스 분석

전편 에서 우 리 는 Array List 의 바 텀 실현 을 분석 한 결과 Array List 바 텀 은 배열 을 바탕 으로 이 루어 진 것 임 을 알 게 되 었 기 때문에 수정 이 빠 르 고 삽입 삭제 가 느 린 특징 이 있다.본 논문 에서 소개 한 LinkedList 는 List 인터페이스의 또 다른 실현 이다.그 밑 층 은 양 방향 링크 를 바탕 으로 이 루어 진 것 이기 때문에 삽입 삭제 가 빠 르 고 수정 이 느 린 특징 을 가지 고 있 으 며,양 방향 링크 에 대한 조작 을 통 해 대기 행렬 과 스 택 기능 도 실현 할 수 있다.링크 드 리스트 의 바 텀 구 조 는 다음 그림 과 같다.

F 는 두 결점 인용 을 나타 내 고 L 은 꼬리 결점 인용 을 나타 낸다.링크 의 모든 결점 은 세 가지 요소 가 있 는데 그것 이 바로 앞 접점 인용(P),결점 요소 의 값(E),뒤 접점 의 인용(N)이다.결점 은 내부 류 노드 에 의 해 표시 되 며,우 리 는 그것 의 내부 구 조 를 보 았 다.

//     
private static class Node<E> {
  E item;     //  
  Node<E> next;  //     
  Node<E> prev;  //     

  Node(Node<E> prev, E element, Node<E> next) {
    this.item = element;
    this.next = next;
    this.prev = prev;
  }
}

Node 라 는 내부 클래스 는 매우 간단 합 니 다.세 개의 구성원 변수 와 하나의 구조 기 만 있 습 니 다.item 은 노드 의 값 을 표시 하고 next 는 다음 노드 의 인용 입 니 다.prev 는 이전 노드 의 인용 으로 구조 기 를 통 해 이 세 개의 값 을 전달 합 니 다.다음은 링크 드 리스트 의 구성원 변수 와 구조 기 를 살 펴 보 자.

//      
transient int size = 0;

//     
transient Node<E> first;

//     
transient Node<E> last;

//     
public LinkedList() {}

//          
public LinkedList(Collection<? extends E> c) {
  this();
  addAll(c);
}
LinkedList 는 두 노드 의 인용 과 끝 점 의 인용 을 가지 고 있 습 니 다.하 나 는 무 참 구조 기 이 고 하 나 는 외부 집합 에 들 어 가 는 구조 기 입 니 다.ArrayList 와 달리 LinkedList 는 초기 크기 의 구조 기 를 지정 하지 않 았 습 니 다.그것 의 첨삭 검사 방법 을 좀 봐 라.

// (  )
public boolean add(E e) {
  //       
  linkLast(e);
  return true;
}

// (  )
public void add(int index, E element) {
  checkPositionIndex(index);
  if (index == size) {
    //       
    linkLast(element);
  } else {
    //       
    linkBefore(element, node(index));
  }
}

// (    )
public E remove(int index) {
  //        
  checkElementIndex(index);
  return unlink(node(index));
}

// (    )
public boolean remove(Object o) {
  if (o == null) {
    for (Node<E> x = first; x != null; x = x.next) {
      if (x.item == null) {
        unlink(x);
        return true;
      }
    }
  } else {
    //    
    for (Node<E> x = first; x != null; x = x.next) {
      if (o.equals(x.item)) {
        //      
        unlink(x);
        return true;
      }
    }
  }
  return false;
}

// 
public E set(int index, E element) {
  //        
  checkElementIndex(index);
  //           
  Node<E> x = node(index);
  //          
  E oldVal = x.item;
  //           
  x.item = element;
  //      
  return oldVal;
}

// 
public E get(int index) {
  //        
  checkElementIndex(index);
  //           
  return node(index).item;
}
링크 드 List 의 요 소 를 추가 하 는 방법 은 주로 링크 Last 와 링크 Before 두 가지 방법 을 호출 하 는 것 입 니 다.링크 Last 방법 은 링크 뒤에 요 소 를 연결 하 는 것 입 니 다.링크 Before 방법 은 링크 중간 에 요 소 를 삽입 하 는 것 입 니 다.링크 목록 의 삭제 방법 은 unlink 방법 을 호출 하여 링크 에서 요 소 를 제거 합 니 다.다음은 링크 의 삽입 과 삭제 작업 의 핵심 코드 를 살 펴 보 겠 습 니 다.

//         
void linkBefore(E e, Node<E> succ) {
  //              
  final Node<E> pred = succ.prev;
  //     ,                        
  //                   
  final Node<E> newNode = new Node<>(pred, e, succ);
  //                  
  succ.prev = newNode;
  //              ,           
  if (pred == null) {
    //           
    first = newNode;
  } else {
    //  ,                         
    pred.next = newNode;
  }
  //        
  size++;
  //      
  modCount++;
}

//      
E unlink(Node<E> x) {
  //         
  final E element = x.item;
  //               
  final Node<E> next = x.next;
  //               
  final Node<E> prev = x.prev;

  //              ,           
  if (prev == null) {
    //                  
    first = next;
  } else {
    //                        
    prev.next = next;
    //             
    x.prev = null;
  }

  //              ,           
  if (next == null) {
    //                  
    last = prev;
  } else {
    //                        
    next.prev = prev;
    x.next = null;
  }

  //          
  x.item = null;
  //        
  size--;
  //      
  modCount++;
  return element;
}
linkBefore 와 unlink 는 대표 적 인 링크 노드 와 마 운 트 해제 지점 의 작업 입 니 다.다른 링크 와 마 운 트 해제 양쪽 노드 의 방법 은 이와 유사 하기 때문에 linkBefore 와 unlink 방법 을 중점적으로 소개 합 니 다.
linkBefore 방법의 과정 도:

unlink 방법의 과정 도:

위의 그림 을 통 해 알 수 있 듯 이 링크 의 삽입 과 삭제 작업 의 시간 복잡 도 는 모두 O(1)이 고 링크 에 대한 검색 과 수정 작업 은 모두 링크 를 옮 겨 다 니 며 요소 의 포 지 셔 닝 을 해 야 합 니 다.이 두 작업 은 모두 호출 된 node(int index)방법 으로 요 소 를 포 지 셔 닝 하고 아래 표 시 를 통 해 요 소 를 어떻게 포 지 셔 닝 하 는 지 보 세 요.

//          
Node<E> node(int 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;
  }
}

아래 표 시 를 통 해 포 지 셔 닝 을 할 때 먼저 링크 의 윗부분 인지 아 랫 부분 인지 판단 하고 윗부분 이 라면 처음부터 찾 으 며 아 랫 부분 이 라면 끝부분 부터 찾 으 므 로 아래 표 시 를 통 해 찾 고 수정 하 는 작업 의 시간 복잡 도 는 O(n/2)이다.양 방향 링크 에 대한 조작 을 통 해 단일 대기 열,양 방향 대기 열 과 스 택 의 기능 도 실현 할 수 있다.
단 방향 대기 열 동작:

//     
public E peek() {
  final Node<E> f = first;
  return (f == null) ? null : f.item;
}

//     
public E element() {
  return getFirst();
}

//     
public E poll() {
  final Node<E> f = first;
  return (f == null) ? null : unlinkFirst(f);
}

//     
public E remove() {
  return removeFirst();
}

//         
public boolean offer(E e) {
  return add(e);
}

양 방향 대기 열 동작:

//     
public boolean offerFirst(E e) {
  addFirst(e);
  return true;
}

//     
public boolean offerLast(E e) {
  addLast(e);
  return true;
}

//     
public E peekFirst() {
  final Node<E> f = first;
  return (f == null) ? null : f.item;
 }

//     
public E peekLast() {
  final Node<E> l = last;
  return (l == null) ? null : l.item;
}
스 택 작업:

//  
public void push(E e) {
  addFirst(e);
}

//  
public E pop() {
  return removeFirst();
}

단 방향 대기 열 이 든 양 방향 대기 열 이 든 스 택 이 든 모두 링크 의 머리 결산 점 과 끝 점 을 조작 하 는 것 입 니 다.그들의 실현 은 모두 addFirst(),addLast(),removeFirst(),removeLast()등 네 가지 방법 을 바탕 으로 합 니 다.그들의 조작 은 linkBefore()와 unlink()와 유사 합 니 다.하 나 는 링크 양 끝 을 조작 하 는 것 이 고 하 나 는 링크 중간 작업 입 니 다.이 네 가지 방법 은 모두 linkBefore()와 unlink()방법의 특수 한 상황 이 라 고 할 수 있 으 므 로 내부 실현 을 이해 하기 어렵 지 않 고 많이 소개 하지 않 습 니 다.여기까지 우 리 는 LinkedList 에 대한 분석 도 곧 끝 날 것 이 고 전문 중의 중점 에 대해 정리 할 것 이다.
1.LinkedList 는 양 방향 링크 를 바탕 으로 이 루어 진 것 으로 삭제 검사 방법 이 든 대기 열 과 스 택 의 실현 이 든 모두 조작 노드 를 통 해 이 루어 질 수 있다.
2.LinkedList 는 미리 용량 을 지정 할 필요 가 없습니다.링크 작업 을 바탕 으로 집합 하 는 용량 은 요소 의 가입 에 따라 자동 으로 증가 하기 때 문 입 니 다.
3.LinkedList 요 소 를 삭제 한 후 집합 하여 사용 하 는 메모리 가 자동 으로 축소 되 며,ArrayList 처럼 trimToSize()방법 을 호출 할 필요 가 없습니다.
4.LinkedList 의 모든 방법 이 동기 화 되 지 않 았 기 때문에 스 레 드 가 안전 한 것 도 아니 므 로 다 중 스 레 드 환경 에서 사용 하지 않도록 해 야 합 니 다.
5.상기 분석 은 JDK 1.7 을 바탕 으로 다른 버 전이 약간 다 를 수 있 기 때문에 일률적으로 논 할 수 없다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기