💙 들어가며
가변이 되는 collection과 한 가지 자료형에 특화된 제네릭에 대해서 알아보자
✏️ 학습내용 정리
#(번외)JAVA 콜렉션과 제네릭, 스트림
자바는 태초부터 콜렉션이라는 개념을 가지고 있어서 매우 편리하다.
JDK 5점대에 제네릭이 추가되고 8점대에 스트림이라는 것이 추가되었다.
#콜렉션이란?
💡 콜렉션이란 데이터를 수집하고 관리해주는 객체로, 쉽게 생각해서 가변길이 배열이다.
※ 이터레이터: 가변이지만 결국은 출력할 때 인덱스가 필요하게 된다.
이것을 해결하기 위한 열거서비스이다.
콜렉션이란 데이터를 수집하고 관리해주는 객체로,
인덱스를 활용할 필요가 없게 서비스 함수를 제공한다.
공간의 크기를 필요한만큼 알아서 늘려주기 때문에
데이터 크기를 알고 있을 필요가 없다.
#Object 형식
💡 Object란 최상의 추상 클래스로 모든 클래스의 부모가 된다.
콜렉션이라는 개념을 이해하려면
먼저 선행해서 알아야 하는 것이 Object형식이다.
자바에서 모든 객체는 타고타고 올라가면
공통의 최상위 부모를 하나 가지고 있는데,
그것이 바로 Object 클래스이다.
(Object클래스는 자동으로 상속되는 것이기 때문에
모든 객체는 자동으로 Object라는 부모를 가지고 있게 된다.)
자바에서는 부모 클래스가 자식 객체의 참조형이 될 수 있다.
모든 객체는 Object를 부모를 가지기 때문에
Object 자료형하에서 하나로 묶일 수 있다.
즉, Object는 모든 객체의 공통 참조형이 될 수 있다.
Object라는 클래스를 참조형으로 한 배열에는
int형, String형, 객체 등 무궁무진한 것들이
동시에 들어올 수 있게된다.
#Wrapper클래스와 Auto Boxing/Unboxing
그렇다면 Object클래스로 참조할 수 없는 값은 없을까?
원시 자료형 데이터들은
Object클래스의 상속을 받지 않는다.
참조란 어떤 객체에 부여되는 이름이다.
값에 부여되는 이름이 아니다.
이 때문에 정수와 같은 원시형은
객체라고 볼 수 없기 때문에
Object클래스로 참조할 수 없다.
💡 원시 자료형 데이터란 무엇일까?
값 그자체를 표현하는 것들로 소문자로 시작하는 자료형들이다.
논리형(boolean)
정수형(byte, short, int, long)
실수형(float, double)
문자형(char)
때문에 원시형을 Object에 포함시키기 위해서
Wrapper 클래스가 등장했다.
Wrapper 클래스가 있어서
값인 3을 공간으로 만들 수 있게 되었다.
공간을 만들어준다는 것은 Boxing을 의미한다.
Boxing해주기 위한 객체를 Wrapper클래스라고 한다.
3을 Object로 참조하면 자동으로 Boxing이 되는데
이런 것을 auto Boxing이라고 한다.
(그냥 들어가는 것이 아니라
자동으로 Wrapper 클래스로 감싸져서
Object를 참조한 것임)
Number클래스를 부모로 가지고 있는
Wrapper클래스들은
매개변수로 전달될 때 참조값으로 전달될 수 없다.(X)
무조건 Unboxing이 된다.
print(int x)에 Object n=3;을 print(n)을 넣으면
n에 3이 들어갈 때 자동 Boxing이 되고
print에 매개변수로 들어갈 때 자동 Unboxing이 된다.
print(Integer x)처럼 매개변수의 자료형을
Wrapper로 적어주었다고 해도
무조건 Unboxing되어서 입력된다.
Object는 모든 클래스의 공통분모가 되기 때문에
여러가지 값이 Object라는 참조형태에 의해서
하나의 배열에 들어갈 수 있게 된다.
#제네릭(Generic): 무엇이든지 될 수 있는
💡 한 종류의 자료형만 담을 수 있는 가변길이 배열이다.
(범용 콜렉션의 장점과 특화된 클래스의 장점을 합쳐놓았다.)
자동으로 한 가지의 형으로 변환을 해서 데이터를 꺼내준다.
기본적으로 Object이다.
단, 무슨 형태이든지
한 가지의 자료형으로 변환될 수 있는 것이
Object와의 차이점이라고 할 수 있다.
Object는 여러 자료형을 하나로 묶을 수 있다보니
굉장히 편리하지만 사용하다 보니 불편함이 발생했다.
모든 자료형을 담을 수 있다보니
역으로 꺼낼 때 신경을 써야 하는 상황이 발생한 것이다.
꺼낼 때 신경을 써야 한다는 것은 무슨 의미일까?
Object클래스 자체에 여러 자료형을 담는 것이 문제가 되지 않으니
컴파일 할 때 잘못되었다고 말해주지 않고,
결국 실행했을 때에야 오류가 발생하는
크리티컬한 상황이 발생한다.
또, Object참조형으로 또 다른 인스턴스 객체까지 담을 수 있다보니
커버해야 할 자료형까지 너무나 많아졌다.
(커스텀까지 가능한 자료형을
모두 미리 예상해서 자료형 변환을 할 수는 없는 노릇)
그래서 통일된 자료형만 담을 수 있으면서
가변이 가능하도록 특화된 콜렉션이 등장했는데,
이것이 제네릭이다.
제네릭을 사용하면
이제 한 가지 자료형만 담을 수 있게 된다.
물론 다음의 방법으로
여러가지 데이터를 넣을 수도 있다.
(꼭 1가지 자료형이 아니라는 의미)
특히 메소드에서 매개변수로 제네릭이 들어갈 때
어떤 자료형이 와도 괜찮다는 뜻으로
사용하기 위해서는 <?>를 사용한다.
String형의 list와 Integer형의 list1을 print로 출력해보자.
💡 어떤 자료형의 제네릭이 와도 다 커버가능하게 해보자.
public class Program {
public static void print(GList<?> list) {
for(int i=0; i<list.size(); i++)
System.out.println(list.get(i));
}//print ends
public static void main(String[] args) {
GList<String> list = new GList<>();
list.add("hello");
GList<Integer> list1 = new GList<>();
list1.add(33);
print(list);
print(list1);
}//main ends
}//class ends
⭕ 출력결과:
list에 들어있는 값인 "hello"와 list1에 들어있는 값인 33이 모두 출력되었다.
print 메소드에서 매개변수의 자료형을 GList<?>로 설정하니
두 개의 자료형을 모두 커버할 수 있게 되었다!

