본문 바로가기

STUDY/Java

<자바의 신> 21장 실수를 방지하기 위한 제네릭이라는 것도 있어요

21장 실수를 방지하기 위한 제네릭이라는 것도 있어요

제네릭 (Generic)

타입 형 변환에서 발생할 수 있는 문제점을 사전에 없애기 위해 나온 타입
  • 제네릭 타입 사용 전

 

public class CastingDTO implements Serializable{
	private Object object;
	public void setObject(Object object){
		this.object=object;
	}
	public Object getObject(){
		return object;
	}
}

 

  • 제네릭 타입 사용

 

public class CastingGenericDTO<T> implements Serializable{
	private T object;
	public void setObject(T obj){
		this.object=obj;
	}
	public T getObject(){
		return object;
	}
}

 

 

제네릭 타입의 이름

  • E : 요소 (Element, 자바 컬렉션에서 주로 사용)
  • K : 키
  • N : 숫자
  • T : 타입
  • V : 값
  • S, U, V : 두번째, 세번째, 네번째에 선언된 타입

 

<?> wildcard 타입

여러 타입을 제네릭 타입으로 받고 싶을 때 사용
public static void main(String[] args){
	WildcardSample sample = new WildcardSample();
	sample.callWildcardMethod();
}

public void callWildcardMethod(){
	wildcardGeneric<String> wildcard = new WildcardGeneric<String>();
	wildcard.setWildcard("A");
	wildcardStringMethod(wildcard);
	
	wildcardGeneric<Integer> wildcardInt = new WildcardGeneric<Integer>();
	wildcardInt.setWildcard(77);
	wildcardStringMethod(wildcardInt);
}

