[Java 기초] 컬렉션 프레임워크 - List
자바에서는 컬렉션 프레임워크를 이용해서 다수의 데이터를 다루는 풍부한 클래스를 제공한다.
그럼 컬렉션 프레임워크가 무엇인지 한번 알아보고 예시를 통해서 개념을 정리해보자
1. 컬렉션 프레임워크란
컬렉션 프레임워크에는 크게 3가지의 타입이 존재한다.

List, Set, Map 3가지의 타입이 존재하고 List와 Set의 공통 부분을 뽑아 Collection이라는 새로운 인터페이스를 정의했다.
인터페이스 | 특징 |
List | 순서가 존재하는 데이터의 집합 구현 클래스 : ArrayList, LinkedList, Stack, Vector 등 |
Set | 순서를 유지하지 않는 데이터의 집합, 중복 허용 ❌ 구현 클래스 : HashSet, TreeSet 등 |
Map | Key & Value 값을 쌍으로 이루어진 데이터의 집합 순서는 유지되지 않으며 Key는 중복 허용 ❌ 구현클래스 : HashMap, TreeMap, Hashtable, Properties 등 |
실제 아직 자바에서는 Vector 클래스나 Stack 클래스가 사라지지 않고 존재하지만 이 클래스들은 컬렉션 이전에 존재하던 클래스로 컬렉션 프레임워크 명명법을 따르지는 않는다.

List,Set의 조상인 Collection 인터페이스는 다음과 같은 메서드가 정의되어 있다.
이는 List,Set의 모든 상속 클래스들에서 사용 가능한 공통메서드 이기 때문에 반드시 기억하자
실제 Java API 에서는 E라고 되어있다 이는 제네릭 표기법인데 이해를 돕기위해 object라고 설명 자세한 사항은 제네릭 포스팅을 참고하자
자 이제 Collection의 기본 메서드도 알아보았고 각 자식 클래스인 List,Set과 Map의 각 상속 클래스들을 한번 알아보자
2. List 인터페이스


리스트 인터페이스의 메서드는 정의한 표를 확인하고 자주 쓰는 메서드는 반드시 기억하자
이제 상속된 컬렉션 클래스들을 알아보자
◼ ArrayList
가장 흔하게 사용 가능한 클래스이다 저장순서가 유지되고 중복을 허용하는 특성을 가지고 있다.
Array와 굉장히 비슷하다. 비슷할 수 밖에 없는게 Object 배열을 사용해서 데이터를 순차 저장하기 때문이다.
순차적으로 데이터를 추가하고 공간이 없으면 새로운 배열을 생성해서 기존내용을 새로운 배열로 복사하고 추가된 값을 저장하는 방식이다.
ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
@java.io.Serial
private static final long serialVersionUID = 8683452581122892189L;
transient Object[] elementData; // non-private to simplify nested class access
실제 자바 ArrayList 클래스를 확인하면 Object 배열이 선언되어 있다.
ArrayList 인스턴스를 생성할때 타입을 명시하지 않아도 선언은 가능하다. 하지만 자바 타입을 명시하지 않고 선언하면 Warning 경고를 제공한다.
이는 실제 개발 업무를 수행할때 타입이 다른 데이터를 하나의 컬렉션 프레임워크에 저장하는 방식을 사용하지 않아 그런것으로 보인다.
기본적인 메서드의 사용은 API 문서를 참고하면 충분히 가능하다.
그럼 만약 변수를 삭제하는 과정을 어떤식으로 진행해야 할까 이를 한번 알아보자
for(int i=list2.size()-1; i>=0; i--){
if(list1.contains(list2.get(i))){
list2.remove(i);
}
}
이렇게 list1과 list2의 공통 부분을 삭제하는 코드가 있다고 생각해보자
왜 처음부터 삭제하지 않고 list1을 역순으로 순회하면서 삭제하는 것일까
만약 i를 0부터 증가시키면 삭제시에 빈 공간을 채우기 위해서 나머지 요소가 자리 이동을 수행한다.
그림을 통해서 한번 알아보자
어떤 ArrayList의 삭제연산을 수행한다.

