제네릭
제네릭을 사용하지 않고 Object와 같은 넓은 범위를 수용할 수 있는 필드 변수의 타입을 사용 시
-> 형반환이 필요, 런타임 오류 발생
클래스나 메서드가 사용할 데이터타입을 컴파일 시점에 지정할 수 있도록 지원하는 문법
-> 매개타입(객체 생성시 타입을 전달받아 결정)
public static void main(String[] args) {
//제네릭 클래스의 매개타입을 전달하지 않으면 기본적으로 Object로 설정
Box aBox = new Box<>();
aBox.setValue("value");
//int num = aBox.getValue(); // 반한시에도 Object로 반환
int num = (Integer)aBox.getValue(); //형변환
System.out.println(aBox.getValue());
Box<Integer> iBox = new Box<>();
//iBox.setValue("value"); 매개타입이 Integer이므로 Stirng 전달불가
iBox.setValue(100);
int num2 = iBox.getValue(); //형변환 불필요
Integer[] arr = new Integer[5];
iBox.setArr(arr);
//iBox.setArr(new String[] {"1", "2"}); String이라서 불가능
}
제네릭은 형변환을 여러번하는 과정을 없애고, 매개 타입에 맞는 데이터만 입력할 수 있도록 해주는 편리한 문법이다.
컬렉션
자료구조 개념이 내장되어 있는 클래스로 자바에서 제공하는 라이브러리
자료구조 : 데이터를 보다 효율적으로 관리(추가, 삭제, 조회, 정렬, 수정)할 수 있도록 해주는 구조
List
순서가 존재하며, 중복이 가능하다.
ArrayList(배열기반), LinkedList(노드기반), Vector, Stack(Vector기반 LIFO구조)
장점
- 크기제약 X, 지정된 크기보다 더 많이 추가해도 에러가 발생하지 않음.
- 여러 타입 보관가능, 다양한 타입의 데이터를 담을 수 있음.
public static void main(String[] args) {
List list = new ArrayList(); //크기를 지정하지 않아도 됨
System.out.println(list); //빈 배열상태
//1. add(E e) : 리스트 공간 끝에 전달된 데이터를 추가
list.add(new Human("이무진", 26));
list.add("이무진"); //여러타입을 저장할 수 있음.
list.add(new Human("이무진", 26));
list.add(new Human("이무진", 26));
System.out.println(list);
//2. add(int index, E e) : 직접 인덱스를 지정해서 해당위치 데이터를 추가
list.add(1, new Human("홍길동", 20));
System.out.println(list);
//3. remove(int index) : 해당 인덱스 위치의 데이터를 삭제시켜주는 메서드
list.remove(1);
System.out.println(list);
//4. remove(E e) : 객체를 직접 지정해서 삭제
list.remove("이무진");
System.out.println(list);
list.remove(new Human("이무진", 26));
System.out.println(list);
//5. set(int index, E e) : 해당 인덱스의 값을 전달받은 e객체로 덮어씌움
list.set(0, new Human("홍길동", 23));
System.out.println(list);
//6. size() : 리스트의 사용중인 사이즈를 반환
System.out.println(list.size());
//7. get(int index) : 해당 인덱스의 객체를 반환
System.out.println(list.get(1));
//8. addAll(Collection c) : 컬렉션을 통째로 뒤에 추가할 수 있음
List sub = new ArrayList();
sub.add(new Human("이무진", 26));
sub.add(new Human("홍길동", 23));
list.addAll(sub);
System.out.println(list);
//9. isEmpty() : boolean -> 컬렉션이 비어있는지 확인
System.out.println(list.isEmpty());
//10. clear() : 컬렉션의 내용을 전부 비워버리는 메서드
list.clear();
System.out.println(list.isEmpty());
System.out.println(list);
//리스트 전체 접근 방법
for(int i=0; i<list.size(); i++) {
System.out.println(list.get(i));
}
for(Object h : list) {
System.out.println(h);
}
//List list = new LinkedList();여도 메서드는 동일
}
Stack
public static void main(String[] args) {
//Stack
Stack<String> stack = new Stack<>();
//push() 데이터 추가
stack.push("첫번째");
stack.push("두번째");
stack.push("세번째");
System.out.println(stack);
//peek() 맨 위 데이터를 확인
System.out.println(stack.peek());
//pop() 데이터 꺼내기
String data = stack.pop();
System.out.println(data);
System.out.println(stack);
//검색 search(E e) : 데이터 검색 후 존재하면 위치 반환(위에서부터 센다) -> equals를 사용하여 비교검색
System.out.println(stack.search("첫번째"));
//stack 전체 꺼내기
while(!stack.isEmpty()) {
System.out.println("꺼낸 값 : " + stack.pop());
}
}
컬렉션에서 List나 Stack은 equals를 오버라이딩해서 사용해야 한다.
@Override
public boolean equals(Object obj) {
if(obj instanceof Human) {
Human h = (Human)obj;
return this.name.equals(h.getName()) && this.age == h.getAge();
}
return false;
}
Set
순서가 없고, 중복을 허용하지 않는 자료구조
index개념이 없어서 위치기반 접근(get(index))이 불가
- HashSet : 일반적인 해시알고리즘이 적용된 set
- LinkedHashSet : hashSet + 순서유지
- TreeSet : 자동정렬 기능 제공
public static void main(String[] args) {
//생성
Set<Human> set = new HashSet<>();
//추가
set.add(new Human("홍길동", 20));
set.add(new Human("홍길동", 25));
set.add(new Human("홍길서", 20));
set.add(new Human("홍길남", 25));
System.out.println(set);
//set에 저장해서 사용하는 객체(Human)는 equals와 hashcode를 오버라이딩해야한다.
//set은 hashCode()로 분류 후 equals()로 바교해서 중복값을 검사함
set.add(new Human("홍길동", 20));
set.add(new Human("홍길동", 25));
System.out.println(set); //동일객체는 추가가 되지 않음
Human h1 = new Human("홍길남", 25);
Human h2 = new Human("홍길남", 25);
//동일객체 : (h1.equals(h2) && h1.hashCode() == h2.hashCode())
//객체마다의 정의된 hashCode와 equals의 결과가 모두 일치하는 객체
//equals와 hashcode를 오버라이딩하지 않으면 Object의 equals와 hashcode를 사용
//Object의 equals -> 주소값 비교
//Object의 hashCode -> 주소값을 가지고 10진수형태의 해시값을 구한것
//contains() 요소에 포함여부 확인
System.out.println("홍길남이 존재? : " + set.contains(h2)); //true
//remove(E e)
set.remove(h1);
System.out.println("삭제 후 : " + set);
//size()
System.out.println("size : " + set.size());
//set의 모든 요소에 순차적으로 접근하는 방법
//set은 index가 없기 때문에 get()을 사용할 수 없음
//1. for ~ each
for(Human h : set) {
System.out.println(h);
}
//2. ArrayList에 담아서 반복 -> addAll(Collection c)
ArrayList list = new ArrayList();
list.addAll(set);
for (int i=0; i<list.size(); i++) {
System.out.println(list.get(i));
}
//3. Iterator(반복자 인터페이스)를 활용
//컬렉션에 저장된 요소를 순차적으로 접근하기 위한 인터페이스
//순서가 없는 set같은 자료구조를 탐색할 때 반드시 필요
//hasNext() : 다음 읽을 요소가 있으면 true, 없으면 false
//next() : 다음 요소를 반환
Iterator<Human> it = set.iterator();
while(it.hasNext()) {
Human h = it.next(); //다음 요소 꺼내기
System.out.println(h);
}
}
Set을 사용하려면 equals뿐만이 아니라 hashcode도 오버라이딩을 해야 한다.
@Override
public boolean equals(Object obj) {
if(obj instanceof Human) {
Human h = (Human)obj;
return this.name.equals(h.getName()) && this.age == h.getAge();
}
return false;
}
@Override
public int hashCode() {
// return ("" + name + age).hashCode(); -> String의 HashCode메서드 이용(문자열 값에 따라 달라짐)
return Objects.hash(name, age); //Object에 hash메서드를 이용하는 방법(비교를 원하는 모든 인자를 전달 가능)
}
Map
key와 value를 쌍으로 저장하는 자료구조
key는 중복 불가, value는 중복 가능
HashMap(가장 보편적이고 속도향상), LinkedHashMap(입력순서 보장), TreeMap(정렬된 key순서 유지)
public static void main(String[] args) {
Map<Integer, Human> hMap = new HashMap<>();
//1. put() : map에 key, value 쌍으로 값을 추가
hMap.put(10, new Human("홍길동", 20));
hMap.put(20, new Human("홍길서", 30));
hMap.put(30, new Human("홍길남", 40));
hMap.put(40, new Human("홍길북", 50));
hMap.put(40, new Human("홍길", 60)); //동일한 key 사용시 값을 덮어씀
System.out.println(hMap); //저장되는 순서 유지 안됨
//2. get(key) : 해당 key의 value를 반환
System.out.println(hMap.get(30));
//3. size() : 담겨있는 객체 수
System.out.println("size : " + hMap.size());
//4. remove(key) : 해당 key의 값을 찾아서 쌍으로 제거
hMap.remove(40);
System.out.println(hMap);
//map의 전체요소를 탐색하는 방법
//Iterator 이용 X
//for each 이용 X
//다른 자료구조로 변경 후 반복
//1. key를 모아서 Set자료 구조 형태로 반환
Set keySet = hMap.keySet(); //모든 key를 Set에 담아서 반환
System.out.println(keySet);
for(Object key : keySet) {
System.out.println("키 : " + key + "값 : " + hMap.get(key));
}
//2. entrySet을 이용한 전체 탐색
//Map은 key와 value를 쌍으로 저장하기 때문에 단순하게 keySet(), values()만으로는
//한번에 key와 value를 가져올 수 없다.
//Set<Map.Entry<K, V>>
Set entrySet = hMap.entrySet();
for(Object entry : entrySet) {
Entry e = (Entry)entry;
System.out.println(e.getKey() + " " + e.getValue());
}
}
마찬가지로 Map도 equals와 hashCode를 오버라이딩해야 한다.(위 Set에서 사용한 equals와 hashCode를 사용)