그러면 Number 형태 중에 아무거나
다 올 수 있게 한다고 하면 어떻게 해야할까?
<? enxtends Number>라고 설정해주면 된다.
💡 Number형 중에 아무거나 매개변수로 올 수 있게 해보자(int, double 등..)
public class Program {
public static void print(GList<? extends Number> list) {
for(int i=0; i<list.size(); i++)
System.out.println(list.get(i));
}//print ends
public static void main(String[] args) {
GList<String> list = new GList<>();
list.add("hello");
GList<Integer> list1 = new GList<>();
list1.add(33);
GList<Double> list2 = new GList<>();
list2.add(34.21);
list2.add(17.78);
print(list); //String형이라서 오류난다.
print(list1); //int라서 오케이
print(list2); //double이라서 오케이
}//main ends
}//class ends
#연습문제(제네릭으로 변환하기)
원하는 형식명을 지정하면
한가지 자료형만 담을 수 있는 제네릭이 된다.
보통 T라고 한다. (Type이라는 의미)
단, 형변환을 할 때는 모두 Object이어야 하기 때문에
int라고 쓸 수는 없다 Integer라고 써야 한다.
(만약 특정한 자료형으로 지정을 하지 않으면
Object로 인식하기 때문에 모든 자료형이 다 들어간다.
<?>와 똑같음)
아래와 같이 String이라고 자료형을 지정하는 순간
String인 "hello"를 제외한 나머지 int형 자료형은
추가될 수가 없게 빨간색으로 오류줄이 나오면서
compile 오류가 난다.
이렇게 자료형을 지정해서 써주게 되면
그 자료형과 어울리는 메소드를 쓸 수 있게
자동으로 형변환을 시켜준다.
#자바가 제공하는 콜렉션: Set/List/Map 콜렉션
모든 언어는 콜렉션이 있지만,
언어 자체에 콜렉션을 포함하고 있는 것은 자바밖에 없다.
그렇다면 자바가 기본적으로 제공하는 콜렉션은 무엇이 있을까?
Set계열 List계열 Map계열에 따른 구체화된 클래스들이 있으나,
내부적으로 차이가 있을 뿐 사용되는 함수는 동일하다.
구현하고 있는 구현체가 엄청나게 많더라도
Set, List, Map만 기억하면 된다.
기능은 같은데 왜 이렇게 구현체가 많은 것일까?
일단 각 계열의 구현체 하나씩만 사용해보자.
💡각 계열별 자주 사용하는 구현체(다 제네릭이다, 형변환해서 꺼내준다.)
Set계열: HashSet
List계열: ArrayList
Map계열: HashMap
Set과 List는 값을 바로 넣을 수 있어서 add를 사용하지만,
Map은 키와 값이 한 세트로 움직이기 때문에
2개의 값을 put을 이용해서 입력한다.
때문에 Set과 List는 <1가지 자료형>을 입력하지만
Map은 <2가지 자료형>을 넣는다.
💡 Set, List, Map계열 구현체를 사용해보자!
//자동으로 필요한 패키지가 import되었음
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
//class
public class Program {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(3);
set.add(3);
set.add(3);
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(3);
list.add(3);
//map에는 add가 없다. put이 있다.
//map은 key와 value가 함께 입력되는 형태이다.
Map<String, Integer> map = new HashMap<>();
map.put("kor1", 3); //같은 키를 넣을 수는 없다.
map.put("kor2", 3); //같은 키를 넣을 수는 없다.
map.put("kor3", 3); //같은 키를 넣을 수는 없다.
}//main ends
}//class ends
#3가지의 차이점
그렇다면 Set, List, Map은 어떤 차이가 있어서
어떤 상황에서 구분해서 사용해야 하는 것일까?
Set | List | Map |
값 자체가 식별자이다. (별도의 식별자가 없으니 값을 꺼내올 수 있는 방법이 없다.) |
식별자가 순서(index)이다. | 식별자가 키(key)이다. 값과 한 세트이며 사용자가 지정한다. |
Set은 달라고 하기 위해 사용하는 식별자 자체가 값이기 때문에 값을 달라고 하는 방법이 따로 없고 값이 있는지만 체크할 수 있다. Set은 중복제거를 할 때 아주 탁월하다. 같은 값들이 입력되면 무효한 입력으로 취급해 저장하지 않는다. |
get(index)로 값을 출력할 수 있다. 순서에 따라 저장하는 방식으로 사용한다. |
get("키값")으로 값을 출력할 수 있다. 데이터 구조를 임시로 담아야 할 때, 임시 키워드가 존재하는 객체의 값 저장공간으로 사용한다. (EX: 한 번만 사용할 건데 클래스를 추가로 만들기 부담스러울 때, 속성이 필요한 데이터 구조가 필요할 때) |
Set, List, Map 각각의 size를 출력해보자.
💡 Set, List, Map의 차이
{
Set<Integer> set = new HashSet<>();
set.add(3);
set.add(3);
set.add(3);
int size = set.size();
System.out.printf("size is %d\n", size);
}
{
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(3);
list.add(3);
list.get(0);
int size = list.size();
System.out.printf("size is %d\n", size);
}
{
//map에는 add가 없다. put이 있다.
//map은 key와 value가 함께 입력되는 형태이다.
Map<String, Integer> map = new HashMap<>();
map.put("kor1", 3); //같은 키를 넣을 수 없다.
map.put("kor2", 3); //같은 키를 넣을 수 없다.
map.put("kor3", 3); //같은 키를 넣을 수 없다.
map.get("kor1");
int size = map.size();
System.out.printf("size is %d\n", size);
}
⭕ 출력결과:
set은 중복된 값이 입력되면
없는 값 취급한다.
(누적하지 않는다.)
따라서 3이 세번 입력되었지만 size는 1이다.

