hoon's bLog

Java List Collection Class 자바 리스트 컬렉션 클래스 본문

IT/Java

Java List Collection Class 자바 리스트 컬렉션 클래스

개발한기발자 2023. 7. 28. 08:30
반응형


지난 컬렉션 프레임워크(Collection Frameworks) 포스팅에서(2년 지난건 비밀),

List, Set, Map을 너무 수박 겉핥기 식으로 알아본 것 같아,

이것보다는 좀 더 깊이 알아봐야겠다 싶어,

생각난 김에 기록하자는 취지로 포스팅을 소개해본다.

List 컬렉션 클래스란?

List 인터페이스를 구현한 모든 List 컬렉션 클래스는 다음과 같은 특징을 가진다.

  • 요소의 저장 순서가 유지
  • 같은 요소의 중복 저장을 허용

대표적인 List 컬렉션 클래스에 속하는 클래스는 다음과 같다.

  • ArrayList<E>
  • LinkedList<E>
  • Vector<E>
  • Stack<E>

좀더 구체적으로 위 클래스에 대해서 알아보자!

ArrayList<E> 클래스 특징

ArrayList 클래스는 가장 많이 사용되는 컬렉션 클래스 중 하나,

JDK 1.2부터 제공된 ArrayList 클래스는 내부적으로 배열을 이용하여 요소를 저장 / 삭제 / 접근한다.

 

ArrayList 클래스는 배열을 이용하기 때문에 인덱스를 이용해 배열 요소에 빠르게 접근할 수 있다.

하지만 배열은 크기를 변경할 수 없는 인스턴스이므로, 크기를 늘리기 위해서는 새로운 배열을 생성하고,

기존의 요소들을 옮겨야 하는 복잡한 과정을 거치게 된다.

물론 이 과정은 자동으로 수행되지만, 데이터가 많아질 경우,

요소의 추가 및 삭제 작업에 걸리는 시간이 매우 길어지는 단점도 존재한다.

ArrayList<E> 클래스 원리

ArrayList 인스턴스가 생성되면 기본값(그림에서는 10) 또는 사이즈(직접 지정)에 맞게 배열을 생성한다.

그리고 배열이 꽉차게 되면 새로운 배열을 만들어서 기존의 값을 Copy 하는 단순한 방식으로 이루어져 있다.

만약에 ArrayList의 데이터가 삭제될 경우 오른쪽 그림과 같은 작업이 발생 한다.

 

오른쪽 그림을 보면, data3이 삭제되면, 2번 index에 저장된 값이 비어있게 되므로,

2번 index 이후의 데이터를 앞으로 당겨야 한다.

데이터를 당기는 방법은 3번 index에 저장된 값부터 7번 index에 저장된 값을 내부적으로 Copy 후 2번 index 부터 덮어쓴다.

그리고 7번 index에 null  값을 저장한다.

(Copy 하여 저장하는 방식은 java code가 아닌 native code로 실행됨.)

 

그렇다면 문법과 예제를 통해 ArrayList 클래스를 사용해보자.

 

문법

import java.util.ArrayList;

ArrayList<Integer> arrList = new ArrayList<Integer>();

예제

// add() 메서드를 이용한 요소의 저장
arrList.add(40);
arrList.add(20);
arrList.add(30);
arrList.add(10);


// for 문과 get() 메서드를 이용한 접근 및 요소의 출력
for (int i = 0; i < arrList.size(); i++) {
    System.out.print(arrList.get(i) + " ");
    // 결과 : 40 20 30 10
}

// remove() 메서드를 이용한 요소의 제거
arrList.remove(1); //[40, 30, 10]

// Enhanced for문(forEach)과 get() 메서드를 이용한 요소의 출력
for (int e : arrList) {
    System.out.print(e + " ");
    // 결과 : 40 30 10
}

// Collections.sort() 메서드를 이용한 요소의 정렬
Collections.sort(arrList);

// iterator() 메서드와 get() 메서드를 이용한 요소의 출력
Iterator<Integer> iter = arrList.iterator();

while (iter.hasNext()) {
    System.out.print(iter.next() + " ");
    // 결과 : 10 30 40
}

// set() 메서드를 이용한 요소의 변경
arrList.set(0, 20);

for (int e : arrList) {
    System.out.print(e + " ");
    // 결과 : 20 10 30 40
}