삭제할 데이터 아래의 데이터를 한칸씩 위로 복사해서 삭제할 데이터에 덮어쓴다.
데이터는 모두 한칸씩 이동했으니 마지막 값을 null로 바꾼다.
그리고 크기를 1 감소시킨다.
이런식으로 동작한다. 즉 이 복사 이동 마지막 삭제 과정을 계속해서 반복하기 때문에 마지막 부터 삭제를 하면 배열 복사 과정이 없어 불필요한 메모리 복사 과정이 사라지는 것이다.
👍장점
배열은 구조가 간단하고 데이터를 읽는데 걸리는 시간이 짧다.
👎단점
크기 변경이 안된다.
크기를 변경하기 위해서 새로운 배열을 생성하고 데이터를 복사해야하며 지나치게 Capacity가 크게 하면 메모리 낭비 발생
데이터를 삭제하기 위해서 다른 데이터를 옭겨야하는 문제가 있다.
삭제 삽입 연산에서 불필요한 작업을 수행해야하는 문제
◼ LinkedList
배열의 단점을 보완한 클래스이다.
배열과 다르게 링크드 리스트는 불연속적으로 존재하는 데이터를 연결하는 구조이다.


만약 이 구조에서 삭제 2번 인덱스의 삭제가 일어나면 1번 참조를 3번이랑 연결시키면 복사없이 바로 삭제 가능
새로운 데이터를 추가할때도 1번인덱스에 새로운게 추가된다고 하면 0번의 다음 참조를 새로운 1번으로 새로운 입력값의 다음 참조를 기존의 1번으로 바꿔주면 된다.
링크드 리스트는 단방향뿐만 아니라 2가지의 종류가 추가로 존재한다.
그림으로 한번 이해해보자

더블리 링크드 리스트로 불리며 단방향이 아니기때문에 접근성이 더욱 향상된다.

더블리 서큘러 링크드리스트로 처음과 끝의 순환 구조로 이어져 있다.
마지막의 다음을 처음으로 이어주고 처음의 이전의 마지막으로 이어주는 구조이다.
쉽게 TV 채널을 생각하자 TV 채널의 마지막 채널에서 다음으로 하면 처음 채널로 이어가는 그런 구조이다.
접근 성능 등을 고려해서 ArrayList와 LinkedList 중 클래슬르 적절히 선택하는 것이 필요하다.
◼ Stack/Queue
스택과 큐를 알아보자
스택과 큐는 서로 상극의 특징을 가지는 자료구조이다.
스택은 LIFO, 큐는 FIFO의 구조를 가진다.
LIFO 는 Last In First Out 이라는 구조이다. 가장 마지막에 들어온 값이 가장 처음으로 나가는 구조이다.

이 그림 처럼 한쪽 막힌 출입구 모양이라고 생각하면 된다.
주로 수식 계산이나 수식괄효, 뒤로가기 같은 작업에 사용된다.
반대로 FIFO 는 First In Frist Out 이라는 의미이다.

위 그림과 같이 생겼다. 앞뒤가 모두 뚫린 구조로 , 최근에 사용한 문저나 ,작업 대기, 버퍼 등에 사용된다.
특히 큐는 변형 구조가 몇개 존재하는데 대표적인 예시가 덱, 우선순위 큐, 블락킹 큐 3가지가 있다.
이 중 특히 우선순위 큐는 힙으로 이어지는 문제로 코딩테스트에서 자주 빈출되는 유형중 하나라 따로 포스팅을 할 예정이다.
3. Iterator, ListIterator
컬렉션에 저장된 데이터를 접근하는데 사용되는 인터페이스이다.
Enumberation이 있지만 이는 Iterator의 구버전으로 따로 설명을 하지 않겠다.