임시 데이터구조 저장공간으로
Map을 사용해보자.
key값으로는 String만 올 수 있게 하고
value로 다양한 값이 올 수 있게 Object로 설정했다.
💡 Menu에 대한 정보를 임시로 담는 Map을 사용해보자.
//Menu 클래스의 인스턴스 만들기
Menu menu = new Menu(1, "아메리카노", "Americano", 3000);
//Map을 이용해서 Menu 클래스의 getter사용하기
Map<String, Object> menuView = new HashMap<>();
menuView.put("id", menu.getId());
menuView.put("korName", menu.getKorName());
menuView.put("engName", menu.getEngName());
menuView.put("price", menu.getPrice());
menuView.put("size", "Large");
💙 마치며
1.
한 가지 자료형에 최적화된
제네릭 Set, List, Map을 잘 사용하면
유연한 데이터구조를 만들 수 있을 것 같다.
2.
Set은 식별자가 값 그 자체라서
값을 꺼낼 수 없다는 것!! 잘 기억해야겠다.
'SQL' 카테고리의 다른 글
[뉴렉처 6기] MyBatis를 활용한 DAO(Repository) 구현하기│SqlSessionFactory│SqlSession (0) | 2023.08.30 |
---|---|
[뉴렉처 6기] 업무로직 / 트랜잭션 / 서비스함수│자바 인터페이스(230830) (0) | 2023.08.30 |
[뉴렉처 6기] Maven 프로젝트│예외처리(230828) (0) | 2023.08.28 |
[뉴렉처 6기] DATABASE│제약조건│도메인│엔티티│관계(230825) (0) | 2023.08.25 |
[뉴렉처 6기] DATABASE│정규화│1NF│2NF│3NF(230824) (0) | 2023.08.24 |