제네릭
- 다양한 타입에도 동작하는 메소드와 클래스 작성이 필요할 때가 있음
ArrayList<T>는 임의의 클래스 T를 요소로 저장
- ArrayList의 클래스 : 제네릭 클래스
- T : 타입 파라미터
- 제네릭의 타입 파라미터(T)는 기본 타입은 지원하지 않음
제네릭 클래스
- 타입 파라미터를 한 개 이상 받는 클래스
<예제>
키/값 쌍을 저장하는 클래스
class Entry<K, V> {
private K key;
private V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
public K get Key() {
return this.key;
}
public V getValue() {
return this.value;
}
}
- 클래스 뒤 <> 안에 타입 파라미터 명시
- 필드와 메소드 파라미터, 반환값으로 사용됨
- 타입변수를 해당 타입으로 교체하여 객체 생성
- Entry<String, Integer>
- 타입 파라미터 인스턴스로 기본타입 사용 불가
- 객체 생성 시 생성자에서 타입 파라미터 생략 가능(JDK 7부터)
- Entry<String, Integer> entry = new Entry<>(“Kim”, 35);
제네릭 메소드
타입 파라미터를 받는 메소드
- 제네릭 클래스나 일반 클래스의 메소드가 될 수 있다.
- 타입 파라미터를 리턴 타입 앞에 적어준다.
Arrays.swap(friends, 0, 1) => 이것도 가능하지만
Arrays.<String>swap(friends, 0, 1) => 보통 이렇게 파라미터 타입을 명시적으로 적어줌.
class Arrays {
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[i] = temp;
}
}
=> swap이라는 메소드의 입력파라미터로 <T>가 온다.
타입 가변성과 와일드 카드
자바에서는 와일드카드로 메소드의 파라미터와 리턴타입이 변하는 방식을 지정
(?(와일드카드)를 사용하여 클래스타입에 제약을 줌)
- 서브타입 와일드카드
public static void printNames(ArrayList<? extends Employee> staff) {…}
=> printNames 메소드의 staff 파라미터를 지정해주는데, ArrayList의 타입 파라미터가 일반적으로 T이면 모든 타입이 들어갈 수 있지만, ? extends Employee는 Employee의 타입과 Employee 하위 타입들이 모두 들어갈 수 있다는 것이다. 와일드카드(?)를 사용하지 않고 그냥 extends Employee를 해주면 Employee 타입만 타입 파라미터로 받는다는 것이다.
- 슈퍼타입 와일드카드
public static void printAll(Employee[] staff, Predicate<? super Employee> filter) {…}
=> printAll 메소드의 두 번째 파라미터인 filter의 파라미터 타입을 Employee와 그 상위 클래스 타입으로 한다.
- 경계 없는 와일드카드
public static boolean hasNulls(ArrayList<?> elements) {…}
=> 그냥 <?> 라면 아무 타입이나 들어갈 수 있다.
컬렉션 프레임워크
- 지금까지 많은 자료구조가 개발되어옴
- 자바는 객체지향이기때문에 모든 자료구조를 클래스로 만들어 놓았다.
- 클래스에는 자료 타입과 알고리즘까지 담겨있고, 이런 타입들이 따로 떨어져있는것이 아니라 계층적 구조를 가지고 있다. 상속 관계 등을 포함해서 모든 자료구조들이 긴밀하게 연결되있는데, 이것을 컬렉션 프레임워크라 한다.
- 모든 데이터는 이 컬렉션 프레임워크를 통해 다뤄진다.
- 이 컬렉션 프레임워크를 제대로 이해해야 데이터를 다룰 수 있다.
- 개발자가 데이터를 효율적으로 저장하고 꺼내올 수 있게 함
컬렉션 프레임워크
- 자료구조와 알고리즘을 구현
- 계층적으로 조직화되어 있음
=> Set과 ArrayList가 많이 쓰인다. Vector는 많이 안쓰인다.
Map은 key와 value로 사용되는데, 그 중에서도 HashMap이 많이 쓰인다.
제네릭, Generic
List를 비롯한 모든 Collection Framework는 Generic을 지원해서 특정 타입을 명시한다.
기본 타입은 타입 파라미터로 사용 불가하므로 Wrapper 클래스를 사용
예) List<int>로, int를 엘레먼트로 사용하는 List를 타입으로 쓰고싶은데 int는 기본타입이라서 못씀. 이 때는 기본 타입을 클래스타입으로 매핑시켜놓은 Wrapper class를 사용한다. 그래서 List<Ingeter>라고 쓰면 된다.
Set, 집합
요소의 순서에 상관 없이 요소를 추가하고 포함 여부를 테스트 가능
- HashSet : 요소에 해시함수가 구현되어 요소의 식별이 가능 (식별만 할 때)
- TreeSet : 이진트리로 구현, 요소가 순서가 있어 전체 요소를 순회 가능 (순서가 중요할 때)
<예제>
나쁜 단어 집합을 생성하고 포함 여부를 테스트 하기
Set<String> badWords = new HashSet<>();
badWords.add(“smoking”);
badWords.add(“drug”);
badWords.add(“c++”);
if (badWords.contains(username.toLowerCase()))
System.out.println(“다른 단어를 사용해 주세요..”);
Map, 맵
- 연관된 키(key)와 값(value)을 저장함. 여러군데에서 많이 쓰이고 있음.
- Redis : 대표적인 키 밸류 데이터베이스.
- HashMap을 주로 사용하며, 순서가 중요할 때는 TreeMap을 사용함
List, 리스트
- 인덱스를 통해 데이터를 관리하는 구조
- List 인터페이스를 구현하며, 아래 두 가지 구현체가 주로 사용됨
- ArrayList : 주로 사용됨.
- LinkedList : 많이 사용되지 않음.
ArrayList vs. LinkedList
=> ArrayList는 데이터가 인접해있기 때문에 데이터를 중간에 끼워넣을때 뒤에있는게 다 밀려야 함. 그래서 데이터의 삽입이 매우 어려움. 데이터 삭제를 할때도 가운데에 있는 데이터를 삭제하면 전체적으로 메모리가 재배열되야하는 문제가 생김.
=> 장점은 참조가 쉽다. 데이터가 자연스럽게 정렬이 되있기 때문에. 포문으로 풀스캔을 할때 등엔 효율적이지만 삽입, 삭제 등을 할 땐 어렵다.
=> LinkedList는 엘레멘트들이 서로 물리적으로 떨어져있음. 0번째 인덱스에는 1번째 인덱스의 주소값을 가지고 있음. 그래서 장점은 데이터 삽입과 삭제가 쉬워진다. link만 바꾸면 되기 때문에. 하지만 참조가 어렵다. 주소값을 확인하고 그곳에 가서 찾아오고 하는 식이기 때문에.
=> 우리가 다루고자 하는 데이터의 성격에 따라 쓰이는 방식이 달라짐. 데이터를 삽입/삭제하는 경우가 빈번하다면 LinkedList, 참조가 중요하다면 ArrayList.
기타 컬렉션
Properties
키/값으로 데이터를 저장하며, 주로 설정파일로 사용됨 (HashMap과 비슷하지만 설정파일에 적용하기 적합하게 만들어놨다. 설정파일로 저장하고 읽어오는 메소드들이 포함됨)
Stack (리스트가 조금 변형된 형태)
한쪽 끝에서 요소를 추가하고 제거하는 데이터 구조임
(데이터를 조회하거나 다룰때 한 쪽 구멍에서만 이용. 데이터를 push하고 pop하는 두 가지 알고리즘만 제공한다. 고스톱같은 프로그램을 만들때 쓸 수 있다. 맨 위에있는 패만 뺄 수 있기 때문.)
Queue (리스트가 조금 변형된 형태)
요소를 한쪽 끝(Tail)에서 추가하고 다른 한쪽 끝(Head)에서 제거함
(양쪽을 다 사용한다. 리스트의 한 쪽 끝에는 추가만, 다른 끝은 제거만 한다.)
=> 기존의 컬렉션 프레임워크를 사용하기도 하지만 우리가 원하는 자료구조를 사용하기 위해 이러한 컬렉션들을 상속받아 사용하는 경우도 있다.
ThinkPoint
데이터를 다룰 때 데이터 구조를 immutable한 데이터 타입을 선호하는 경우가 많다. immutable하다는 의미와 어떤 경우에 데이터 구조를 immutable하게 가져갈지에 대해서 생각해보자.
Immutable이란 생성 후 변경 불가한 객체이다.
그래서 immutable에는 set 메소드가 없다.
return type이 void인 메소드도 없다.
주로 void 메소드는 뭔가를 하고(하지 않을 수도 있고..) 멤버변수를 변경하는 역할을 하는 것이기 때문에 쓸 일이 거의 없다. Immutable을 쓰면, 멀티 쓰레드 환경에서 좀 더 신뢰할 수 있는 코드를 만들어 내기가 쉽다.
'Programming > Java' 카테고리의 다른 글
[Java9 프로그래밍] 16. 스트림 활용과 Optional (0) | 2020.03.30 |
---|---|
[Java9 프로그래밍] 15. 스트림 (0) | 2020.03.30 |
[Java9 프로그래밍] 13. 예외 처리 (0) | 2020.03.21 |
[Java9 프로그래밍] 12. 람다 표현식 (0) | 2020.03.21 |
[Java9 프로그래밍] 11. 인터페이스 (0) | 2020.03.21 |