ListIterator는 접근성을 향상한 버전으로 단방향 접근에서 양방향 접근으로 바꾼것이다.
Interator를 사용하면 Collection의 List, Set 인터페이스에 기반한 클래스들을 모두 동일한 방식으로 요소를 읽어오는것이 가능하다.
Map의 경우에는 entrySet 메서드로 Set 형태로 만들어서 사용가능

한쪽으로 이동하는 next 뿐만 아니라 previous 와 같이 이전 이동 메서드도 존재한다.
4. 기타 메서드
◾ toString
배열을 요소를 한번에 출력해주는 메서드
Arrays.toString(배열) 을 통해서 사용 가능
◾ 다차원 배열 비교/출력
메서드 이름 | 기능 |
deepEquals() | 깊은 비교로 다차워 배열이 서로 동일한지 비교해준다. 그냥 equals로 하면 false밖에 안나온다 |
deepToString(); | 다차원 배열의 요소를 출력해주는 함수 |
◾ 배열 복사 / 채우기
메서드 이름 | 기능 |
copyOf(arr, idx) | 배열의 시작부터 idx이전까지의 값을 복사 |
copyOfRange(arr,start,end) | strat부터 end-1 까지의 값을 복사 |
fill(arr, value) | value로 모든 값 채우기 |
setAll(arr, 람다식) | 람다식을 통한 값으로 모든 array 채우기 |
◾ 배열 리스트 변환 / 정렬 / 검색
메서드 이름 | 기능 |
asList(Obejct a) | 배열을 리스트로 변환(대신 크기 변환 불가) ✅ 크기 변환을 위해서는 ArrayList로 생성 필요 |
sort() | 오름 차순 정렬 |
binarySearch(arr, value) | 이진탐색을 통해서 value이 가장 낮은 인덱스 반환 |
'Java' 카테고리의 다른 글
[Java 기초] Java 기본 구조 및 실행 과정 (1) | 2024.04.18 |
---|---|
Java 기초 - 제네릭(Generic)이란 (0) | 2024.01.16 |
[Java 기초] 컬렉션 프레임워크 - List
자바에서는 컬렉션 프레임워크를 이용해서 다수의 데이터를 다루는 풍부한 클래스를 제공한다.
그럼 컬렉션 프레임워크가 무엇인지 한번 알아보고 예시를 통해서 개념을 정리해보자
1. 컬렉션 프레임워크란
컬렉션 프레임워크에는 크게 3가지의 타입이 존재한다.

List, Set, Map 3가지의 타입이 존재하고 List와 Set의 공통 부분을 뽑아 Collection이라는 새로운 인터페이스를 정의했다.
인터페이스 | 특징 |
List | 순서가 존재하는 데이터의 집합 구현 클래스 : ArrayList, LinkedList, Stack, Vector 등 |
Set | 순서를 유지하지 않는 데이터의 집합, 중복 허용 ❌ 구현 클래스 : HashSet, TreeSet 등 |
Map | Key & Value 값을 쌍으로 이루어진 데이터의 집합 순서는 유지되지 않으며 Key는 중복 허용 ❌ 구현클래스 : HashMap, TreeMap, Hashtable, Properties 등 |
실제 아직 자바에서는 Vector 클래스나 Stack 클래스가 사라지지 않고 존재하지만 이 클래스들은 컬렉션 이전에 존재하던 클래스로 컬렉션 프레임워크 명명법을 따르지는 않는다.

List,Set의 조상인 Collection 인터페이스는 다음과 같은 메서드가 정의되어 있다.
이는 List,Set의 모든 상속 클래스들에서 사용 가능한 공통메서드 이기 때문에 반드시 기억하자
실제 Java API 에서는 E라고 되어있다 이는 제네릭 표기법인데 이해를 돕기위해 object라고 설명 자세한 사항은 제네릭 포스팅을 참고하자
자 이제 Collection의 기본 메서드도 알아보았고 각 자식 클래스인 List,Set과 Map의 각 상속 클래스들을 한번 알아보자
2. List 인터페이스


