Do it! 자바 프로그래밍 입문 12장
<T>와 같은 내용을 붙임< >는 다이아몬드 연산자T는 타입 매개변수타입 매개변수는 레퍼런스 타입만 가능 (int불가, Integer가능)
일반적으로 아래와 같은 매개변수 이름 사용
<T> Type
<K> Key
<N> Number
<E> Element
<V> Value
class DoublePrinter <T> {
private T target;
public DoublePrinter(T target) {
this.target = target;
}
public void print() {
System.out.println(target);
System.out.println(target);
}
}
public class Test {
public static void main(String[] args) {
DoublePrinter<Integer> p1 = new DoublePrinter<Integer>(53);
p1.print(); // 53 53
// 앞에 <Character>를 붙이지 않아도 타입 추론 가능
DoublePrinter p2 = new DoublePrinter<Character>('a');
p2.print(); // a a
}
}
<>연산자 안에 콤마,로 구분하여 여러 개의 타입매개변수 입력 가능
class Printer <T, U> {
private T target1;
private U target2;
public Printer(T target1, U target2) {
this.target1 = target1;
this.target2 = target2;
}
public void print() {
System.out.println(target1);
System.out.println(target2);
}
}
public class Test {
public static void main(String[] args) {
Printer p1 = new Printer<Float, Float>(5.3f, 53.53f);
p1.print(); // 5.3 53.53
Printer p2 = new Printer<Boolean, Byte>(true, (byte)53);
p2.print(); // true 53
}
}
제네릭 클래스가 아니더라도 제네릭 메소드를 가질 수 있음
class Printer {
public <T, U> void print(T t, U u) {
System.out.println(t);
System.out.println(u);
}
}
public class Test {
public static void main(String[] args) {
Printer p1 = new Printer();
p1.<Integer, Double>print(53, 5.3); // 53 5.3
}
}
아래와 같이 <T>가 두 번 등장할 수 있다.
여기서 클래스 Printer에 대한 <T>와 메소드 print()에 대한 <T>는 다른 T이다.
class Printer <T> {
private T variable_For_Class;
public <T> void print(T variable_For_This_Method) {
System.out.println(variable_For_This_Method);
}
}
TCP School 타입변수의 제한
<T extends Something>과 같이 T를 특정 타입으로 제한 가능
class뿐 아니라 interface도 타입 매개변수를 제한할 때
implements가 아닌extends사용
아래 코드에서 타입변수 T는 Language를 상속한 클래스만 올 수 있도록 제한됨
class CodeRunner <T extends Language> {
private T target;
public CodeRunner(T target) {
this.target = target;
}
public void run() {
System.out.println(target.toString());
}
}
abstract class Language {
protected int runCnt;
public Language() {
runCnt = 0;
}
}
class Python extends Language {
@Override
public String toString() {
runCnt++;
return "Python is running / cnt : " + runCnt;
}
}
class Java extends Language {
@Override
public String toString() {
runCnt++;
return "Java is running / cnt : " + runCnt;
}
}
class Gorani {
// something
}
public class Test {
public static void main(String[] args) {
CodeRunner pyRunner = new CodeRunner<Python>(new Python());
CodeRunner javaRunner = new CodeRunner<Java>(new Java());
pyRunner.run(); // Python is running / cnt : 1
javaRunner.run(); // Java is running / cnt : 1
javaRunner.run(); // Java is running / cnt : 2
javaRunner.run(); // Java is running / cnt : 3
// compile error (Gorani타입을 받을 수 없음)
// CodeRunner goraniRunner = new CodeRunner<Gorani>(new Gorani());
}
}
java.util패키지에 포함** tree가 붙는 클래스는 Red-Black Tree로 구현된 BST사용
두 계열의 클래스 모두 iterator를 사용한 순회 가능
Collection인터페이스 상속)Collection인터페이스 상속)List변수 = Collections.synchronizedList(ArrayList의 인스턴스)로 받으면 동기화 가능import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Vector;
class Gorani {
int id;
String name;
public Gorani (int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return name+"#"+id;
}
}
public class Test {
public static void main(String[] args) {
Gorani g1 = new Gorani(123, "a");
Gorani g2 = new Gorani(35, "b");
Gorani g3 = new Gorani(8999, "c");
// ArrayList
ArrayList list = new ArrayList<Gorani>();
list.add(g1); list.add(g2); list.add(g3);
System.out.println(list); // [a#123, b#35, c#8999]
// 동기화 기능을 추가한 ArrayList
List<Gorani> syncList = Collections.synchronizedList(new ArrayList<Gorani>());
syncList.add(g1); syncList.add(g2);
System.out.println(syncList); // [a#123, b#35]
// Vector (동기화 지원)
Vector v = new Vector<Gorani>();
v.add(g1); v.add(g3);
System.out.println(v); // [a#123, c#8999]
}
}
import java.util.Stack;
public class Test {
public static void main(String[] args) {
Stack<String> s = new Stack<String>();
s.push("a"); s.push("b"); s.push("c");
System.out.println(s.empty()); // false
System.out.println(s.peek()); // c
System.out.println(s.pop()); // c
System.out.println(s.pop()); // b
System.out.println(s.size()); // 1
}
}
실제 Queue라는 이름의 클래스는 없고, 인터페이스가 존재한다.
JAVA 18 DOCS : Interface Queue
아래 포스트에서 Queue<E> queue = new LinkedList<E>(); 꼴로 큐처럼 사용하는 예시를 볼 수 있다.
DevAndy : Java Collection - Queue
ArrayList로 구현할 수도 있다.
Github easyspubjava/JAVA_LAB/ ... /QueueTest.java
아래 순회방법 예시 1~3은 모두 같은 결과를 낸다.
import java.util.ArrayList;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
ArrayList<String> ar = new ArrayList<String>();
ar.add("aa"); ar.add("bb"); ar.add("abcd");
// 순회방법 예시 1 : enhanced for loop
for(String item : ar)
System.out.println(item); // aa bb abcd
// 순회방법 예시 2 : for loop with get()
for(int i=0; i<ar.size(); i++)
System.out.println(ar.get(i)); // aa bb abcd
// 순회방법 예시 3 : iterator
Iterator<String> itr = ar.iterator();
while(itr.hasNext())
System.out.println(itr.next()); // aa bb abcd
}
}
해싱으로 구현하는 Set
타입매개변수에 직접 정의한 클래스를 넣는 경우 equals()와 hashCode()를 재정의 해야 해싱의 특성상 정상적으로 동작함
import java.util.HashSet;
import java.util.Iterator;
class Gorani {
int id;
String name;
public Gorani (int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return name+"#"+id;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Gorani) {
Gorani g = (Gorani)obj;
if(this.id == g.id)
return true;
}
return false;
}
@Override
public int hashCode() {
return id;
}
}
public class Test {
public static void main(String[] args) {
// String HashSet
HashSet<String> hs1 = new HashSet<String>();
Iterator<String> itr1;
hs1.add("aa");
hs1.add("bb");
hs1.add("cc");
itr1 = hs1.iterator();
while(itr1.hasNext())
System.out.println(itr1.next()); // aa bb cc
hs1.add("bb"); // 이미 같은 원소가 존재하므로 추가되지 않음
itr1 = hs1.iterator();
while(itr1.hasNext())
System.out.println(itr1.next()); // aa bb cc
// Gorani HashSet
HashSet<Gorani> hs2 = new HashSet<Gorani>(); // id가 유니크한 값
Iterator<Gorani> itr2;
hs2.add(new Gorani(123, "xx"));
hs2.add(new Gorani(335, "yy"));
hs2.add(new Gorani(975, "zz"));
itr2 = hs2.iterator();
while(itr2.hasNext())
System.out.println(itr2.next()); // xx#123 yy#335 zz#975
hs2.add(new Gorani(123, "xyzyzyz")); // 이미 같은 원소가 존재하므로 추가되지 않음
itr2 = hs2.iterator();
while(itr2.hasNext())
System.out.println(itr2.next()); // xx#123 yy#335 zz#975
}
}
객체의 정렬에 사용. Red-Black 기반의 BST로 구현되어있음.
타입매개변수에 직접 정의한 클래스를 넣는 경우 Case1, Case2 둘 중 하나를 수행해야한다.
( 클래스 식별자가 C라고 가정 )
Comparable<C> 인터페이스 구현CompareTo(C obj)메소드 오버라이드Comparator<C> 인터페이스 구현Compare(C obj1, C obj2)메소드 오버라이드new C()전달import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
class Gorani implements Comparable<Gorani>, Comparator<Gorani> {
int id;
String name;
public Gorani() { }
public Gorani (int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int compareTo(Gorani g) { // id기준 오름차순
return this.id - g.id;
}
@Override
public int compare(Gorani g1, Gorani g2) { // id기준 내림차순 (-1을 곱했기 때문)
return (g1.id - g2.id) * (-1);
}
@Override
public String toString() {
return name+"#"+id;
}
}
public class Test {
public static void main(String[] args) {
// String TreeSet
TreeSet<Integer> ts1 = new TreeSet<Integer>();
ts1.add(53); ts1.add(999); ts1.add(1); ts1.add(68);
Iterator<Integer> itr1 = ts1.iterator();
while(itr1.hasNext())
System.out.println(itr1.next()); // 1 53 68 999
// Gorani TreeSet : Case1
// ---> Comparable<E>가 구현 위임한 compareTo(E e)를 호출
// ---> compareTo메소드는 오름차순 정렬하도록 오버라이드 하였음
TreeSet<Gorani> ts2 = new TreeSet<Gorani>();
ts2.add(new Gorani(123, "a")); ts2.add(new Gorani(11, "b")); ts2.add(new Gorani(99, "c"));
Iterator<Gorani> itr2 = ts2.iterator();
while(itr2.hasNext())
System.out.println(itr2.next()); // b#11 c#99 a#123
// Gorani TreeSet : Case2
// ---> Comparator<E>가 구현 위임한 compare(E e1, E e2)를 호출
// ---> compare메소드는 내림차순 정렬하도록 오버라이드 하였음
TreeSet<Gorani> ts3 = new TreeSet<Gorani>(new Gorani());
ts3.add(new Gorani(123, "a")); ts3.add(new Gorani(11, "b")); ts3.add(new Gorani(99, "c"));
Iterator<Gorani> itr3 = ts3.iterator();
while(itr3.hasNext())
System.out.println(itr3.next()); // a#123 c#99 b#11
}
}
JAVA 18 DOCS : Interface Map<K,V>
객체의 유일성(unique함)을 판단하기 위해 equals()와 hashCode() 재정의 필요(오버라이드)
key와 value를 받는 클래스
key를 해싱해서 value저장
import java.util.HashMap;
import java.util.Iterator;
class Gorani {
int id;
String name;
public Gorani() { }
public Gorani (int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return name+"#"+id;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Gorani) {
Gorani g = (Gorani)obj;
if(this.id == g.id)
return true;
}
return false;
}
@Override
public int hashCode() {
return id;
}
}
public class Test {
public static void main(String[] args) {
// key로 Gorani의 id (int) 사용
// value로 Gorani인스턴스 사용
HashMap<Integer, Gorani> hashmap = new HashMap<Integer, Gorani>();
hashmap.put(123, new Gorani(123, "aaaaa"));
hashmap.put(666, new Gorani(666, "zdfdfd"));
hashmap.put(999, new Gorani(999, "0o0o0o0o0"));
var tmp = hashmap.get(123);
System.out.println(tmp.name); // aaaaa
tmp = hashmap.remove(666);
System.out.println(tmp.name); // zdfdfd
// 순회
System.out.println();
Iterator<Integer> itr = hashmap.keySet().iterator();
while(itr.hasNext()) {
var key = itr.next();
System.out.println( hashmap.get(key) ); // 0o0o0o0o0#999 aaaaa#123
}
}
}
HashMap과 유사하고 동기화 지원 (ArrayList-Vector의 관계와 유사)
JAVA 18 DOCS : Class Hashtable<K,V>
iterator로 원소를 순회하면 key값을 기준으로 정렬되어 나온다.
Set계열 클래스 중 TreeSet과 유사한 원리로 대소비교.
(key값에 대해 비교 인터페이스 구현 + 비교 메소드 오버라이드 필요)
사용법은 HashMap과 동일
import java.util.Iterator;
import java.util.TreeMap;
class Gorani {
int id;
String name;
public Gorani() { }
public Gorani (int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return name+"#"+id;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Gorani) {
Gorani g = (Gorani)obj;
if(this.id == g.id)
return true;
}
return false;
}
@Override
public int hashCode() {
return id;
}
}
public class Test {
public static void main(String[] args) {
// key로 Gorani의 id (int) 사용
// value로 Gorani인스턴스 사용
TreeMap<Integer, Gorani> treemap = new TreeMap<Integer, Gorani>();
treemap.put(123, new Gorani(123, "a"));
treemap.put(666, new Gorani(666, "d"));
treemap.put(999, new Gorani(999, "ee"));
treemap.put(2, new Gorani(2, "ccc"));
treemap.put(53, new Gorani(53, "ee"));
var tmp = treemap.get(999);
System.out.println(tmp.name); // ee
tmp = treemap.remove(999);
System.out.println(tmp.name); // ee
// 순회
System.out.println();
Iterator<Integer> itr = treemap.keySet().iterator();
while(itr.hasNext()) {
var key = itr.next();
System.out.println( treemap.get(key) ); // ccc#2 ee#53 a#123 d#666
}
}
}