JUINTINATION
제네릭과 컬렉션 본문
반응형
컬렉션
- 요소(element) 객체들의 저장소
- 객체들의 컨테이너라고도 불림
- 요소의 개수에 따라 크기 자동 조절
- 요소의 삽입, 삭제에 따른 요소의 위치 자동 이동
- 고정 크기의 배열을 다루는 어려움 해소
- 다양한 객체들의 삽입, 삭제, 검색 등의 관리 용이
- 컬렉션은 제네릭(generics) 기법으로 구현됨
- 컬렉션의 요소는 객체만 가능
- 기본적으로 int, char, double 등의 기본 타입 사용 불가
- JDK 1.5부터 자동 박싱/언박싱으로 기본 타입 값을 객체로 자동 변환
- 기본적으로 int, char, double 등의 기본 타입 사용 불가
제네릭
- 특정 타입만 다루지 않고, 여러 종류의 타입으로 변신할 수 있도록 클래스나 메소드를 일반화시키는 기법
- <E>, <K>, <V> : 타입 매개 변수
- 요소 타입을 일반화한 타입
- <E>, <K>, <V> : 타입 매개 변수
- 제네릭 클래스 사례
- 제네릭 스택 : Stack<E>
- E에 특정 타입으로 구체화
- 정수만 다루는 스택 Stack<Integer>
- 문자열만 다루는 스택 Stack<String>
- 제네릭 스택 : Stack<E>
Vector<E>
- java.util.Vector
- <E>에서 E 대신 요소로 사용할 특정 타입으로 구체화
- 여러 객체들을 삽입, 삭제, 검색하는 컨테이너 클래스
- 배열의 길이 제한 극복
- 원소의 개수가 넘쳐나면 자동으로 길이 조절
- Vector에 삽입 가능한 것
- 객체, null
- 기본 타입은 Wrapper 객체로 만들어 저장
- Vector에 객체 삽입
- 벡터의 맨 뒤에 객체 추가
- 벡터 중간에 객체 삽입
- Vector에서 객체 삭제
- 임의의 위치에 있는 객체 삭제 가능 : 객체 삭제 후 자동 자리 이동
Vector<Integer> v = new Vector<Integer>();
v.add(4); // 4 → Integer.valueOf(4)로 자동 박싱
int k = v.get(0); // Integer 타입이 int 타입으로 자동 언박싱, k = 4
ArrayList<E>
- java.util.ArrayList, 가변 크기 배열을 구현한 클래스
- <E>에서 E 대신 요소로 사용할 특정 타입으로 구체화
- ArrayList에 삽입 가능한 것
- 객체, null
- 기본 타입은 박싱/언박싱으로 Wrapper 객체로 만들어 저장
- ArrayList에 객체 삽입/삭제
- 리스트의 맨 뒤에 객체 추가
- 리스트의 중간에 객체 삽입
- 임의의 위치에 있는 객체 삭제 가능
- 벡터와 달리 스레드 동기화 기능 없음
- 다수 스레드가 동시에 ArrayList에 접근할 때 동기화되지 않음
- 개발자가 스레드 동기화 코드 작성
ArrayList<Integer> a = new ArrayList<Integer>();
a.add(4); // 4 → Integer.valueOf(4)로 자동 박싱
int k = a.get(0); // Integer 타입이 int 타입으로 자동 언박싱, k = 4
LinkedList<E>
- java.util.LinkedList
- E에 요소로 사용할 타입 지정하여 구체와
- List 인터페이스를 구현한 컬렉션 클래스
- Vector, ArrayList 클래스와 매우 유사하게 작동
- 요소 객체들은 양방향으로 연결되어 관리됨
- 요소 객체는 맨 앞, 맨 뒤에 추가 가능
- 인덱스를 이용하여 중간에 삽입 가능
- 맨 앞이나 맨 뒤에 요소를 추가하거나 삭제할 수 있어 스택이나 큐로 사용 가능
HashMap<K, V>
- java.util.HashMap
- 키(key)와 값(value)의 쌍으로 구성되는 요소를 다루는 컬렉션
- K는 키로 사용할 요소의 타입, V는 값으로 사용할 요소의 타입 지정
- 키와 값이 한 쌍으로 삽입
- 키는 해시맵에 삽입되는 위치 결정에 사용
- 값을 검색하기 위해서는 반드시 키 이용
- 삽입, 삭제, 검색이 빠르다는 특징
- 요소 삽입 : put() 메소드
- 요소 검색 : get() 메소드
Iterator<E>
HashMap<String, String> map = new HashMap<>();
HashSet<String> set = new HashSet<>(cities.keySet());
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
String key = it.next();
String val = map.get(key);
}
Collections 클래스
- java.util.Collections
- 컬렉션에 대해 연산을 수행하고 결과로 컬렉션 리턴
- 모든 메소드는 static 타입
- 주요 메소드
- 컬렉션에 포함된 요소들을 소팅하는 sort() 메소드
- 요소의 순서를 반대로 하는 reverse() 메소드
- 요소들의 최대, 최솟값을 찾아내는 max(), min() 메소드
- 특정 값을 검색하는 binarySearch() 메소드
제네릭 클래스 만들기
- 클래스나 인터페이스 선언부에 일반화된 타입 추가
public class MyClass<T> {
T val;
void set(T a) {
val = a;
}
T get() {
return val;
}
}
- 제네릭 클래스 레퍼런스 변수 선언
MyClass<Double> d;
- 구체화
- 제네릭 타입의 클래스에 구체적인 타입을 대입하여 객체 생성
- 컴파일러에 의해 이루어짐
- 타입 매개 변수에 기본 타입은 사용할 수 없음
MyClass<String> s = new MyClass<String>(); // 제네릭 타입 T에 String 지정
s.set("hello");
System.out.println(s.get()); // "hello" 출력
MyClass<Integer> n = new MyClass<Integer>(); // 제네릭 타입 T에 Integer 지정
n.set(5);
System.out.println(n.get()); // 숫자 5 출력
타입 매개 변수
- ‘<‘과 ‘>’사이에 하나의 대문자를 타입 매개변수로 사용
- 많이 사용하는 타입 매개 변수 문자
- E : Element를 의미하며 컬렉션에서 요소를 표시할 때 많이 사용한다.
- T : Type을 의미한다.
- V : Value를 의미한다.
- K : Key를 의미
- 타입 매개변수가 나타내는 타입의 객체 생성 불가
- ex)
T a = new T();
- ex)
- 타입 매개변수는 나중에 실제 타입으로 구체화
- 어떤 문자도 매개 변수로 사용 가능
MyStack 만들기
- 제네릭에서 배열의 제한
- 제네릭 클래스 또는 인터페이스의 배열을 허용하지 않음
GStack<Integer>[] gs = new GStack<Integer>[10];
- 제네릭 타입의 배열도 허용되지 않음
T[] a = new T[10];- 아래 예제에서는 Object 타입으로 배열 생성 후 실제 사용할 때 타입 캐스팅
- return (T)stck[tos]; // 타입 매개 변수 T타입으로 캐스팅
- 타입 매개변수의 배열에 레퍼런스는 허용
- public void myArray(T[] a) {....}
- 제네릭 클래스 또는 인터페이스의 배열을 허용하지 않음
-
제네릭 메소드 선언 가능
class GenericMethodEx {
static <T> void toStack(T[] a, GStack<T> gs) {
for (int i = 0; i < a.length; i++) {
gs.push(a[i]);
}
}
}
-
제네릭 메소드를 호출할 때는 컴파일러가 메소드의 인자를 통해 이미 타입을 알고 있으므로 타입을 명시할 필요 X
Object[] oArray = new Object[100];
GStack<Object> objectStack = new GStack<Object>();
GenericMethodEx.toStack(oArray, objectStack); // 타입 매개변수 T를 Object로 유추함
class GStack<T> {
int tos;
Object[] stck;
public GStack() {
tos = 0;
stck = new Object[10];
}
public void push(T item) {
if (tos == 10) return;
stck[tos++] = item;
}
public T pop() {
if (tos == 0) return null;
return (T)stck[--tos];
}
}
class GenericMethodEx {
public static <T> GStack<T> reverse(GStack<T> a) {
GStack<T> s = new GStack<T>();
while (true) {
T tmp = a.pop();
if (tmp == null) break;
s.push(tmp);
}
return s;
}
static <T> void toStack(T[] a, GStack<T> gs) {
for (T ai : a) {
gs.push(ai);
}
}
}
public class MyStack extends GenericMethodEx {
public static void main(String[] args) {
GStack<Integer> intStack = new GStack<Integer>();
for (int i = 0; i < 10; i++) {
intStack.push(i);
}
for (Object e : intStack.stck) {
System.out.print(e + " ");
}
System.out.println();
intStack = reverse(intStack);
for (Object e : intStack.stck) {
System.out.print(e + " ");
}
}
}
제네릭의 장점
- 컴파일 시에 타입이 결정되어 보다 안전한 프로그래밍 가능
- 런타임 타입 충돌 문제 방지
- ClassCastException 방지
내 생각
수업 시간에 위의 내용을 배우기 전에 코딩하면서 당연하다고 생각하며 넘어갔던 내용이었습니다. 그저 배열의 크기가 한정되어 있지 않기 때문에 사용하던 것들이었는데 추후에 프로젝트를 진행하면서 여러 가지 Exception을 방지할 수 있고 보다 안전한 프로그래밍이 가능하다는 장점이 있으며 원한다면 저만의 제네릭 클래스를 만들 수 있다는 사실이 놀라웠습니다.
728x90
'자바프로그래밍및실습' 카테고리의 다른 글
JDBC 프로그래밍 (0) | 2023.01.08 |
---|---|
소켓 프로그래밍 (0) | 2023.01.06 |
스레드와 멀티태스킹 (0) | 2023.01.03 |
Comments