public void wildcardStringMethod(WildcardGeneric<?> c){
	Object value = c.getWildcard(); // 메소드 내부에서는 Object로 처리해야 한다
	if(value instanceof String){ // 타입 확인 
		System.out.println(value); 
}
  • 와일드카드로 객체를 선언했을 때는 특정 타입으로 값을 지정하는 것이 불가능하므로 메소드의 매개 변수로만 사용하는 것이 좋음

 

 

? extends 타입 (Bounded Wildcards)

  • 사용하는 타입을 제한할 때
  • ? extends Car 라고 선언 → 제네릭 타입으로 Car 클래스와 관련되어 있는 상속한 클래스만 사용 가능
  • 값을 할당할 수는 없기 때문에 조회용 매개 변수로 사용해야 함

 

 

메소드를 제네릭하게 선언하기

public class GenericWildcardSample{
    public static void main(String arg[]){
        GenericWildcardSample sample = new GenericWildcardSample();
    }
    public <T> void genericMethod(WildcardGeneric<T> c, T addValue){
        c.setWildcard(addValue);
        T value = c.getWildcard();
        System.out.println(value);
    }
}
  • 리턴 타입 앞에 제네릭 타입을 선언해주고 그 타입을 매개 변수에서 사용하면 값을 할당 가능
  • 리턴 타입 앞에 도 가능

제네릭 타입이 여러개 일 때

public <S,T extends Car> void multiGenericMethod(WildcardGeneric<T> c, T addValue, S another)

 


 

22장 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part1(List)

Collection

목록성 데이터를 처리하는 자료 구조
  • List: 순서가 있는 목록형
  • Set: 순서가 없는 셋형
  • Queue: FIFO인 큐형
  • Map: Key-Value로 저장되는 맵형

 

Collection 인터페이스

public interface Collection<E> extends Iterable<E>

 

List

  • 순서가 있음
  • ArrayList, Vector, Stack, LinkedList
  • ArrayList는 Thread safe하지 않고, Vector는 Thread safe 하다

 

1. ArrayList

// 상속 관계
java.lang.Object
	ㄴjava.util.AbstractCollection<E>
		ㄴjava.util.AbstractList<E>
			ㄴjava.util.ArrayList<E>

 

  • 생성자
생성자 설명
ArrayList() 객체를 저장할 공간이 10개인 ArrayList를 만든다
ArrayList(Collection<? extends E> c) 매개 변수로 넘어온 컬렉션 객체가 저장되어 있는 ArrayList를 만든다
ArrayList(int initialCapacity) 매개 변수로 넘어온 initialCapacity 개수만큼의 저장공간을 갖는 ArrayList를 만든다
ArrayList<String> list1 = new ArrayList<String>();
// JDK 7부터는 ArrayList<String> list1 =  new ArrayList<>(); 으로 사용해도 됨

*보통 ArrayList는 한 가지 종류의 객체만 저장

 

  • 메소드
리턴 타입 메소드 이름 및 매개 변수 설명
boolean add(E e) 데이터를 가장 끝에 저장
void add(int index, E e) 데이터를 지정된 index 위치에 저장
boolean addAll(Collection<? extends E> c) 컬렉션 데이터를 가장 끝에 저장
boolean addAll(int index, Collection<? extends E> c) 컬렉션 데이터를 index에 지정된 위치에 저장
// 향상된 for문
for(타입이름 임시변수명 : 반복대상객체){ 
	...
}
// list 복사 (addAll() 사용)
public void checkArrayList(){
	ArrayList<String> list = new ArrayList<>();
	list.add("A");
	list.add("B");
	list.add("C");
	ArrayList<String> list2 = new ArrayList<>();
	list2.add("0");
	list2.addAll(list);
	for(String tempData : list2){
		System.out.println("List2 : " + tempData);
	}
}

// 결과
List2 0
List2 A
List2 B
List2 C
// list 복사 (생성자 사용)
public void checkArrayList(){
	ArrayList<String> list = new ArrayList<>();
	list.add("A");
	list.add("B");
	list.add("C");
	// 생성자로 복사
	ArrayList<String> list2 = new ArrayList<>(list);  
	
	for(String tempData : list2){
		System.out.println("List2 : " + tempData);
	}
}
// Shallow copy 
public void checkArrayList(){
	ArrayList<String> list = new ArrayList<>();
	list.add("A");
	
	ArrayList<String> list2 = list;  
	list.add("B");
	for(String tempData : list2){
		System.out.println("List2 : " + tempData);
	}
}

// 결과
List2 : A
List2 : B

*list2 = list 는 list라는 객체가 생성되어 참조되고 있는 주소까지 사용하겠다는 의미

*Shallow copy: 다른 객체에 원본 객체의 주소 값만을 할당하는 것

(↔ Deep copy: 객체의 모든 값을 복사하여 복제된 객체에 있는 값을 변경해도 원본에 영향이 없도록 하는 것)

 

리턴 타입 메소드 이름 및 매개 변수 설명
int size() ArrayList 객체에 들어가 있는 데이터의 개수를 리턴
E get(int index) 매개 변수에 지정한 위치에 있는 데이터를 리턴
int indexOf(Object o) 매개 변수로 넘어온 객체와 동일한 데이터의 위치를 리턴
int lastIndexOf(Object o) 매개 변수로 넘어온 객체와 동일한 마지막 데이터의 위치를 리턴
리턴 타입 메소드 이름 및 매개 변수 설명
Object[] toArray() ArrayList 객체에 있는 값들을 Object[] 타입의 배열로 생성
<T> T[] toArray(T[] a) ArrayList 객체에 있는 값들을 매개 변수로 넘어온 T 타입의 배열로 생성
public void checkArrayList(){
    ArrayList<String> list = new ArrayList<>();
    list.add("A");
    String[] strList = list.toArray(new String[0]);
    System.out.println(strList[0]);
}

// 결과
A

*크기가 0인 배열을 넘겨주는 게 좋음

 

 

리턴 타입 메소드 이름 및 매개 변수 설명
void clear() 모든 데이터를 삭제
E remove(int index) 지정한 위치에 있는 데이터를 삭제하고 삭제한 데이터를 리턴
boolean remove(Object o) 매개 변수와 동일한 첫 번째 데이터를 삭제
boolean removeAll(Collection<?> c) 매개 변수로 넘어온 컬렉션 객체에 있는 데이터와 동일한 모든 데이터 삭제
리턴 타입 메소드 이름 및 매개 변수 설명
E set(int index, E element) 지정한 위치에 있는 데이터를 두 번째 매개변수로 넘긴 값으로 변경하고 해당 위치에 있던 데이터를 리턴
void trimToSize() ArrayList 객체 공간의 크기를 데이터의 개수만큼 변경

 

  • Thread 안전하게 하려면
List list = Collections.synchronizedList(new ArrayList(...));

 

2. Stack

  • LIFO (Last In First Out) 을 지원, 가장 마지막에 추가한 값을 가장 먼저 뺌
  • Thread 안전
  • ArrayDeque: Stack 보다 빠른 LIFO 지원, Thread 안전x 
// 상속 관계
java.lang.Object
	ㄴjava.util.AbstractCollection<E>
		ㄴjava.util.AbstractList<E>
			ㄴjava.util.Vector<E>
				ㄴjava.util.Stack<E>
  • 생성자
생성자 설명
stack() 아무 데이터도 없는 Stack 객체를 생성

 

  • 메소드
리턴 타입 메소드 이름 및 매개 변수 설명
boolean empty() 객체가 비어있는지 확인
E peek() 객체의 가장 위에 있는 데이터를 리턴
E pop() 객체의 가장 위에 있는 데이터를 지우고 리턴
E push(E item) 매개 변수로 넘어온 데이터를 가장 위에 저장
int search(Object o) 매개 변수로 넘어온 데이터의 위치를 리턴
public void checkPop(){
	Stack<Integer> intStack = new Stack<>();
	for(int loop=0; loop<5; loop++){
		intStack.push(loop);
		System.out.println(intStack.pop());
	}
	System.out.println("size = " + intStack.size());
}

// 결과
0
1
2
3
4
size = 0

 

 


 

 

23장 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part2(Set과 Queue)

Set

  • 순서에 상관 없이, 어떤 데이터가 존재하는지를 확인하기 위한 용도로 많이 사용
  • 중복되는 것을 방지하고 원하는 값이 포함되어 있는지를 확인하는 것이 주 용도

1. HashSet

순서가 전혀 필요 없는 데이터를 해시 테이블에 저장한다. Set 중에 가장 성능이 좋다
// 상속 관계
java.lang.Obejct
    ㄴjava.util.AbstractCollection<E>
        ㄴjava.uitl.AbstractSet<E>
            ㄴjava.util.HashSet<E>
  • 생성자
생성자 설명
HashSet() 데이터를 저장할 수 있는 16개의 공간과 0.75의 로드 팩터를 갖는 객체를 생성
HashSet(Collection<? extends E> c) 매개 변수로 받은 컬렉션 객체의 데이터를 HashSet에 담는다
HashSet(int initialCapacity) 매개 변수로 받은 개수만큼의 데이터 저장공간과 0.75의 로드팩터를 갖는 객체를 생성
HashSet(int initialCapacity, float loadFactor) 첫 매개 변수로 받은 개수만큼의 데이터 저장 공간과 두 번째 매개 변수로 받은 만큼의 로드 팩터를 갖는 객체를 생성

*로드 팩터 = (데이터의 개수)/(저장 공간)

*데이터의 개수가 증가하여 로드팩터보다 커지면 해시 재정리 작업을 해야 함→ 성능 영향

 

  • 메소드
리턴 타입 메소드 이름 및 매개 변수 설명
boolean add(E e) 데이터를 추가
void clear() 모든 데이터를 삭제
Object clone() HashSet 객체를 복제(데이터는 복제 x)
boolean contains(Object o) 지정한 객체가 존재하는지 확인
boolean isEmpty() 데이터가 있는지 확인
Iterator<E> iterator() 데이터를 꺼내기 위한 Interator 객체를 리턴
boolean remove(Object o) 매개 변수로 넘어온 객체를 삭제
int size() 데이터의 개수를 리턴
public class SetSample{
	public static void main(String[] args){
		SetSample sample = new SetSample();
		String[] cars = new String[]{"BMW", "Benz", "Mini Cooper", "BMW"};
		System.out.println(sample.getCarKinds(cars));
}

public int getCarKinds(String[] cars){
	if(cars==null) return 0;
	if(cars.length==1) return 1;
	Set<String> carSet = new HashSet<>();
	for(String car:cars){
		carSet.add(car); // 순서는 없지만 중복 없이 개수를 구할 수 있음
	}
return carSet.size();
}

// 결과
3
// iterator 사용하여 출력
public void printCarSet(Set<String> carSet){
	Iterator<String> iterator = carSet.iterator(); // Iterator 객체 생성
	while(iterator.hasNext()){ // 다음 데이터가 존재하는 지 확인
		System.out.print(iterator.next()+ " ");
	}
	System.out.println();
}

 

 

2. LinkedList

  • List이면서 Queue, Deque
    // 상속 관계
    java.lang.Object
    	ㄴjava.util.AbstractCollection<E>
    		ㄴjava.util.AbstractList<E>
    			ㄴjava.uitl.AbstractSequentialList<E>
    				ㄴjava.util.LinkedList<E>
  • 생성자
생성자 설명
LinkedList() 비어 있는 LinkedList 객체를 생성
LinkedList(Collection<? extends E> c) 매개 변수로 받은 컬렉션 객체의 데이터를 LinkedList에 담는다

*처음부터 크기를 지정하지 않음 → 각 데이터들이 앞 뒤로 연결되는 구조이므로

 

  • 메소드
리턴 타입 메소드 이름 및 매개 변수 설명
void addFirst(Object) LinkedList 객체의 가장 앞에 데이터를 추가
boolean add(Object) LinkedList 객체의 가장 뒤에 데이터를 추가
boolean addLast(Object) LinkedList 객체의 가장 뒤에 데이터를 추가
void add(int, Object) LinkedList 객체의 특정 위치에 데이터를 추가
Object set(int, Object) LinkedList 객체의 특정 위치에 있는 데이터를 수정하고 기존에 있던 데이터를 리턴
boolean addAll(Collection) 매개 변수로 넘긴 컬렉션의 데이터를 추가
boolean addAll(int, Collection) 매개 변수로 넘긴 컬렉션의 데이터를 지정된 위치에 추가
리턴 타입 메소드 이름 및 매개 변수 설명
Object getFirst() LinkedList 객체의 맨 앞에 있는 데이터를 리턴
Object getLast() LinkedList 객체의 맨 뒤에 있는 데이터를 리턴
Object get(int) LinkedList 객체의 지정한 위치에 있는 데이터를 리턴
리턴 타입 메소드 이름 및 매개 변수 설명
boolean contains(Object) 매개 변수로 넘긴 데이터가 있을 경우 true
int indexOf(Object) 매개 변수로 넘긴 데이터의 위치를 앞에서부터 검색하여 리턴 (없을 경우 -1)
int lastIndexOf(Object) 매개 변수로 넘긴 데이터의 위치를 끝에서부터 검색하여 리턴 (없을 경우 -1)
리턴 타입 메소드 이름 및 매개 변수 설명
Object removeFirst() LinkedList 객체의 가장 앞에 있는 데이터를 삭제하고 리턴
Object removeLast() LinkedList 객체의 가장 끝에 있는 데이터를 삭제하고 리턴
리턴 타입 메소드 이름 및 매개 변수 설명
void clear() 모든 데이터를 삭제
Object clone() LinkedList 객체를 복제
int size() 데이터의 개수를 리턴
Object[] toArray() LinkedList 객체에 있는 값들을 Object[] 타입의 배열로 생성
리턴 타입 메소드 이름 및 매개 변수 설명
ListIterator listIterator(int) 매개 변수에 지정된 위치부터의 데이터를 검색하기 위한 listIterator 객체를 리턴
Iterator descendingIterator() LinkedList의 데이터를 끝에서부터 검색하기 위한 Iterator 객체를 리턴

*next(), previous() 메소드로 이전, 다음 데이터 검색 가능

 

3. TreeSet

저장된 데이터의 값에 따라서 정렬되는 셋, red-black이라는 트리 타입으로 값이 저장되며, HashSet보다 약간 성능이 느리다

 

4. LinkedHashSet

연결된 목록 타입으로 구현된 해시 테이블에 데이터를 저장한다. 저장된 순서에 따라서 값이 정렬된다. 대신 성능이 가장 나쁘다

 

 

 


 

 

24장 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part3(Map)

Map

  • Key-Value로 이루어진 자료 구조
  • 모든 데이터는 Key와 Value가 존재한다
  • Key가 없이 Value만 저장될 수는 없다
  • Value 없이 Key만 저장할 수도 없다
  • Key는 해당 Map에서 고유해야만 한다
  • Value는 Map에서 중복되어도 상관 없다

Map 인터페이스

리턴 타입 메소드 이름 및 매개 변수 설명
V put(K key, V value) Key, Value 데이터를 저장
V get(Object key) Key에 해당하는 값을 리턴
V remove(Object key) Key에 해당하는 값을 넘겨주며 키와 값은 Map에서 삭제
Set<K> keySet() key의 목록을 Set 타입으로 리턴
Collection<V> values() Value의 목록을 Collection 타입으로 리턴
Set<Map.Entry<K,V>> entrySet() Map 안에 Entry라는 타입의 Set을 리턴
int size() Map의 크기를 리턴
void clear() Map의 내용을 지운다
  • Hashtable 클래스와 Map 인터페이스를 구현한 클래스의 차이
HashMap Hashtable
키나 값에 null 저장 가능 키나 값에 null 불가
쓰레드 안전 X 쓰레드 안전 O
// HashMap을 Thread 안전하게 하려면
Map m = Collections.synchronizedMap(new HashMap(...));

 

HashMap

// 상속 관계
java.lang.Object
    ㄴjava.util.AbstractMap<K,V>
        ㄴjava.util.HashMap<K,V>
  • 생성자
생성자 설명
HashMap() 16개의 저장 공간을 갖는 HashMap 객체를 생성
HashMap(int initialCapacity) 매개 변수만큼의 저장 공간을 갖는 HashMap 객체 생성
HashMap(int initialCapacity, float loadFactor) 매개 변수 만큼의 저장 공간과 로드 팩터를 갖는 HashMap 객체 생성
HashMap(Map<? extends K, ? extends V> m) 매개 변수로 넘어온 Map을 구현한 객체에 있는 데이터를 갖는 HashMap 객체 생성

*hashCode()와 eqauls() 메소드를 잘 구현해 놔야 함 (java map buckets)

// 새로운 값을 추가하거나 수정 모두 put() 사용
public void checkHashMap(){
    HashMap<String, String> map = new HashMap<String, String>();
    map.put("A", "a");
    System.out.println("1: " + map.get("A"));
    map.put("A", "b");
    System.out.println("2: " + map.get("B"));
}

// 결과
1: a
2: b
  • keySet()
public void checkKeySet(){
	HashMap<String, String> map = new HashMap<>();
	map.put("A", "a");
	map.put("B", "b");
	map.put("C", "c");
	Set<String> keySet = map.keySet();
	for(String tempKey : keySet){
		Sysetm.out.println(tempKey + " = " + map.get(tempKey));
	}
}

// 결과 (순서가 중요하지 않음)
B = b
A = a
C = c
  • values()
public void checkValues(){
	HashMap<String, String> map = new HashMap<>();
	map.put("A", "a");
	map.put("B", "b");
	map.put("C", "c");
	
	Collection<String> values = map.values();
	for(String value : values){
		System.out.println(value);
	}
}

// 결과
b
a
c

 

  • entrySet()
public void checkHashMapEntry(){
	HashMap<String, String> map = new HashMap<>();
	map.put("A", "a");
	map.put("B", "b");
	map.put("C", "c");
	
	Set<Map.Entry<String,String>> entries = map.entrySet();
	for(Map.Entry<String,String> entry : entries){
		System.out.println(entry.getKey() + " = " + entry.getValue());
	}
}

// 결과
B = b
A = a
C = c

 

 

TreeMap

  • 키를 정렬하여 저장하는 Map
  • 숫자 > 알파벳 대문자 > 알파벳 소문자 > 한글 순
  • firstKey(), lastKey(), higherKey(), lowerKey() 메소드 제공