제네릭의 장점을 이해하고 제네릭 클래스를 정의할 수 있다.
특정 메서드만 제네릭으로 선언하여 사용할 수 있다.
제네릭의 장점은?
1. 타입체크와 형변환을 생략해서 간결한 코드 작성이 가능하다.
2. 클래스나 메서드 내부에서 사용되는 객체의 타입 안정성을 제공한다.
👻지네릭스란?
- Generics : 컴파일 시 타입을 체크 해주는 기능
* Error : ClassCastException : 형변환 에러
- 컴파일 에러 > 실행 시 에러
- 실행시 발생하는 에러를 컴파일 단으로 끌고올 수 있을까 ? -> 지네릭스의 탄생
- 지네릭스의 사용으로 형변환 생략 가능, 타입 체크 강화, Object쓰면 레거시 처럼 여러 객체 사용 가능
👻타입 변수
- 타입변수 : 클래스 작성 시 <E>와 같은 타입변수를 주어 작성한다.
- 타입변수 대입 : 참조변수와 생성자에 실제타입을 지정해주어야 한다.
-> 한번에 변경되기 때문에 형변환은 따로 해주지 않아도 된다.
👻지네릭스용어, 지네릭 타입과 다형성
1. 참조변수와 생성자의 대입된 타입(타입 매개변수)은 일치(상속관계여도 안됨 무조건 일치해야함)
2. 지네릭 ‘클래스’간의 다형성은 성립된다(다형성: 조상 클래스의 참조변수로 자손 클래스의 객체를 다룰수 있다.)
3. 매개변수의 다형성도 성립
👻Iterator, HashMap과 지네릭스
- Iterator<E> : 클래스 작성 시 Object타입 대신 T와 같은 타입 변수 사용
- HashMap<K, V> : 여러 개의 타입 변수가 필요한 경우, 콤마 구분자
👻제한된 지네릭 클래스, 지네릭스의 제약
- 제한된 지네릭 클래스 : extends로 대입할 수 있는 클래스 제한
-> class FruitBox<T extends Fruit> { ... // Fruit의 자손만 타입으로 대입 가능 }
- interface인 경우에도 extends 사용
- 지네릭스의 제약
1) 타입변수의 대입은 인스턴스 별로 다르게 가능
2) static 멤버에 타입 변수 사용 불가 -> 모든 인스턴스에 공통
3) 배열 생성할 때 타입 변수 사용 불가. 타입 변수로 배열 선언은 가능 -> new T(불가 x) - 객체생성 x, 배열생성 x(타입이 확정적이기 못하기 때문에)
👻와일드카드, 지네릭 메서드
- 와일드 카드 <?> : 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능 (지네릭 타입 일치 조건을 맞추는 번거로움을 해결하고자 등장), 지네릭의 다형성
- <? extends T> : 와일트 카드의 상한 제한, T와 그 자손들만 가능
- <? super T> : 와일드 카드의 하한 제한, T와 그 조사들만 가능
- <?> : 제한 없음. 모든 타입이 가능, <? extends Object> 와 동일
- 지네릭 메서드
- 메서드 선언부에 선언된 메서드를 지네릭 메서드라 한다.
- 지네릭 메서드의 선언 위치는 반환 타입 바로 앞이다.
- 지네릭 클래스에 정의된 타입 매개변수가 T이고, 지네릭 메서드에 정의된 타입 매개변수가 T이더라도 이 둘은 전혀 별개의 것이다.
- 지네릭 메서드는 꼭 지네릭 클래스가 아닌 일반 클래스에도 정의될 수 있다.
- 원래는 static 멤버에는 타입 매개변수를 사용할 수 없지만, 메서드에 지네릭 타입을 선언하고 사용하는 것은 가능하다.
👻지네릭 형변환
- 지네릭 타입의 형변환 : 지네릭타입과 원시 타입간의 형변환은 가능은 하지만 바람직 하지 않다. (경고 발생)
-> 지네릭 타입 --> 원시타입(경고 발생), 원시 타입 --> 지네릭 타입(경고 발생)
-> Box<String> --> Box<Object> or Box<String> --> Box<Object> 는 형변환 안됨(에러)
- 지네릭 타입의 제거 : 하위호환성과 안정성 문제 때문에 자바 컴파일러는 지네릭 타입을 제거하고 필요한 곳에 형변환을 넣는다.
1) 지네릭 타입의 경계(bound)를 제거(T -> Object or 제한된 타입)
2) 지네릭 타입 제거 후에 타입이 불일치 하면 형변환을 추가
3) 와일드 카드가 포함된 경우엔, 적절한 타입으로 형변환 추가
컬렉션 프레임워크의 핵심 인터페이스를 이해하고 사용할 수 있다.
주요 인터페이스와 컬렉션 클래스의 핵심 메서드를 사용할 수 있다.
필요에 따라 어떤 인터페이스와 컬렉션 클래스를 사용할지 이해할 수 있다.
👻컬렉션프레임웍과 핵심 인터페이스
- 컬렉션(collection) : 여러 객체(데이터)를 모아 놓은 것
- 프레임웤(framework) : 표준화, 정형화된 체계적인 프로그래밍방식, 생산성을 올려줌, 등장 배경을 이해하면 프레임웤을 사용하는 이유를 저절로 알게 됨
ex) collection framework, django framework, spring framework
- 라이브러리 : 정보, 책, 오디오 라이브러리 등 -> 기능만 제공
- 컬렉션 프레임웤 : 컬렉션(다수의 객체)을 다루기 위한 표준화된 프로그래밍 방식, java.util패키지에 포함, jdk1.2부터제공
- List : 순서 O, 중복 O
ex) 대기자 명단
* 구현 클래스 : ArrayList, LinkedList, Stack, Vector
- Set : 순서X, 중복X ex) 양의 정수 집합, 소수의집합
* 구현클래스 : HashSet, TreeSet
- Map : key, value쌍, 순서X, 중복(키X, 값O)
ex) 우편번호, 지역번호(전화번호)
* 구현클래스 : HashMap, TreeMap, HashTable(legacy), Properties(legacy)
👻Collection, List, Set, Map
Collection인터페이스
List인터페이스(순서O, 중복O: ArrayList, LinkedList..)
-List subList(from, to): 일부 반환
Set인터페이스(순서X, 중복X: HashSet, TreeSet..)
-boolean을 반환하는 집합 관련 메서드
Map인터페이스(순서X, 키중복X, 값중복O: HashMap, TreeMap..)
-LinkedHashMap: 순서O
-HashTable: old, 동기화(HashMap 별도 처리)
-put(key, value) / putAll(Map)
-Set entrySet(), Set keySet(), Collection values()
👻ArrayList
- ArrayList: Vector를 개선한 것, 배열기반으로 데이터 저장, 순서와 중복 O, 객체만 저장가능
list1.remove(1): 인덱스가 1인 객체를 삭제 VS list1.remove(new Integer(1)): 1을 삭제.
- 삭제과정
1. 삭제할 데이터의 아래에 있는 데이터를 한 칸씩 위로 복사해서 삭제할 데이터를 덮어쓴다.
2. 데이터가 모두 한 칸씩 위로 이동하였으므로 마지막 데이터는 null로 변경해야한다.
3. 데이터가 삭제되어 데이터의 개수(size)가 줄었으므로 size의 값을 1 감소시킨다.
만약 반복문으로 삭제하게 될 때
첫번째부터 삭제한다면 배열복사가 발생하여 모두 지워지지 않는다.
하지만 마지막부터 삭제한다면, 배열 복사가 발생되지 않아 모두 지워지게 된다.
👻LinkedList
- 배열의 장단점 :
(장) - 구조간단, 데이터를 읽는 데 걸리는 시간 짧음, 순차적 데이터 추가, 삭제는 빠름
(단) - 크기변경 어려움(배열 새로 생성 : 더 큰 배열 생성, 기존 내용 복사), 비순차적인 데이터 추가 삭제에 시간 많이 걸림
- LinkedList(배열의 단점을 보완) : 불연속적으로 존재하는 데이터를 연결 / 하나씩 연결된 기차라고 생각하자
* 데이터 삭제 : 단 한번의 참조변경만으로 가능
* 데이터 추가 : 한 번의 Node객체 생성, 두 번의 참조변경으로 가능
- ArrayList vs LinkedList 비교 -> Array 와 LinkedList DS비교와 동일
👻Stack과 Queue
- Stack : LIFO, push, pop, 순차적 추가 / 삭제가 가능한 배열 사용해 구현
- Queue : FIFO, offer, poll, 링크드 리스트가 더 적합(삭제 - 추출에 더 유리, 자리이동이 없으므로)
- Stack&Queue에서 peek : 맨 위의 저장된 객체를 본다.(반환)
search : 0이 아닌 1부터 시작
- Queue에서 add, remove : 예외발생O
- Queue에서 offer, poll : 예외발생X
- Queue는 interface로 정의 됨 -> 객체 생성 불가 : Queue직접 구현, 구현된 클래스 사용
👻Stack, Queue 활용
- 스택 활용 예 : 수식계산, 수식괄호검사, 워드프로세서 undo/redo, 웹브라우저 뒤로/앞으로
- 큐의 활용 예 : 최근사용문서, 인쇄작업 대기목록, 버퍼
👻Iterator, Enumeration, Map과 Iterator
- Iterator, ListIterator, Enumeration(legacy) : 컬렉션에 저장된 데이터를 접근하는데 사용되는 인터페이스
ListIterator는 Iterator의 접근성 향상 (양방향)
* 컬렉션에 저장된 요소를 읽어오는 방법 표준화 : hasNext()(확인), next()(읽기)
- Iterator는 끝까지 가고나면 다시 못돌아옴(1회용) -> 다시 얻어와야 한다
- 컬렉션 프레임워크를 종종 바꿔줘야하는 이슈가 있기에 Iterator를 사용
- Map은 Iterator가 없다. 대신에 keySet(), entrySet(), values() 사용하여 Iterator를 사용할 수 있다.
👻Arrays
- Arrays : 배열을 다루는데 편리하게 해주는 static 메서드 제공 : toString()(출력), copyOf(), copyOfRange()(복사), fill(), setAll()(람다식 들어감)(채우기), sort(), binarySearch() (정렬과 검색, bs는 정렬후에 해라)
- 순차검색과, 이진검색(SQL 튜닝의 그것과 연관, Full scan, Indexing)
- 다차원 배열의 출력 : deepToString()
- 다차원 배열의 비교 : deepEquals()
- asList() : 배열을 다루기 편리한 메서드(static) 제공: 읽기전용
- 람다스트림 관련 : parallelXXX(), spliterator(), stream()
👻Comparator와 Comparable
- Comparator, Comparable : 객체 정렬에 필요한 메서드를 정의한 interface
-> Comparable : 기본 정렬기준 구현
-> Comparator : 기본 정렬 기준 외 다른 기준으로 정렬하고자 할 때
- sort() : 두 대상에 대하여 1) 비교 2) 자리바꿈
- compare(), compareTo() : 두 객체의 비교 결과 반환, 같으면 0 오른쪽이 크면 -, 왼쪽이 크면 +
- Integer와 Comparable <- 기본 정렬 기준 제공
- 3항연산자가 속도가 빨라 조금 더 유리하다.
👻HashSet
HashSet
순서를 유지하지 않고, 중복을 허용하지 않는다. (list와 반대 특성)
HashSet과 TreeSet은 set 인터페이스를 구현한 대표적인 클래스
Set이 필요하다면 일반적인 HashSet을 사용하지만,
순서를 유지하고 싶다면 LinkedHashSet클래스를 사용한다.
HashSet - 주요 메서드
HashSet(Colletion c) - 생성자를 가지고 있다.
지정 된 클래스에 모든 객체를 저장.
HashSet(int initialCapacity) - 초기용량 정하기 보통 두배로 늘린다.
언제 두배로 늘릴 것인지 정하는
HashSet(int initialCapacity, float loadFactor)
추가 삭제와 모두 삭제하는 메서드
retainAll(Colletion c) - 조건부 삭제 . 교집합 구할 때 쓰인다.
addAll(Colletion c) - 합집합
removeAll(Colletion c) - 차집합
contains(Object o) - set이 해당 객체를 포함하고 있는데 true or false 반환
containsAll(Colletion c) - 컬렉션에 담긴 여러 객체가 모두 포함되어 있는지? True or false 반환 컬렉션으로 받는다.
이터레이터() - 컬렉션 요소 읽어오기
이즈엠프티() - 비어있는지? 확인
사이즈() - 저장 된 객체의 갯수
투어레이(Object[] a) - set에 저장되어 있는 객체를 객체 배열로 반환
기본적으로 set은 정렬이 불가능한데 정렬을 하고싶다면 set의 모든 요소를 리스트에 저장한 후에 리스트를 정렬하면 된다.
- HashSet : 같은 객체가 없으면 저장, 있으면 저장X ex) add메서드를 실행 시 equals, hashCode를 호출
- equals, hashCode을 overriding하지 않으면 위 비교작업이 제대로 작동하지 않음
👻TreeSet
범위 검색(from~to)과 정렬에 유리한 컬렉션 클래스.
단점으로는 HashSet 보다 데이터 추가, 삭제에 시간이 더 걸린다.
이진 탐색 트리(binary search tree)라는 자료구조 형태로 데이터를 저장하게 되는데,
이진트리는 나무와 비슷하게 생겼으면 구성되어 있는 각 하나를 노드라고 한다.
각 노드는 0~2개의 노드를 연결할 수 있다.
이진 탐색 트리는 부모노드의 왼쪽에는 부모노드의 값보다 작은 값을, 오른쪽에는 큰 값의 자식 노드를 저장하는 이진 트리이다.이처럼 정렬된 상태를 유지하기 때문에 단일 값 검색과 범위검색이 매우 빠르다.
하지만 링크드 리스트보다 데이터의 추가/삭제 시간은 더 걸린다. 대신 배열이나 링크드 리스트에 비해 검색과 정렬기능이 더 뛰어나다.
👻HashMap
- HashMap, HashTable(legacy) : 순서X, 중복(키X, 값O)
- 해싱의 원리 : 환자정보관리 예제 생각, 해시 함수를 이용해서 해시테이블에 있는 데이터 읽고 저장
- 해시테이블 : 배열과 링크드 리스트가 조합된 형태(chaining), 접근성과 변경의 유리를 섞은 형태
- 저장된 데이터를 가져오는 과정 : 키로 해시함수 호출하고 해시코드를 얻는다 -> 해시코드(해시f반환값)에 대응하는 링크드 리스트를 배열에서 찾는다 -> 링크드 리스트에서 키와 일치하는 데이터 탐색
-> 같은 키에 대해 같은 해시코드 반환해야 함, 서로 다른 키 일지라도 같은 값의 해시코드를 반환 할 수 있음
👻Collections클래스, 컬렉션 클래스 요약
- Objects(객체), Arrays(배열), Collections(컬렉션)
- 컬렉션의 동기화 : synchronizedXXX() ex) synchronizedCollection(Collection c), synchronizedList(List list)
- List syncList = Collections.synchronizedList(new ArrayList(...)); // 좌 : 동기화 된 리스트, 우 : 동기화 되지 않은 리스트
- 변경불가(readOnly) 컬렉션 만들기 : unmodifiableXXX()
- 싱글톤 컬렉션 만들기 : singletonXXX() // 객체 1개만 저장
- 한 종류의 객체만 저장하는 컬렉션 만들기 : checkedXXX() // 한 종류의 객체만 저장하는 컬렉션 ex) List checkedList = checkedList(list, String.class) // String만 저장 가능
- disjoint(list, newList) // 공통 요소가 있는지 확인할 때 사용하는 메서드
오늘은 컬렉션 프레임 워크와 제너릭에 대한 개념을 학습했다.
아마 지금까지 공부를 하면서 가장 절망을 많이 한 날이 아닌가싶다.
객체지향에 대한 개념도 가끔 흔들리는 부분이 있다보니 여러번 반복하면서 계속 복습해야할 것 같다.
지금 하고 있는 것들에 대한 개념이 확실하게 잡히지 않으면 미래에 스프링을 공부할 때 많이 힘들다는 얘기를 들었다. 그렇기에 나중에 가서 헷갈리지 않도록 확실하게 복습 할 예정이다.
오늘 정말 고생 많았고 내일도 파이팅!