프로그래밍 공부
자바 컬렉션 - List 인터페이스 본문
리스트
- 자바의 리스트(List)는 컬렉션 프레임워크의 한 부분으로, 순서가 있는 요소의 집합을 저장하는 인터페이스.
- 리스트는 중복된 요소를 허용하며, 요소의 삽입 순서가 유지됨.
- 자바에서 리스트를 구현하는 주요 클래스는
ArrayList
,LinkedList
,Vector
각각의 리스트 클래스는 특정한 사용 사례에 따라 장단점이 있음.
1. List 인터페이스
List
인터페이스는 순서가 있는 요소의 목록을 정의하며, 다음과 같은 주요 메서드를 제공함:
add(E e)
: 리스트의 끝에 요소를 추가.add(int index, E element)
: 지정된 위치에 요소를 추가.get(int index)
: 특정 인덱스에 있는 요소를 반환.remove(int index)
: 특정 인덱스의 요소를 제거.set(int index, E element)
: 지정된 인덱스의 요소를 새 요소로 교체.size()
: 리스트의 요소 개수를 반환.isEmpty()
: 리스트가 비어 있는지 확인.contains(Object o)
: 리스트에 특정 요소가 포함되어 있는지 확인.indexOf(Object o)
: 특정 요소의 인덱스를 찾음. 요소가 없으면 -1 출력clear()
: 리스트의 모든 요소를 제거.iterator()
: 리스트의 요소를 반복할 수 있는 반복자를 반환.of()
: 리스트 인터페이스의 구현체를 반환, 생성된 리스트는 불변(immutable)으로, 요소를 수정할 수 없음
2. 주요 리스트 구현 클래스
1. ArrayList
- 구조: 동적 배열을 기반으로 하며, 요소의 추가 및 검색이 빠름.
- 장점:
- 인덱스를 통한 접근이 O(1)로 빠름.
- 메모리 사용이 효율적.
- 단점:
- 요소를 중간에 삽입하거나 삭제할 때, 나머지 요소를 이동시켜야 하므로 O(n)의 시간이 소요.
예시:
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("Apple");
arrayList.add("Banana");
String fruit = arrayList.get(0); // "Apple"
2. LinkedList
- 구조: 이중 연결 리스트를 기반으로 하며, 각 요소가 이전과 다음 요소를 참조.
- 장점:
- 요소의 삽입 및 삭제가 O(1)로 빠름 (특히 리스트의 앞이나 뒤에서).
- 메모리 조정이 용이.
- 단점:
- 인덱스를 통한 접근이 O(n)으로 느림.
- 각 요소가 추가적인 포인터를 가지고 있으므로 메모리 사용이 더 많음.
예시:
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("Apple");
linkedList.add("Banana");
String fruit = linkedList.get(1); // "Banana"
3. Vector
- 구조: 동적 배열을 기반으로 하며,
ArrayList
와 유사하지만, 스레드 안전(Thread-safe)함. - 장점:
- 스레드 안전성을 제공.
- 동기화된 접근이 필요할 때 유용.
- 단점:
- 성능이 떨어질 수 있으며, 주로 스레드 안전성이 필요하지 않은 경우에는
ArrayList
를 사용하는 것이 좋음.
- 성능이 떨어질 수 있으며, 주로 스레드 안전성이 필요하지 않은 경우에는
예시:
Vector<String> vector = new Vector<>();
vector.add("Apple");
vector.add("Banana");
String fruit = vector.get(0); // "Apple"
3. 리스트의 주요 특징
- 중복 허용: 리스트는 동일한 요소를 여러 번 추가할 수 있음.
- 순서 유지: 요소가 추가된 순서가 유지됨.
- 가변 크기: 리스트는 동적으로 크기를 조절할 수 있어, 요소를 추가하거나 제거할 때 유연함.
4. 사용 예
- 리스트는 다양한 상황에서 사용할 수 있음.
- 예를 들어, 학생의 이름 목록, 장바구니의 상품 목록 등 순서가 중요한 데이터를 저장할 때 유용함.
리스트 예제:
import java.util.ArrayList;
import java.util.List;
public class ListExample {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
// 리스트 출력
for (String fruit : fruits) {
System.out.println(fruit);
}
// 특정 인덱스의 요소 제거
fruits.remove(1); // "Banana" 제거
// 리스트의 크기 출력
System.out.println("리스트 크기: " + fruits.size());
}
}
결론
- 자바 리스트는 다양한 데이터 구조를 제공하여, 요소를 순서대로 저장하고 관리하는 데 매우 유용함.
- 각 리스트의 특징을 이해하고, 필요에 따라 적절한 리스트 구현을 선택하면 효율적인 프로그래밍이 가능
- ArrayList, Vector와 LinkedList
- 데이터 구조
- ArrayList: 내부적으로 동적 배열을 사용하여 요소를 저장.
배열의 크기가 가득 차면 새로운 배열을 만들어 기존 요소를 복사. - LinkedList: 이중 연결 리스트를 사용하여 각 요소가 이전 요소와 다음 요소를 참조
각 요소는 노드(Node)로 표현.
- ArrayList: 내부적으로 동적 배열을 사용하여 요소를 저장.
- 성능
- ArrayList
- 접근 시간: 요소에 인덱스를 통해 O(1)로 빠르게 접근할 수 있음.
- 삽입/삭제 시간: 리스트의 중간에 요소를 삽입하거나 삭제할 경우 O(n).
요소를 이동해야 하므로 성능이 저하됨.
- LinkedList
- 접근 시간: 특정 인덱스의 요소에 접근할 때 O(n).
리스트의 처음부터 순회해야 하기 때문. - 삽입/삭제 시간: 리스트의 앞이나 뒤에서 요소를 삽입하거나 삭제할 경우 O(1)로 빠름.
중간에서 삽입/삭제할 경우 O(n)임.
- 접근 시간: 특정 인덱스의 요소에 접근할 때 O(n).
- ArrayList
- 메모리 사용
- ArrayList: 동적 배열이기 때문에 요소를 저장하는 데 필요한 메모리 외에 추가적인 메모리가
필요하지 않음. 그러나 초기 용량을 설정할 수 있어, 필요할 때 크기를 늘림. - LinkedList: 각 요소가 두 개의 포인터(이전 노드와 다음 노드)를 가지고 있어
메모리 사용량이 더 많음. 따라서, 요소가 많을 경우 메모리 오버헤드가 발생할 수 있음.
- ArrayList: 동적 배열이기 때문에 요소를 저장하는 데 필요한 메모리 외에 추가적인 메모리가
- 사용 용도
- ArrayList: 요소의 인덱스를 통한 빠른 접근이 필요한 경우
또는 리스트의 변경이 적을 때 적합 예를 들어, 데이터 조회가 많은 경우에 유리함. - LinkedList: 요소의 삽입과 삭제가 빈번하게 발생하는 경우에 적합.
특히, 리스트의 앞이나 뒤에서 작업할 때 성능이 좋음.
- ArrayList: 요소의 인덱스를 통한 빠른 접근이 필요한 경우
- 데이터 구조
- ArrayList와 Vector
- 동기화
- ArrayList: 기본적으로 동기화되지 않음. 즉, 여러 스레드가 동시에 접근할 때
안전하지 않으므로, 멀티스레드 환경에서 사용할 경우 별도의 동기화 처리가 필요. - Vector: 기본적으로 모든 메서드가 동기화. 따라서 멀티스레드 환경에서 안전하지만
성능이 저하될 수 있음.
- ArrayList: 기본적으로 동기화되지 않음. 즉, 여러 스레드가 동시에 접근할 때
- 성능
- ArrayList: 동기화가 없기 때문에 일반적으로
Vector
보다 성능이 빠름.
스레드 안전성이 필요하지 않은 경우에 사용하기 적합. - Vector: 동기화로 인해 성능이 떨어질 수 있음. 그러나
Vector
는 초기 용량을 설정할 수 있으며, 요소가 추가될 때마다 자동으로 크기를 조정.
- ArrayList: 동기화가 없기 때문에 일반적으로
- 크기 조정
- ArrayList: 기본적으로 50% 증가하는 방식으로 크기를 조정.
즉, 배열의 크기가 가득 차면 새로운 배열을 만들고, 기존 요소를 복사하여 추가 공간을 만듬. - Vector: 기본적으로 100% 증가하는 방식으로 크기를 조정.
즉, 배열의 크기가 가득 차면 두 배로 늘어남.
- ArrayList: 기본적으로 50% 증가하는 방식으로 크기를 조정.
- 사용 용도
- ArrayList: 성능이 중요한 경우, 또는 스레드 안전성이 필요하지 않은 경우에 주로 사용됩니다. 일반적인 상황에서 가장 많이 사용됩니다.
- Vector: 과거의 레거시 코드나 스레드 안전성이 필요한 경우에 사용되지만
현대의 자바에서ArrayList
와Collections.synchronizedList
를 사용하여 동기화할 수
있으므로 잘 사용되지 않음.
- 결론
ArrayList
와Vector
는 비슷한 기능을 제공하지만, 동기화 여부, 성능, 크기 조정 방식 등에서 차이가 있다.- 일반적으로 멀티스레드 환경이 아닌 경우에는
ArrayList
를 사용하는 것이 더 효율적이며, 스레드 안전성이 필요하다면Collections.synchronizedList
를 통해ArrayList
의 동기화된 버전을 사용하는 것이 더 나은 선택일 수 있음.
- 동기화
추가 정보
리스트 오토박싱
오토박싱 : 기본 데이터 타입(예:
int
,char
,double
등)을 해당하는
래퍼 클래스(예:Integer
,Character
,Double
등)로 자동 변환하는 기능언박싱 : 래퍼 클래스를 기본 데이터 타입으로 변환하는 과정
예시 : 오토박싱을 사용하여 여러 가지 타입을 리스트에 넣고, 이들을 비교
이 예제에서는List<Object>
를 사용하여 다양한 타입의 객체를 저장하고, 각 객체의 타입을 비교하고 출력예제 코드
import java.util.ArrayList; import java.util.List; public class AutoboxingMixedTypesExample { public static void main(String[] args) { // 다양한 타입을 저장할 수 있는 리스트 생성 List<Object> mixedList = new ArrayList<>(); // 오토박싱을 통해 다양한 타입의 데이터 추가 mixedList.add(10); // Integer mixedList.add(20.5); // Double mixedList.add('A'); // Character mixedList.add("Hello"); // String mixedList.add(true); // Boolean // 리스트의 각 요소 타입 비교 및 출력 for (Object obj : mixedList) { if (obj instanceof Integer) { System.out.println("Integer: " + obj); } else if (obj instanceof Double) { System.out.println("Double: " + obj); } else if (obj instanceof Character) { System.out.println("Character: " + obj); } else if (obj instanceof String) { System.out.println("String: " + obj); } else if (obj instanceof Boolean) { System.out.println("Boolean: " + obj); } else { System.out.println("Unknown type: " + obj); } } } }
코드 설명
리스트 생성:
List<Object>
를 사용하여 다양한 타입의 객체를 저장할 수 있는 리스트를 생성합니다.Object
타입은 모든 클래스의 슈퍼클래스이므로, 모든 객체를 저장할 수 있습니다.오토박싱: 다양한 타입의 데이터를 리스트에 추가합니다. 이 과정에서 기본형 데이터(예:
int
,double
,char
,boolean
)는 자동으로 해당하는 래퍼 클래스로 변환됩니다.타입 비교 및 출력:
for
루프를 사용하여 리스트의 각 요소를 순회합니다.instanceof
연산자를 사용하여 각 요소의 타입을 확인하고, 해당 타입에 맞는 메시지를 출력.
출력 예시
이 코드를 실행하면 다음과 같은 출력이 생성:
Integer: 10 Double: 20.5 Character: A String: Hello Boolean: true
오토박싱을 사용시 에러
오토박싱을 사용하여 Java의 컬렉션에서 요소를 제거할 때 발생할 수 있는 문제와 관련된 내용
remove
메서드와 오토박싱 간의 관계에 관한 내용을 예시로 설명오토박싱과
remove
메서드- Java의
ArrayList
와 같은 컬렉션에서remove()
메서드는 두 가지 오버로드된 버전을 제공함remove(int index)
: 주어진 인덱스에 있는 요소를 제거합니다.remove(Object o)
: 주어진 객체와 일치하는 첫 번째 요소를 제거합니다.
- Java의
오토박싱은 기본 데이터 타입과 래퍼 클래스 간의 변환을 자동으로 처리하지만
이 과정에서 의도치 않은 동작이나 예외가 발생할 수 있음.문제 예시
다음은
ArrayList
에서 오토박싱을 사용할 때 발생할 수 있는 문제를 보여주는 예제.import java.util.ArrayList; import java.util.List; public class AutoboxingRemoveExample { public static void main(String[] args) { List<Integer> integerList = new ArrayList<>(); // 오토박싱: int를 Integer로 변환 integerList.add(10); // Integer 10 integerList.add(20); // Integer 20 integerList.add(30); // Integer 30 // remove(Object o) 사용 Integer toRemove = 20; // Integer 객체 boolean removed = integerList.remove(toRemove); // Integer 20을 제거 System.out.println("제거 성공: " + removed); // 출력: 제거 성공: true System.out.println("리스트: " + integerList); // 출력: 리스트: [10, 30] // remove(int index) 사용 try { integerList.remove(5); // 유효하지 않은 인덱스 } catch (IndexOutOfBoundsException e) { // 출력 인덱스 오류 메시지 System.out.println("인덱스 오류: " + e.getMessage()); } } }
설명
- 오토박싱:
integerList.add(10);
와 같은 코드에서 기본형int
가Integer
객체로 자동 변환됨. remove(Object o)
:Integer toRemove = 20;
에서Integer
객체를 생성하고, 이후integerList.remove(toRemove);
를 통해 해당 객체를 리스트에서 제거.
이 경우는 오토박싱과 관련된 문제 없이 정상적으로 작동함.remove(int index)
:integerList.remove(5);
에서 유효하지 않은 인덱스를 사용하면IndexOutOfBoundsException
이 발생. 이 경우는 오토박싱과는 관계가 없음
- 오토박싱:
오토박싱으로 인해 문제가 발생할 수 있는 경우는 다음과 같음:
null
처리: 만약null
을 리스트에 추가하고,remove()
메서드를 호출할 때,remove(Integer)
를 사용하면NullPointerException
이 발생할 수 있음integerList.add(null); integerList.remove(0); // 이 경우, null 제거가 가능하므로 오류는 발생하지 않음
그러나,
remove(Integer.valueOf(0));
와 같은 경우는NullPointerException
을 유발할 수 있다.형변환 이슈: 오버로딩된
remove()
메서드가 존재할 때
오토박싱이 자동으로 작동하여 의도하지 않은 메서드가 호출될 수 있음.// boolean List<E extends Object>.remove(Object o)가 아닌 // E List<E extends Object>.remove(int index)가 실행 integerList.remove(20); // 이 경우, 20이 int로 인식되어 remove(int index)가 호출
이 경우
20
이 리스트에서 인덱스로 해석되므로IndexOutOfBoundsException
이 발생할 수 있음
결론
- 오토박싱은 기본 데이터 타입과 객체 간의 변환을 편리하게 만들어 주지만, 컬렉션에서 요소를 제거할 때는 주의가 필요함.
- 특히,
remove()
메서드를 사용할 때는 인덱스와 객체를 명확히 구분해야 하며,null
값 처리에 유의해야 함. - 이와 같은 문제를 피하기 위해, 항상 인덱스 범위를 체크하고,
null
값이 포함된 경우 적절한 예외 처리를 구현하는 것이 좋다.
'Programming > JAVA' 카테고리의 다른 글
자바 컬렉션 - Queue, PriorityQueue (0) | 2024.10.08 |
---|---|
자바 컬렉션 - Set 인터페이스 (0) | 2024.10.08 |
자바 컬렉션(Java Collection Framework) (0) | 2024.10.08 |
객체지향 프로그래밍(다형성) (0) | 2024.10.08 |
Array, ArrayLists (0) | 2024.10.08 |