// size() 메서드를 이용한 요소의 총 개수 출력
System.out.println("리스트의 크기 : " + arrList.size()); // 결과 : 4
  • 메서드를 통해 요소 삽입(add) / 삭제(remove) / 접근(get) 가능!
  • 컬렉션 클래스의 요소를 출력하는 방법에는 for 문enhanced for 문, iterator() 메서드를 이용한 방법 등 다양한 방법을 사용
  • 가능 자바의 Collections 클래스는 JDK 1.2부터 제공되는 컬렉션에서 동작하거나, 컬렉션을 반환하는 클래스 메서드(static method)만으로 구성된 클래스!!
  • 자바의 Collection은 인터페이스이며, Collections는 클래스임을 주의해야 한다!!!!!!

LinkedList<E> 클래스

LinkedList 클래스는 ArrayList 클래스가 배열을 이용하여 요소를 저장함으로써 발생하는 단점을 극복하기 위해 고안되었다.
JDK 1.2부터 제공된 LinkedList 클래스는 내부적으로 연결 리스트(linked list)를 이용하여 요소를 저장한다.


배열은 저장된 요소가 순차적으로 저장된다.
하지만 LinkedList저장된 요소가 비순차적으로 분포되며, 이러한 요소들 사이를 링크(link)로 연결하여 구성한다.
다음 요소를 가리키는 참조만을 가지는 연결 리스트를 단일 연결 리스트(singly linked list)라고 한다.

LinkedList<E> 클래스 원리

LinkedList는 Node에 의해 데이터들이 연결되어 있는 리스트로, 이름 그대로 연결 리스트라고도 한다.

LinkedList는 데이터를 배열에 저장하는 구조가 아니며, 그림처럼 Node라는 객체에 data를 저장한다.

Node는 Node끼리 참조값을 가지고 있는 이중 연결 리스트로 구성 되어 있다.

그림에서와 같이 Node next 변수는 다음 Node의 인스턴스 참조값을 저장하고 있다.

좌측 Node를 현재 Node라고 한다면, 우측 노드를 다음 Node라고 한다.

현재 Node에서 Node next 변수에 다음 Node 참조 값(next ref)만 저장하고 있기 때문에 다음 Node만 접근할 수 있다. 

위와 같이, Node1에서 Node2 접근은 가능하지만, Node3로 바로 접근할 수 없다.

Node3으로 접근하기 위해서는 반드시 Node2를 지나야 한다.

그래서 오른쪽 그림과 같이, data2를 삭제하면, Node1에서는 Node3의 참조값을, Node3에서는 Node1의 참조값을 저장한다.

LinkedList<E> 클래스 특징

LinkedListNode에서 참조값을 삭제하는 방식으로 데이터를 삭제한다.

그러므로 ArrayList보다 삭제시 지연시간이 짧다!

하지만, 특정 데이터를 찾으려면 최초 생성 Node부터 차례로 Node를 검색하기 때문에,

이런 경우에는 ArrayList의 index 방식보다 느릴 수 있다.

 

문법 

import java.util.List;
import java.util.ArrayList;

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

예제

LinkedList<String> lnkList = new LinkedList<String>();
 
// add() 메소드를 이용한 요소의 저장
lnkList.add("넷");
lnkList.add("둘");
lnkList.add("셋");
lnkList.add("하나");

// for 문과 get() 메소드를 이용한 요소의 출력
for (int i = 0; i < lnkList.size(); i++) {
    System.out.print(lnkList.get(i) + " ");
    // 결과: 넷 둘 셋 하나
}

// remove() 메소드를 이용한 요소의 제거
lnkList.remove(1); 

// Enhanced for 문과 get() 메소드를 이용한 요소의 출력
for (String e : lnkList) {
    System.out.print(e + " ");
    // 결과: 넷 셋 하나
}


// set() 메소드를 이용한 요소의 변경
lnkList.set(2, "둘");


for (String e : lnkList) {
    System.out.print(e + " ");
    // 결과: 넷 셋 둘
}

// size() 메소드를 이용한 요소의 총 개수
System.out.println("리스트의 크기 : " + lnkList.size()); // 결과: 3

위의 예제를 살펴보면 앞선 예제와 연결 리스트를 생성 코드만 다르고 전반적인 기능은 똑같다.
ArrayListLinkedList의 차이는 사용 방법이 아닌, 내부적으로 요소를 저장하는 방법 차이 뿐이다.