리스트 인터페이스의 메서드는 정의한 표를 확인하고 자주 쓰는 메서드는 반드시 기억하자
이제 상속된 컬렉션 클래스들을 알아보자
◼ ArrayList
가장 흔하게 사용 가능한 클래스이다 저장순서가 유지되고 중복을 허용하는 특성을 가지고 있다.
Array와 굉장히 비슷하다. 비슷할 수 밖에 없는게 Object 배열을 사용해서 데이터를 순차 저장하기 때문이다.
순차적으로 데이터를 추가하고 공간이 없으면 새로운 배열을 생성해서 기존내용을 새로운 배열로 복사하고 추가된 값을 저장하는 방식이다.
ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
@java.io.Serial
private static final long serialVersionUID = 8683452581122892189L;
transient Object[] elementData; // non-private to simplify nested class access
실제 자바 ArrayList 클래스를 확인하면 Object 배열이 선언되어 있다.
ArrayList 인스턴스를 생성할때 타입을 명시하지 않아도 선언은 가능하다. 하지만 자바 타입을 명시하지 않고 선언하면 Warning 경고를 제공한다.
이는 실제 개발 업무를 수행할때 타입이 다른 데이터를 하나의 컬렉션 프레임워크에 저장하는 방식을 사용하지 않아 그런것으로 보인다.
기본적인 메서드의 사용은 API 문서를 참고하면 충분히 가능하다.
그럼 만약 변수를 삭제하는 과정을 어떤식으로 진행해야 할까 이를 한번 알아보자
for(int i=list2.size()-1; i>=0; i--){
if(list1.contains(list2.get(i))){
list2.remove(i);
}
}
이렇게 list1과 list2의 공통 부분을 삭제하는 코드가 있다고 생각해보자
왜 처음부터 삭제하지 않고 list1을 역순으로 순회하면서 삭제하는 것일까
만약 i를 0부터 증가시키면 삭제시에 빈 공간을 채우기 위해서 나머지 요소가 자리 이동을 수행한다.
그림을 통해서 한번 알아보자
어떤 ArrayList의 삭제연산을 수행한다.

삭제할 데이터 아래의 데이터를 한칸씩 위로 복사해서 삭제할 데이터에 덮어쓴다.
데이터는 모두 한칸씩 이동했으니 마지막 값을 null로 바꾼다.
그리고 크기를 1 감소시킨다.
이런식으로 동작한다. 즉 이 복사 이동 마지막 삭제 과정을 계속해서 반복하기 때문에 마지막 부터 삭제를 하면 배열 복사 과정이 없어 불필요한 메모리 복사 과정이 사라지는 것이다.
👍장점
배열은 구조가 간단하고 데이터를 읽는데 걸리는 시간이 짧다.
👎단점
크기 변경이 안된다.
크기를 변경하기 위해서 새로운 배열을 생성하고 데이터를 복사해야하며 지나치게 Capacity가 크게 하면 메모리 낭비 발생
데이터를 삭제하기 위해서 다른 데이터를 옭겨야하는 문제가 있다.
삭제 삽입 연산에서 불필요한 작업을 수행해야하는 문제
◼ LinkedList
배열의 단점을 보완한 클래스이다.
배열과 다르게 링크드 리스트는 불연속적으로 존재하는 데이터를 연결하는 구조이다.


만약 이 구조에서 삭제 2번 인덱스의 삭제가 일어나면 1번 참조를 3번이랑 연결시키면 복사없이 바로 삭제 가능
새로운 데이터를 추가할때도 1번인덱스에 새로운게 추가된다고 하면 0번의 다음 참조를 새로운 1번으로 새로운 입력값의 다음 참조를 기존의 1번으로 바꿔주면 된다.
링크드 리스트는 단방향뿐만 아니라 2가지의 종류가 추가로 존재한다.
그림으로 한번 이해해보자

더블리 링크드 리스트로 불리며 단방향이 아니기때문에 접근성이 더욱 향상된다.