List 인터페이스 메서드

List 인터페이스는 Collection 인터페이스를 상속받으므로, Collection 인터페이스에서 정의한 메소드도 모두 사용할 수 있다.

아래 메서드들은 주로 많이 사용하는 메서드들이니 필요한 기능에 맞게 잘 사용하면 되겠다.

boolean add(E e) 해당 리스트(list)에 전달된 요소를 추가함. (선택적 기능)
void add(int index, E e) 해당 리스트의 특정 위치에 전달된 요소를 추가함. (선택적 기능)
void clear() 해당 리스트의 모든 요소를 제거함. (선택적 기능)
boolean contains(Object o) 해당 리스트가 전달된 객체를 포함하고 있는지를 확인함.
boolean equals(Object o) 해당 리스트와 전달된 객체가 같은지를 확인함.
E get(int index) 해당 리스트의 특정 위치에 존재하는 요소를 반환함.
boolean isEmpty() 해당 리스트가 비어있는지를 확인함.
Iterator<E> iterator() 해당 리스트의 반복자(iterator)를 반환함.
boolean remove(Object o) 해당 리스트에서 전달된 객체를 제거함. (선택적 기능)
boolean remove(int index) 해당 리스트의 특정 위치에 존재하는 요소를 제거함. (선택적 기능)
E set(int index, E e) 해당 리스트의 특정 위치에 존재하는 요소를 전달받은 객체로 대체함. (선택적 기능)
int size() 해당 리스트의 요소의 총 개수를 반환함.
Object[] toArray() 해당 리스트의 모든 요소를 Object 타입의 배열로 반환함.

Vector<E> / Stack<E> 클래스

제목이 통으로 묶였다는건?

그만큼 이 두개는 이제 더 이상 잘 사용되지 않는다!!!!!!! why?!?!?

 

Vector는 get()과 set()역할을 하는 모든 메서드에 synchronized 키워드가 붙어 있다.
따라서 멀티스레드 프로그래밍을 하는게 아니라면, 비슷한 역할을 하는 ArrayList를 사용하는게 좋다.

(ArrayList에는 synchronized 키워드가 없음!)

Stack은 자바에서 Deque를 구현하는 방법은 크게 LinkedListArrayDeque가 있다.

스택의 size가 엄청 커질 가능성이 있고,

size의 변동성이 매우 큰 경우 즉각적인 메모리 공간 확보를 위해선 LinkedList방식이 적절하며,

그렇지 않은 경우는 자체 메모리 소모량이 적고 iterate의 효율이 좋은 ArrayDeque를 사용하면 된다.

결국, Java의 Vector와 Stack은 멀티스레드 환경의 여부와 상관없이 대부분의 조건에서 성능 저하를 일으킨다.
Vector가 필요한 상황이라면 대신 ArrayList를 사용하는 것이 바람직하다.
마찬가지로 Stack 대신 Deque의 하위컬렉션을 상황에 맞게, 혹은 그냥 ArrayList를 사용하는 것이 적절하다.

 

이렇게 List Collection에 대해 알아봤는데,

대략적인 쓰임과 흐름을 파악하고 상황에 맞게 잘 쓰는게 좋겠다.

원론적인 개념이나 원리는 솔직히 잘 모르겠고 눈으로 직관적으로 확인할 수는 없으나,

프로그래밍을 하는데 있어 도움이 되는 날이 있을 수 있으니, '아 이런게 있구나', 하고 공부하고 넘어가면 되겠다.

언제나 새로운 정보 공유와 잘못된 정보

비판/지적/태클은 환영입니다!

끝.

 

Reference

자바의 정석 3rd Edition 내용 정리

https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html

 

ArrayList (Java Platform SE 8 )

Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is

docs.oracle.com

https://aahc.tistory.com/8

 

자바에서 Vector와 Stack 컬렉션이 쓰이지 않는 이유?

자바 컬렉션 프레임워크 Vector와 Stack은 왜 안쓰는가? C++ STL 중 Vector는 Stack과 다르게 random access가 가능하고, iterator 등 구성 원소에 접근이 용이한 여러 기능을 가지고 있어 널리 쓰인다.Vector로 Sta

aahc.tistory.com

 

728x90
반응형