더블리 서큘러 링크드리스트로 처음과 끝의 순환 구조로 이어져 있다.
마지막의 다음을 처음으로 이어주고 처음의 이전의 마지막으로 이어주는 구조이다.
쉽게 TV 채널을 생각하자 TV 채널의 마지막 채널에서 다음으로 하면 처음 채널로 이어가는 그런 구조이다.
접근 성능 등을 고려해서 ArrayList와 LinkedList 중 클래슬르 적절히 선택하는 것이 필요하다.
◼ Stack/Queue
스택과 큐를 알아보자
스택과 큐는 서로 상극의 특징을 가지는 자료구조이다.
스택은 LIFO, 큐는 FIFO의 구조를 가진다.
LIFO 는 Last In First Out 이라는 구조이다. 가장 마지막에 들어온 값이 가장 처음으로 나가는 구조이다.

이 그림 처럼 한쪽 막힌 출입구 모양이라고 생각하면 된다.
주로 수식 계산이나 수식괄효, 뒤로가기 같은 작업에 사용된다.
반대로 FIFO 는 First In Frist Out 이라는 의미이다.

위 그림과 같이 생겼다. 앞뒤가 모두 뚫린 구조로 , 최근에 사용한 문저나 ,작업 대기, 버퍼 등에 사용된다.
특히 큐는 변형 구조가 몇개 존재하는데 대표적인 예시가 덱, 우선순위 큐, 블락킹 큐 3가지가 있다.
이 중 특히 우선순위 큐는 힙으로 이어지는 문제로 코딩테스트에서 자주 빈출되는 유형중 하나라 따로 포스팅을 할 예정이다.
3. Iterator, ListIterator
컬렉션에 저장된 데이터를 접근하는데 사용되는 인터페이스이다.
Enumberation이 있지만 이는 Iterator의 구버전으로 따로 설명을 하지 않겠다.

ListIterator는 접근성을 향상한 버전으로 단방향 접근에서 양방향 접근으로 바꾼것이다.
Interator를 사용하면 Collection의 List, Set 인터페이스에 기반한 클래스들을 모두 동일한 방식으로 요소를 읽어오는것이 가능하다.
Map의 경우에는 entrySet 메서드로 Set 형태로 만들어서 사용가능

한쪽으로 이동하는 next 뿐만 아니라 previous 와 같이 이전 이동 메서드도 존재한다.
4. 기타 메서드
◾ toString
배열을 요소를 한번에 출력해주는 메서드
Arrays.toString(배열) 을 통해서 사용 가능
◾ 다차원 배열 비교/출력
메서드 이름 | 기능 |
deepEquals() | 깊은 비교로 다차워 배열이 서로 동일한지 비교해준다. 그냥 equals로 하면 false밖에 안나온다 |
deepToString(); | 다차원 배열의 요소를 출력해주는 함수 |
◾ 배열 복사 / 채우기
메서드 이름 | 기능 |
copyOf(arr, idx) | 배열의 시작부터 idx이전까지의 값을 복사 |
copyOfRange(arr,start,end) | strat부터 end-1 까지의 값을 복사 |
fill(arr, value) | value로 모든 값 채우기 |
setAll(arr, 람다식) | 람다식을 통한 값으로 모든 array 채우기 |
◾ 배열 리스트 변환 / 정렬 / 검색
메서드 이름 | 기능 |
asList(Obejct a) | 배열을 리스트로 변환(대신 크기 변환 불가) ✅ 크기 변환을 위해서는 ArrayList로 생성 필요 |
sort() | 오름 차순 정렬 |
binarySearch(arr, value) | 이진탐색을 통해서 value이 가장 낮은 인덱스 반환 |
'Java' 카테고리의 다른 글
[Java 기초] Java 기본 구조 및 실행 과정 (1) | 2024.04.18 |
---|---|
Java 기초 - 제네릭(Generic)이란 (0) | 2024.01.16 |