11. 컬렉션과제네릭 이장에서는자바에서객체들을저장할수있는동적인배열공간을갖는컬렉션프레임워크 (Collection Framework) 과제네릭 (Generic) 기능에대해설명하기로하겠습니다. 자바초기버전에서는객체를저장할수있도록 Vector, Enumeration 클래스등간단한클래스만제공되었습니다. 그후 JDK 1.2 버전에서많은컬렉션인터페이스와클래스들이추가되었고이를컬렉션프레임워크라는이름으로사용하고있습니다. 이장에서는객체를저장하는다양한방법과컬렉션의종류들에대해서설명하고있습니다. Java SE 5(JDK 1.5) 버전에서는제네릭기능이도입되었습니다. 이로인해컬렉션객체에형안정성 (Type Safety) 을제공할수있게되었습니다. 이장에서는제네릭이무엇이며, 이를사용했을때의장점은무엇인지설명하고있습니다. 11장의주요내용입니다. - Set - List - Map - Iterator - Comparator - Comparable - 객체동등비교 - JDK1.1의 Collections - 제네릭과형안정성
Java 자바야놀자 11.1. Collection Framework 11.1.1. Collection 자바프로그램에서객체를객체들의모음으로관리할수있는방법제공하는클래스들을컬렉션프레임워크라고부릅니다. 그리고객체를저장할수있는자료구조클래스들을컬렉션이라고합니다. 컬렉션은배열과유사하지만데이터를저장 / 조회 / 수정 / 삭제작업을쉽게처리할수있으며, 동적인크기를갖는장점이있습니다. 이들컬렉션계열은 Collection, Set, List, Map 등의인터페이스가있으며이를구현한클래스를이용하면객체를저장할수있습니다. 자바의컬렉션프레임워크들에는배열구조 ( 객체들을배열처럼인덱스를이용하여관리 ), 리스트구조 ( 순차적접근가능 ), 맵구조 ( 객체저장시 key/value 쌍으로저장 ) 등이있습니다. 이들클래스들은기존의배열에비해높은성능을보장하며, 저장된객체들을관리할수있는다양한메서드들이포함되어있습니다. 컬렉션에저장된객체를엘리먼트 (Element) 라부르며 Set, List, Map은각각 Collection 인터페이스를상속받습니다. Collection 인터페이스는순서가없고중복을허락하는구조입니다. Set 인터페이스는순서가없고, 중복도허락하지않는구조를갖습니다. Set 관련클래스들은데이터를저장할때저장하는순서를보장하지않으며, 같은엘리먼트 (Element) 가두개이상저장될수없습니다. List는입력되는엘리먼트 (Element) 들사이에순서가정해져있으며, 입력되는순서가다르면같은데이터도저장할수있도록중복을허락하는구조를갖습니다. List 관련클래스들은엘리먼트를저장하는순서대로조회할수있으며, 같은엘리먼트 (Element) 를두개이상저장할수있습니다. Collection, Set, List, Map은모두인터페이스이기때문에직접객체를생성하지못하며구현된클래스를이용하여객체를생성합니다. 구현된클래스들중에서 Set 계열에는 HashSet클래스, List 계열에는 ArrayList와 LinkedList가주로사용됩니다. 이들인터페이스또는클래스들은 java.util 패키지에있기때문에사용하기위해서는 import 문이필요합니다. 인터페이스순서중복구현된클래스 Collection X O Set X X List O O HashSet TreeSet ArrayList LinkedList
Chapter 컬렉션과제네릭 11 Collection 인터페이스의주요메서드는다음과같습니다. 리턴타입 boolean void boolean boolean Iterator<E> boolean int Object[] <T> T[] 메서드와설명 add(e e) 컬렉션에엘리먼트를추가합니다. 만일추가되지않을경우 false 를리턴합니다. clear() 컬렉션의모든엘리먼트를삭제합니다. contains(object o) 컬렉션이주어진객체를포함하면 true 를리턴합니다. isempty() 컬렉션이비어있으면 true 를리턴합니다. iterator() 이컬렉션의 iterator 객체를반환합니다. remove(object o) 주어진엘리먼트객체를제거합니다. size() 컬렉션의엘리먼트의개수를반환합니다. toarray() 이컬렉션의모든엘리먼트들을배열로반환합니다. toarray(t[] a) 이컬렉션의모든엘리먼트들을주어진타입의배열로반환합니다. 11.1.2. Set Set 인터페이스는중복된데이터의저장을허용하지않습니다. 이미저장된엘리먼트와같은엘리먼트를저장하려고했을때 add() 메서드는 false를반환합니다. Set 인터페이스를상속받은인터페이스에는 SortedSet 인터페이스가있습니다. Set은순서를보장하지않지만 SortedSet은저장된엘리먼트들을오름차순으로정렬하여보관할수있습니다. Set 인터페이스를구현한클래스들에는 AbstractSet, ConcurrentSkipListSet, CopyOnWriteArraySet, EnumSet, HashSet, JobStateReasons, LinkedHashSet, TreeSet 클래스가있습니다. SortedSet 인터페이스를구현한클래스는 ConcurrentSkipListSet 클래스와 TreeSet 클래스가있습니다. Set 인터페이스의주요메서드는다음과같습니다. 리턴타입 boolean void boolean 메서드와설명 add(e e) 세트에엘리먼트를추가합니다. 만일추가되지않을경우 false 를리턴합니다. clear() 세트의모든엘리먼트를삭제합니다. contains(object o) 세트가주어진객체를포함하면 true 를리턴합니다.
Java 자바야놀자 boolean Iterator<E> boolean int Object[] <T> T[] isempty() 세트비어있으면 true 를리턴합니다. iterator() 이세트 iterator 객체를반환합니다. remove(object o) 세트에서주어진엘리먼트객체를제거합니다. size() 세트의엘리먼트의개수를반환합니다. toarray() 이세트의모든엘리먼트들을배열로반환합니다. toarray(t[] a) 이세트의모든엘리먼트들을주어진타입의배열로반환합니다. HashSet HashSet 클래스는해시테이블에저장된데이터를사용하기위한 Set 계열컬렉션클래스입니다. 해시테이블저장소는데이터를저장하기위해유일 (Unique) 한 Key와 Value를한세트로매핑하여저장합니다. 이때키는프로그래머가지정하는것이아니고자동으로처리됩니다. HashSet 클래스는 Set 인터페이스를구현한클래스이므로저장된엘리먼트의순서를보장하지않습니다. 저장된엘리먼트는 hashcode 로구분되어저장되기때문에 index를통해엘리먼트에접근할수없습니다. 이렇게 HashSet 클래스는해싱 (hashing) 기술을사용하여엘리먼트들을저장하게됩니다. 해싱을이용하면많은양의데이터를관리할때추가 / 삭제 / 검색등에있어서순차적으로 데이터를관리하는것보에비하여속도가향상됩니다. HashSet 은정렬기능을지원하지 않습니다. 정렬기능을갖는 Set 계열클래스에는 TreeSet 클래스가있습니다. 다음프로그램은 HashSet 클래스를사용하여엘리먼트를저장하는 Set 의사용예를보인 것입니다. HashSetExample.java 1: import java.util.*; 2: 3: public class HashSetExample { 4: 5: public static void main(string args[]) { 6: 7: Set set = new HashSet(); 8: 9: set.add("three"); 10: set.add("one"); 11: set.add("two");
Chapter 컬렉션과제네릭 11 12: set.add("four"); 13: set.add("five"); 14: set.add(new Integer(4)); 15: boolean isadded = set.add("five"); 16: 17: System.out.println(set); 18: System.out.println(isAdded); 19: 20: System.out.println(set.size()); 21: 22: set.remove("two"); 23: System.out.println(set); 24: 25: set.clear(); 26: System.out.println(set); 27: 28: if (set.isempty()) { 29: System.out.println("set is Empty"); 30: } 31: } 32: } 코드를작성하면 Set 과관련된부분에서경고메시지를볼수있습니다. Set 인터페이스와 HashSet 클래스에 Generic 기능을사용하지않아형안정성을제공하지않기때문에보 이는경고메시지입니다. 아직 Generic 을배우지않았으므로무시하고넘어가세요. TreeSet TreeSet 클래스는 Set 인터페이스와 SortedSet 인터페이스를구현한클래스이기때문에정렬기능을지원합니다. 클래스이름에서알수있듯이트리형태로데이터를관리합니다. 엘리먼트가저장될때에오름차순으로정렬되어저장됩니다. 정렬기능을포함하기때문에검색속도가떨어지지는않습니다. 다음프로그램은 TreeSet 클래스를사용하여엘리먼트를저장하는 Set 의사용예를보인 것입니다. TreeSetExample.java 1: import java.util.treeset; 2: 3: public class TreeSetExample { 4: 5: public static void main(string[] args) { 6: TreeSet<String> ts = new TreeSet<String>(); 7:
Java 자바야놀자 8: ts.add("hello"); 9: ts.add("java"); 10: ts.add("aaa"); 11: ts.add("computer"); 12: 13: for(string str : ts) { 14: System.out.print(str + "\t"); 15: } 16: } 17: } 예제는저장된데이터를알파벳오름차순으로정렬된순서로출력합니다. TreeSet 은저장 한데이터를정렬해서보관합니다. 정렬방법변경과관련된내용은 Comparable 인터페 이스와 Comparator 인터페이스를설명할때언급됩니다. TreeSet 클래스뒤에 <String> 이붙은것은 Generic 기능을사용한것입니다. TreeSet 에 String 데이터만저장하겠다는의미입니다. String 이아닌다른객체를 add() 메서드로추 가할수없습니다. Generic 에관해서는이장의후반부부분에서다시설명됩니다. 11.1.3. List List 인터페이스는저장된엘리먼트들의순서를보장합니다. 그리고동일엘리먼트를여러개저장할수있습니다. List 인터페이스를구현한주요클래스들은 AbstractList, AbstractSequentialList, ArrayList, AttributeList, CopyOnWriteArrayList, LinkedList, RoleList, RoleUnresolvedList, Stack, Vector 등이있습니다. List 계열클래스들은순서를가질수있습니다. 엘리먼트가저장되었을때엘리먼트의위치를알수있다는것입니다. 엘리먼트의저장된위치는 0부터시작합니다. List 인터페이스의주요메서드는다음과같습니다. 리턴타입 boolean boolean void boolean E 메서드와설명 add(e e) 리스트에엘리먼트를추가합니다. 만일추가되지않을경우 false 를리턴합니다. add(int index, E e) 이리스트의주어진 index 위치에엘리먼트를추가합니다. 만일추가되지않을경우 false 를리턴합니다. clear() 리스트의모든엘리먼트를삭제합니다. contains(object o) 리스트가주어진객체를포함하면 true 를리턴합니다. get(int index) 리스트에서주어진 index 위치의엘리먼트를반환합니다.
Chapter 컬렉션과제네릭 11 int boolean Iterator<E> ListIterator<E> E boolean E int Object[] <T> T[] indexof(object o) 리스트가주어진객체의위치를반환합니다. 엘리먼트를찾지못할경우 -1 을반환합니다. isempty() 리스트가비어있으면 true 를리턴합니다. iterator() 이리스트의 iterator 객체를반환합니다. listiterator() 이리스트의 list iterator 객체를반환합니다. remove(int index) 리스트에서주어진위치의엘리먼트를제거합니다. remove(object o) 주어진엘리먼트객체를제거합니다. set(int index, E element) 리스트의 index 위치엘리먼트를주어진 element 로대체합니다. size() 컬렉션의엘리먼트의개수를반환합니다. toarray() 이컬렉션의모든엘리먼트들을배열로반환합니다. toarray(t[] a) 이컬렉션의모든엘리먼트들을주어진타입의배열로반환합니다. ArrayList ArrayList 클래스는엘리먼트가저장되기위한공간을동적으로할당하는배열구조를갖는 List 계열컬렉션클래스입니다. 자바의배열은고정된크기를지정해야하며, 배열의크기를늘리거나줄이기위해서는새로운배열을생성해야합니다. 그리고배열은중간에새로운데이터를삽입하거나삭제할때삽입 / 삭제가발생하는인덱스뒤의데이터들을처리해줘야하므로매우불편합니다. ArrayList를사용하면엘리먼트가저장되는크기를동적으로할당할수있어필요에따라자동으로크기가늘어나거나줄어듭니다. 뿐만아니라엘리먼트를추가하거나삭제하는다양한메서드를제공합니다. 다음프로그램은 ArrayList 의사용예를보인것입니다. 여러메서드들의사용에주의해서 보시기바랍니다. ArrayListExample.java 1: import java.util.*; 2: 3: public class ArrayListExample { 4: 5: public static void main(string[] args) { 6: List list = new ArrayList(); 7: 8: list.add("one");
Java 자바야놀자 9: boolean a=list.add("second"); 10: list.add("3rd"); 11: list.add(new Integer(4)); 12: list.add(new Float(5.0f)); 13: boolean b=list.add("second"); 14: list.add(new Integer(4)); 15: list.add("second"); 16: 17: System.out.println(a); 18: System.out.println(b); 19: System.out.println(list); 20: 21: list.remove(0); 22: System.out.println(list); 23: 24: Object o=list.get(1); 25: System.out.println(o); 26: 27: int i=list.indexof("second"); 28: System.out.println("second = "+i); 29: 30: list.clear(); 31: if (list.isempty()) { 32: System.out.println("list is Empty"); 33: } 34: } 35: } LinkedList LinkedList 클래스는 Java SE 5(JDK 1.5) 버전에추가된 Queue 인터페이스를구현하였고, Java SE 6(JDK 1.6) 버전에서는 Deque 인터페이스를구현하면서많은새로운메서드들이추가되었습니다. LinkedList 클래스는 add() 메서드외에 addfirst(), addlast() 메서드를통해서리스트의맨처음이나마지막에엘리먼트를추가할수있습니다. LinkedList는 Queue 인터페이스를구현했기때문에 peek() 메서드와 poll() 메서드등을사용할수있습니다. peek() 메서드는데이터를조회한후에리스트에서데이터를삭제하지않지만, poll() 메서드는데이터를조회한후에리스트로부터데이터를삭제합니다. LinkedList는 Deque 인터페이스의 push() 와 pop() 메서드를사용할수도있습니다. 다음프로그램은 LinkedList 예입니다. LinkedListExample.java 1: import java.util.linkedlist; 2:
Chapter 컬렉션과제네릭 11 3: public class LinkedListExample { 4: public static void main(string[] args) { 5: LinkedList<String> list = new LinkedList<String>(); 6: 7: list.add("hello"); 8: list.add("java"); 9: list.add("banana"); 10: list.addfirst("apple"); 11: list.addlast("zoo"); 12: 13: System.out.println("list data : " + list); 14: 15: list.remove(); //head 엘리먼트삭제 16: System.out.println("list data after remove() : " + list); 17: 18: list.remove(2); //2번인덱스엘리먼트삭제 19: System.out.println("list data after remove(2) : " + list); 20: 21: list.set(1, "new element"); //1번째엘리먼트변경 22: System.out.println("list data after set() : " + list); 23: 24: String str1 = list.peek(); // 엘리먼트조회 25: System.out.println("str1 : " + str1); 26: System.out.println("list data after peek() : " + list); 27: 28: String str2 = list.poll(); // 엘리먼트조회후삭제 29: System.out.println("str2 : " + str2); 30: System.out.println("list data after poll() : " + list); 31: } 32: } 11.1.4. Map Map 인터페이스는저장한엘리먼트를대체키 (key) 값으로지정하여관리할수있는구조입니다. 즉, 엘리먼트들을 key/value를하나의쌍으로저장하여관리할수있는컬렉션입니다. 같은키 (key) 로엘리먼트를두개이상저장할수는없지만키 (key) 가다르면같은엘리먼트를저장할수있습니다. Map 인터페이스를구현한클래스에는 AbstractMap,
Java 자바야놀자 Attributes, AuthProvider, ConcurrentHashMap, ConcurrentSkipListMap, EnumMap, HashMap, Hashtable, IdentityHashMap, LinkedHashMap, PrinterStateReasons, Properties, Provider, RenderingHints, SimpleBindings, TabularDataSupport, TreeMap, UIDefaults, WeakHashMap 클래스들이있습니다. Map 인터페이스의주요메서드는다음과같습니다. 리턴타입 void boolean boolean Set<Map. Entry <K, V>> V boolean Set<K> V void V int Collection<V> 메서드와설명 clear() map 에저장되어있는모든엘리먼트를삭제합니다. containskey(object key) 해당하는키를포함하고있으면 true 를반환합니다. containsvalue(object value) 해당하는객체를포함하고있으면 true 를반환합니다. entryset() Map.Entry 객체를엘리먼트로갖는 Set 객체를반환합니다. get(object key) 키를갖는엘리먼트를반환합니다. 키와매핑되는엘리먼트가없으면 null 을반환합니다. isempty() 키 / 값매핑이비어있으면 true 반환합니다. keyset() map 저장된키들을 Set 객체로반환합니다. put(k key, V value) 키 / 값매핑을저장합니다. putall(map<? extends K,? extends V> m) map 에저장된모든키 / 값매핑을현재맵에복사합니다. remove(object key) 해당키를갖는키 / 값매핑을삭제합니다. size() 키 / 값매핑개수를반환합니다. values() map 이포함하고있는값을 Collection 객체로반환합니다. HashMap HashMap 클래스는 Map 계열컬렉션클래스입니다. 이클래스도엘리먼트를저장하기 위해해시테이블을사용합니다. 엘리먼트를저장할때키 / 값을매핑하여저장합니다. 다음프로그램은 HashMap 클래스의사용예를보인것입니다. HashMapExample.java 1: import java.util.date; 2: import java.util.hashmap;
Chapter 컬렉션과제네릭 11 3: import java.util.map; 4: import java.util.set; 5: 6: public class HashMapExample { 7: public static void main(string[] args) { 8: Map maps = new HashMap(); 9: 10: String s1 = new String(" 홍길동 "); 11: maps.put("name", s1); 12: maps.put("hiredate", new Date()); 13: maps.put("salary", 20000); 14: 15: System.out.println(maps); 16: 17: System.out.println(); 18: System.out.println(maps.get("hiredate")); 19: System.out.println(maps.get("salary")); 20: System.out.println(maps.get("name")); 21: 22: System.out.println(); 23: //map 안의엘리먼트를 entryset() 메서드를이용하여조회 24: Set<Map.Entry<String, Object>> s = maps.entryset(); 25: for(map.entry<string, Object> me : s) { 26: System.out.println(me.getKey() + " : " + me.getvalue()); 27: } 28: 29: System.out.println(); 30: //keyset() 메서드로 map 키를리턴받고 get(key) 메서드로조회 31: Set<String> ss = maps.keyset(); 32: for(string key : ss) { 33: System.out.println(key + " :: " + maps.get(key)); 34: } 35: } 36: }
Java 자바야놀자 TreeMap TreeMap 클래스는 Map 인터페이스와 SortedMap 인터페이스를구현한클래스입니다. 정렬기능이지원되는 Map 계열컬렉션클래스입니다. 이클래스도엘리먼트를저장할때키 / 값을매핑하여저장합니다. 데이터를저장할대오름차순으로저장되며, 데이터조회시 HashSet에비하여더빠른성능을보장합니다. 다음프로그램은 TreeMap 클래스의사용예를보인것입니다. TreeMapExample.java 1: import java.util.map; 2: import java.util.set; 3: import java.util.treemap; 4: 5: public class TreeMapExample { 6: 7: public static void main(string[] args) { 8: 9: Map<String, Integer> accounts = new TreeMap<String, Integer>(); 10: 11: accounts.put(" 홍길동 ", 10000); 12: accounts.put(" 이순신 ", 50000); 13: accounts.put(" 정준수 ", 90000); 14: accounts.put(" 허현수 ", 70000); 15: 16: System.out.println(accounts); 17: 18: System.out.println(); 19: System.out.println(" 허현수 : " + accounts.get(" 허현수 ")); 20: 21: System.out.println(); 22: Set<Map.Entry<String, Integer>> s = accounts.entryset(); 23: for(map.entry<string, Integer> me : s) { 24: System.out.println(me.getKey() + " : " + me.getvalue()); 25: } 26: 27: System.out.println(); 28: Set<String> ss = accounts.keyset(); 29: for(string key : ss) { 30: System.out.println(key + " :: " + accounts.get(key)); 31: } 32: } 33: }
Chapter 컬렉션과제네릭 11 11.1.5. Iterator 컬렉션에저장되어있는엘리먼트를검색하는절차를 Iteration이라합니다. Iterator 인터페이스는컬렉션에저장된엘리먼트를순차적으로하나씩접근하고자할때사용됩니다. JDK 1.2 이전버전에는 Enumeration 인터페이스를사용하였습니다. Iterator 인터페이스는저장된엘리먼트를제거할수있는기능이추가되었습니다. Set 계열에서는 Collection 인터페이스에정의된 iterator() 메서드를통하여 Iterator 객체를반환받아사용하며, List를사용했을때는 listiterator() 메서드를통해 ListIterator 객체를반환받아사용할수있습니다. ListIterator를사용하면엘리먼트를앞또는뒤의원하는방향으로검색할수있습니다. Iterator는인덱스를사용해서엘리먼트에접근하는것이아닙니다. 저장된데이터를하나씩순서대로접근하면서얻어오거나삭제할수있습니다. 다음은 Iterator 인터페이스의주요메서드입니다. boolean hasnext() : 다음엘리먼트를포함하고있으면 true를반환합니다. E next() : 다음엘리먼트를반환합니다. void remove() : iterator에의해반환된마지막엘리먼트를삭제합니다. 다음프로그램은 Iterator 를사용하여 Set 계열클래스로부터데이터를검색하는예를보 인것입니다. IteratorExample.java 1: import java.util.hashset; 2: import java.util.iterator; 3: import java.util.set; 4: 5: public class IteratorExample { 6: 7: public static void main(string args[]) { 8: 9: Set set = new HashSet(); 10: set.add("three"); 11: set.add("two"); 12: set.add("four"); 13: set.add("five"); 14: set.add(new Integer(4)); 15: 16: Iterator it = set.iterator(); 17: while( it.hasnext() ) { 18: System.out.println(it.next()); 19: } 20: } 21: }
Java 자바야놀자 11.2. 객체비교 자바의기본형변수의동등비교는 == 또는!= 연산자를이용하면됩니다. 그리고크기비교는 >, >=, <, <= 등의연산자를이용하면됩니다. 그러나비교대상이객체일경우에는이등연산자를이용해비교했을경우객체의값을이용해비교할수없습니다. 이번절에서는객체들의동등비교및크기비교를위해무엇이필요하고어떻게비교하는지알아보겠습니다. 11.2.1. 객체동등비교 Set계열클래스에는같은객체가두개이상들어갈수없다고했습니다. Set 계열클래스에엘리먼트를 add 할경우엘리먼트의 hashcode() 메서드와 equals() 메서드를통해엘리먼트의동등비교가이루어집니다. 개발자는새로운클래스를정의할때객체들의동등비교가될수있도록해주어야합니다. equals() 와 hashcode() 메서드는 Object 클래스에있는메서드입니다. 두메서드의원형은다음과같습니다. public boolean equals(object obj) public int hashcode() equals() 메서드에서는객체의멤버를비교하여 boolean형값을리턴하도록구현해야합니다. hashcode() 메서드에서는멤버변수의내용이같으면동일한리턴값을갖도록해야합니다. 물론 haskcode() 메서드는항상같은값을리턴하면안됩니다. 즉 return 1; 은안된다. 는뜻입니다. hashcode() 메서드의리턴값이다르면두객체는다른객체임을보장합니다. 그러나 hashcode() 메서드의리턴값이같더라도두객체가같다는것을보장하지는않습니다. hashcode() 는동등비교에사용할멤버변수들의해시코드값을 ^(XOR) 해서리턴하도록구현할수있으며, hashcode() 는객체동등비교에 equals() 와같이사용됩니다. Java SE 7 버전 API 에서제공하는클래스들중에서 HashMap 클래스의 put() 메서드구 현부를살펴보면다음과같습니다. 객체동등비교를위해 hashcode() 메서드를통해객 체동등비교를수행한다음 equals() 메서드를통해다시객체동등비교를수행하는것을 알수있습니다. HashSet 의 add() 메서드는 HashMap 클래스의 put() 메서드를호출합 니다. 그래서 HashMap 클래스의 put() 메서드를보여준것입니다. 386: public V put(k key, V value) { 387: if (key == null) 388: return putfornullkey(value); 389: int hash = hash(key.hashcode()); 390: int i = indexfor(hash, table.length);
Chapter 컬렉션과제네릭 11 391: for (Entry<K,V> e = table[i]; e!= null; e = e.next) { 392: Object k; 393: if (e.hash == hash && ((k = e.key) == key key.equals(k))) { 394: V oldvalue = e.value; 395: e.value = value; 396: e.recordaccess(this); 397: return oldvalue; 398: } 399: } 400: 401: modcount++; 402: addentry(hash, key, value, i); 403: return null; 404: } 다음은 Dog 클래스에 equals() 메서드와 hashcode() 메서드를구현한예입니다. Dog.java 1: public class Dog extends Object { 2: String dogid; 3: String dogname; 4: 5: public Dog(String dogid, String dogname) { 6: super(); 7: this.dogid = dogid; 8: this.dogname = dogname; 9: } 10: 11: public String tostring() { 12: return dogid + " : " + dogname; 13: } 14: 15: public boolean equals(object obj) { 16: if (this == obj) return true; 17: if (obj == null) return false; 18: 19: // 상속관계있을경우데이터가같더라도다른객체 20: if (getclass()!= obj.getclass()) return false; 21: 22: Dog d = (Dog)obj; 23: if(this.dogid.equals(d.dogid) && this.dogname.equals(d.dogname) ) { 24: return true; 25: }else { 26: return false; 27: } 28: } 29: 30: public int hashcode() { 31: return dogid.hashcode() ^ dogname.hashcode(); 32: } 33: 34: }
Java 자바야놀자 다음코드는앞의 Dog 클래스의인스턴스를생성한다음 HashSet 에넣어서같은객체가 들어가는지확인하는클래스입니다. ObjectEqualsExample.java 1: import java.util.hashset; 2: import java.util.set; 3: 4: public class ObjectEqualsExample extends Object { 5: public static void main(string[] args) { 6: Dog d1 = new Dog("1234567890", " 바둑이 "); 7: Dog d2 = new Dog("1234567890", " 바둑이 "); 8: System.out.println(d1.equals(d2)); 9: System.out.println(d1 == d2); 10: System.out.println(d1); 11: 12: Set<Dog> set = new HashSet<Dog>(); 13: System.out.println("d1 객체를 set에저장 : " + set.add(d1)); 14: System.out.println("d2 객체를 set에저장 : " + set.add(d2)); 15: } 16: } hashcode() 메서드와 equals() 메서드는이클립스에서쉽게작성할수있습니다. 소스코드창에서오른쪽마우스버튼을클릭하고 Source -> Generate hashcode and equals... 를선택하여나타난창에서객체동등비교에사용할멤버변수들을체크한다음 [OK] 버튼을클릭하면자동으로 hashcode() 메서드와 equals() 메서드가추가됩니다. 11.2.2. java.lang.comparable Set 계열클래스에는 TreeSet, 그리고 Map 계열클래스에는 TreeMap 클래스를이용하면 저장되는엘리먼트들을정렬시킬수있습니다. 그러나 List 계열클래스에는 Tree 구조를 갖는클래스가존재하지않습니다. List 계열컬렉션을정렬시키기위해서 Collections 클
Chapter 컬렉션과제네릭 11 래스의 sort() 메서드를이용할수있습니다. 그런데 TreeSet, TreeMap 에저장되는엘리먼트클래스들이나, sort() 메서드의인자로전달되는 List에저장되어있는 sort의대상이되는클래스는 java.lang.comparable 인터페이스를구현한클래스여야합니다. Comparable 인터페이스를구현할때에는 compareto() 메서드를재정의해야합니다. Collections 클래스의 sort() 메서드원형은다음과같습니다. static <T extends Comparable<? super T>> void sort(list<t> list) static <T> void sort(list<t> list, Comparator<? super T> c) 첫번째 sort() 메서드는메서드인자로 List객체를받습니다. 그런데 List에저장된객체들이 Comparable 인터페이스를구현한클래스여야합니다. 두번째 sort() 메서드는인자를두개받습니다. List에저장된객체가 Comparable 인터페이스를구현하지않았을경우비교기 (Comparator 인터페이스를구현한클래스 ) 를지정하여두객체의크기비교를할수있습니다. 다음코드는 ArrayList 에저장되어있는엘리먼트를 Collections.sort(List<T> list) 메서드 를이용하여정렬하는예입니다. ArrayList 에저장되는 Tiger 클래스는 Comparable 인터 페이스를구현한클래스입니다. Tiger.java 1: public class Tiger implements Comparable<Tiger>{ 2: String name; 3: 4: Tiger(String name) { 5: this.name = name; 6: } 7: 8: public int compareto(tiger obj) { 9: return this.name.compareto(obj.name); 10: } 11: } SortExample.java 1: import java.util.arraylist; 2: import java.util.collections; 3: import java.util.list; 4: 5: public class SortExample { 6: public static void main(string[] args){ 7: 8: List<Tiger> list = new ArrayList<>(); 9: 10: list.add(new Tiger(" 라이거 ")); 11: list.add(new Tiger(" 백호 ")); 12: list.add(new Tiger(" 한라산 "));
Java 자바야놀자 13: list.add(new Tiger(" 백두 ")); 14: 15: Collections.sort(list); 16: 17: for(tiger t : list) { 18: System.out.println(t.name); 19: } 20: } 21: } 11.2.3. java.util.comparator Collections.sort() 메서드를이용하여리스트내의엘리먼트들을정렬시킬수있고, TreeSet과 TreeMap은엘리먼트를저장할때자동으로정렬시켜저장한다고했습니다. 리스트에저장되는객체또는 TreeSet, TreeMap에저장하는객체들이프로그래머가정의하는클래스일경우에무엇을기준으로정렬이되어야할지생각해야합니다. 이처럼엘리먼트들을정렬해야할상황이발생하면프로그래머는클래스를정의할때 Comparable 인터페이스를구현하여 compareto() 메서드에서객체의크기비교를수행할수있도록하면된다고했습니다. 만일, TreeSet 또는 TreeMap에저장되는엘리먼트클래스를수정할수없는상황이거나 List에저장된객체들의클래스를수정할수없는상황이있습니다. 그럴경우에는 java.util.comparable 인터페스를구현하도록클래스를수정할수없으므로, 정렬을위한크기비교를대신해주는비교기 (Comparator) 라는것이필요합니다. 앞에서설명한 Comparable 인터페이스는 java.lang 패키지에있습니다. 그러나 Comparable 인터페이스는 java.util 패키지에정의되어있기때문에 import 문장이필요합니다. 비교기는 Comparator 인터페이스를구현한클래스를이용해객체들사이의크기비교를해줄수있습니다. Comparator 인터페이스의 compare() 메서드는매개변수로전달된두값을비교하여같은경우에는 0을리턴하며, 첫번째값이두번째값보다큰경우에는양수를, 그렇지않은경우음수를리턴하게됩니다. 만일두매개변수가서로다른유형일경우에는 ClassCastException이라는예외를던집니다. 그러므로프로그래머는컬렉션계열클래스중에서 TreeSet이나 TreeMap에저장할엘리먼트클래스를선언할경우또는 Collections 클래스의 sort(list<t> list, Comparator<? super T> c) 메서드를사용할경우에는 Comparator 인터페이스를구현하여 compare() 메서드를재정의한비교기클래스를만들어야합니다.
Chapter 컬렉션과제네릭 11 다음프로그램은 Employee 의이름을기준으로정렬시키기위하여 Comparator 인터페이 스를구현한예입니다. Employee.java 1: public class Employee { 2: String name; 3: int salary; 4: 5: public Employee() {} 6: 7: public Employee(String name, int salary) { 8: super(); 9: this.name = name; 10: this.salary = salary; 11: } 12: 13: public String tostring() { 14: return name + ":" + salary; 15: } 16: } EmployeeComparator.java 1: import java.util.comparator; 2: 3: public class EmployeeComparator implements Comparator<Employee> { 4: 5: public int compare(employee obj1, Employee obj2) { 6: return obj1.name.compareto(obj2.name); 7: } 8: } ComparatorExample.java 1: import java.util.treeset; 2: 3: public class ComparatorExample { 4: public static void main(string[] args) { 5: Employee e1 = new Employee(" 홍길동 ", 20000); 6: Employee e2 = new Employee(" 김진숙 ", 30000); 7: Employee e3 = new Employee(" 허현정 ", 70000); 8: Employee e4 = new Employee(" 이순신 ", 40000); 9: 10: TreeSet<Employee> list = new TreeSet<Employee>(new EmployeeComparator()); 11: list.add(e1); 12: list.add(e2); 13: list.add(e3); 14: list.add(e4); 15: 16: for(employee s : list) { 17: System.out.println(s); 18: } 19: } 20: }
Java 자바야놀자 실행결과를보면입력한 Employee 엘리먼트들이이름을기준으로정렬된것을확인할수있습니다. Employee를 salary 순으로정렬시키고싶으면 EmployeeComparator 클래스의 6라인을아래와같이바꿔서실행해보세요. EmployeeComparator.java 6: return obj1.salary - obj2.salary; Comparator 클래스를이용하면기존객체를수정하지않고도여러가지정렬방법을정할수있습니다. Comparable 인터페이스를구현하는것이객체의기본정렬방법을결정하는것이라면 Comparator 인터페이스를구현하는것은객체의추가정렬방법을결정할수있는것입니다. 11.2.4. JDK 1.1 이전의 Collections Set계열과 List 계열컬렉션클래스들외에도 JDK 1.1 이전의컬렉션클래스들이있습니다. 과거에작성된소스코드들은객체를저장할때 Vector 클래스를주로사용했었습니다. 요즘은성능상의이유로 Set과특별한경우가아니면 List 계열컬렉션을사용할것을권장합니다. Vector : List 인터페이스를구현한클래스입니다. Vector 클래스는확장가능한객체배열을구현합니다. 배열과마찬가지로정수인덱스를사용하여액세스할수있는구성요소가들어있습니다. 그러나 Vector를만든후항목을추가하거나제거할수있도록필요에따라 Vector의크기를늘리거나줄일수있습니다 Stack : Vector 클래스의하위클래스입니다. Stack 클래스는 LIFO (Last-In-First-Out) 스 택을나타냅니다. empty(), push(), pop(), peek() 메서드를지원합니다. Hashtable : Map인터페이스를구현한클래스입니다. 이클래스는키를값에매핑하는해시테이블을구현합니다. null 이외의객체는키또는값으로서사용할수있습니다. 해시테이블에서객체를성공적으로저장및검색하려면키로사용되는객체가 hashcode 메서드와 equals 메서드를구현해야합니다. Enumeration : Vector, Stack, Hashtable 등에서엘리먼트를조회하기위한클래스입니 다. Enumeration 인터페이스를구현하는객체는한번에하나씩일련의요소를생성합니 다. nextelement() 메서드를연속적으로호출하면연속되는일련의요소가반환됩니다.
Chapter 컬렉션과제네릭 11 다음프로그램은 Person 객체를 Vector 클래스에저장하는예입니다. Person.java 1: public class Person { 2: private String name; 3: private int age; 4: 5: public Person(String name, int age) { 6: this.name = name; 7: this.age = age; 8: } 9: 10: public String getname() { 11: return name; 12: } 13: public int getage() { 14: return age; 15: } 16: } VectorExample.java 1: import java.util.*; 2: 3: public class VectorExample { 4: 5: public static void main(string[] args) { 6: Vector v = new Vector(); 7: 8: v.addelement(new Person(" 홍길동 ", 29)); 9: v.addelement(new Person(" 이순신 ", 30)); 10: v.addelement(new Person(" 강감찬 ", 65)); 11: 12: System.out.println(v); 13: Person p = (Person)v.elementAt(2); 14: System.out.println(p.getName()); 15: v.remove(1); 16: System.out.println(v); 17: System.out.println(v.size()); 18: System.out.println(v); 19: } 20: }
Java 자바야놀자 11.3. 제네릭과형안정성 (Generic & Type Safety) 11.3.1. 제네릭클래스 (generic class) 제네릭클래스란클래스선언에유형매개변수가들어있는클래스를뜻합니다. 제네릭은자바 5.0에서도입되었습니다. 제네릭이도입되기전까지는모든컬렉션이파라미터로 Object 유형의객체를받기때문에어떤객체를넣든컴파일러는이를상관하지않았습니다. 예를들면 ArrayList에야구공 (Ball) 을넣을수도있고, 자동차 (Car) 를넣을수도있었습니다. 그러나반대로컬렉션에서객체를빼내올경우에는리턴유형이 Object이기때문에객체를넣기전의상태로되돌리기위해서는반드시형변환이필요했었습니다. 그러나제네릭기능이도입된이후에는제네릭기능으로인해컬렉션에원하지않는객체가저장되는것을들어가거나반대로꺼낼때저장된객체유형으로형변환해야하는것에대해신경쓰지않아도됩니다. 예를들어 ArrayList<Student> 이라고하면 ArrayList에는 Student객체만넣을수있고, 객체를꺼낼때도 Student 레퍼런스로나오게할수있습니다. 제네릭을사용하기위해서는컴파일러버전이 5.0이상이어야하며, 특정유형의변수를넣는컬렉션을선언하기위해서는다음과같이클래스이름뒤에 < > 기호를사용하여컬렉션에넣을유형을선언해야합니다. 제네릭클래스를사용하기위해서 API 문서를참조할수있습니다. 다음은컬렉션중에서가장흔하게쓰이는 ArrayList 클래스의클래스선언부를나타내고있습니다. public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable 위의클래스선언부의 <E> 는엘리먼트 (Element) 의약어입니다. 컬렉션에저장하거나컬 렉션에서리턴할엘리먼트의유형 (type) 입니다. ArrayList 에서유형매개변수를사용하기위해서는클래스이름뒤에 E 대신에저장된엘 리먼트의유형을적으면되는데다음과같이선언하면 ArrayList 에는 String 객체만넣고 뺄수있게됩니다. ArrayList<String> mylist = new ArrayList<String>(); Java SE 7(JDK 1.7) 부터는다음코드와같이 r-value 에해당하는부분에제네릭유형을
Chapter 컬렉션과제네릭 11 써주지않아도됩니다. 이를 Diamond operation 이라고부릅니다. ArrayList<String> mylist = new ArrayList<>(); 앞의코드는컴파일러에의해서다음과같은식으로해석됩니다. public class ArrayList<String> extends AbstractList<String>... { public boolean add(string o) { //... } 다음은 ArrayList 에 Student 유형의변수를넣는컬렉션을선언한예입니다. ArrayList<Student> mylist = new ArrayList<Student>(); 다음은제네릭기능을사용한예입니다. GenericExample.java 1: import java.util.arraylist; 2: 3: public class GenericExample { 4: 5: public static void main(string[] args) { 6: ArrayList<String> lists=new ArrayList<>(); 7: 8: lists.add("first"); 9: lists.add("second"); 10: lists.add("third"); 11: // list.add(new Integer(4)); //String 이아닌다른객체는저장못함 12: // list.add(new Float(5.0f)); //String 이아닌다른객체는저장못함 13: 14: String s1 = lists.get(0); // 형변환하지않아도됨 15: System.out.println(s1); 16: 17: for(string s : lists) { 18: System.out.println(s); 19: } 20: } 21: }
Java 자바야놀자 코드에서 ArrayList 선언시 String 만올수있도록설정하였습니다. 그러므로 String 아닌 다른유형을넣으려고하면오류가발생하는것을알수있습니다. 제네릭기능을사용하 면컬렉션에들어있는엘리먼트를꺼내올때에형변환을하지않아도됩니다. 11.3.2. 제네릭메서드 (generic method) 제네릭메서드는메서드선언에유형매개변수가포함되어있는메서드를뜻합니다. 메서 드에서는클래스선언부에서정의된유형매개변수를사용하는방법과클래스선언부에서 쓰이지않은유형매개변수를사용하는방법이있습니다. 다음은클래스선언부에서정의된유형매개변수를사용하는방법입니다. 클래스를정의할 때 'E' 를사용하였기때문에 add() 메서드에서도 'E' 를사용할수있습니다. public class ArrayList<E> } extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable { public boolean add(e o) { } // //... 다음은클래스선언부에서쓰이지않는유형매개변수를사용하는방법입니다. public <T extends Animal> void dosomething(arraylist<t> list)
Chapter 컬렉션과제네릭 11 11.4. Typesafe enum 11.4.1. enum enum 은 Java SE 5(JDK 1.5) 버전에추가된데이터유형입니다. enum 은수년간메서드 가없는변수만선언된클래스를이용하여 enum 값으로사용되어온 public static final int 선언과상당히유사합니다. 클래스를이용하여열거형을표현했을때와비교하여가장 크고확실하게개선된점은 typesafe 입니다. 클래스의 int 형상수변수와는달리 enum 유 형으로선언된변수의값은다른유형의위치에사용할수없습니다. 그이유는기존의방 법으로열거형을표현했을때에선언된상수변수들이모두 int 형이라면변수의이름이 다르더라도컴파일러에게는모든것이똑같아보이기때문입니다. 아주드물게예외가있 긴하지만이경우에도 enum 과유사한모든 int 구성을 enum 인스턴스로교체해야합니 다. enum 은여러가지추가기능을제공합니다. 유틸리티클래스인 EnumMap 및 EnumSet 은특히 enum 에최적화된표준컬렉션클래스들입니다. 컬렉션에 enum 만포함되는것 을알고있다면 HashMap 또는 HashSet 대신이러한특정컬렉션을사용해야합니다. 대부분의경우코드에서모든 public static final int 를 enum 으로교체할수있습니다. enum 은비슷 (Comparable) 하고, 내부클래스 ( 또는내부 enum) 일지라도이에대한참조 가똑같아보이므로정적으로가져올수있습니다. 유의해야할점은 enum 을비교할때 선언되는순서가 C 언어에서처럼서수값을나타내지않는다는것입니다. enum 의선언은클래스선언과유사합니다. 패키지를선언할수있으며, 접근제한모드를 가질수있습니다. 열거항목들은콤마 (,) 로구분하여나열합니다. 그리고열거항목의마 지막에세미콜론 (;) 을입력하는것을잊지마세요. 선언예 : public enum Coin { PENNY, NICKEL, DIME, QUARTER; } 사용예 : Coin coin = Coin.DIME; 다음코드는열거형을선언한예입니다. 패키지가선언되어있음에유의하세요. 그리고접 근모드가 public 입니다. 상황에따라접근모드는달라질수있을것입니다. 그리고패키
Java 자바야놀자 지이름에서 enum 뒤에 _ 문자가포함되어있는이유는패키지이름에키워드는포함될 수없기때문이고, enum 과관련된예제를나타내기위해서 enum_ 라고표현한것입니다. Coin.java 1: public enum Coin { 2: PENNY, 3: NICKEL, 4: DIME, 5: QUARTER; 6: } 열거형을반드시별도의파일로작성할필요는없습니다. 한클래스서만사용된다면클래 스블록안에내부열거형으로선언할수도있습니다. 다음코드는위의열거형을사용한클래스입니다. EnumBasicExample.java 1: public class EnumBasicExample { 2: public static void main(string[] args) { 3: Coin coin = Coin.DIME; 4: 5: switch(coin) { 6: case PENNY : 7: System.out.println("1 센트동전입니다."); 8: break; 9: case NICKEL : 10: System.out.println("5 센트동전입니다."); 11: break; 12: case DIME : 13: System.out.println("10 센트동전입니다."); 14: break; 15: case QUARTER : 16: System.out.println("25 센트동전입니다."); 17: break; 18: default : 19: break; 20: } 21: } 22: } 앞에서도언급했지만자바의열거형은 C 언어처럼서수를대신하여사용할수없습니다. 위 코드에서 coin 에숫자값을할당할수없으며마찬가지로 switch 문에열거형일경우 case 문에서정수값으로비교할수없습니다.
Chapter 컬렉션과제네릭 11 11.4.2. "Hidden" 정적메서드 작성한모든 enum 선언에는 values() 메서드와 valueof() 메서드가표시됩니다. 이두가 지메서드는 Enum 클래스자체가아니라 enum 하위클래스에대한정적메서드이기때 문에 java.lang.enum 에대한 javadoc 에는표시되지않습니다. 첫번째 values() 는 enum 에가능한모든값의배열을반환합니다. 두번째 valueof() 는제공된문자열에대한 enum 을반환하는데, 원본코드선언과똑같 이일치해야합니다. 다음코드는 enum 의정적메서드예입니다. EnumHiddenMethodExample.java 1: public class EnumHiddenMethodExample { 2: public static void main(string[] args) { 3: Coin[] coins = Coin.values(); 4: 5: System.out.println(coins); 6: for(coin c : coins) { 7: System.out.println(c.toString()); 8: } 9: 10: Coin c1 = Coin.valueOf("QUARTER"); 11: System.out.println("c1 의값 : " + c1); 12: System.out.printf("c1 의값 : %s\n",c1); 13: } 14: } 11.4.3. 메서드를갖는 enum enum 의좋은점중의하나는메서드를가질수있다는것입니다. 메서드를이용하면열거 데이터를원하는형태의값으로사용할수있습니다. 다음은앞에서선언한 Coin 열거형에메서드를선언한예입니다. Coin.java 1: public enum Coin { 2: PENNY, 3: NICKEL, 4: DIME, 5: QUARTER; 6: 7: int getvalue() {
Java 자바야놀자 8: switch(this) { 9: case PENNY: return 1; 10: case NICKEL: return 5; 11: case DIME: return 10; 12: case QUARTER: return 25; 13: default: return 0; 14: } 15: } 16: } 다음코드는위의열거형을사용하는예입니다. EnumMethodExample.java 1: public class EnumMethodExample { 2: public static void main(string[] args) { 3: for(coin coin : Coin.values()) { 4: System.out.println(coin + " 의값은 " + coin.getvalue()); 5: } 6: } 7: } 실행결과는빈약하지만이것은많은의미를내포하고있습니다. C언어에서는열거형데이터를기수형태의정수값으로표현이가능하지만자바의열거형은열거데이터를기수형태의정수값으로사용하지못하는단점 (?) 이있었습니다. 열거형메서드는 C언어에서처럼기수형태의정수값뿐만아니라사용자가원하는형태의값을사용할수있도록해줍니다. 11.4.4. enum 추상메서드 enum 에서추상메서드를선언한다음각각의열거항목에서추상메서드를다르게구현 할수도있습니다. 다음은추상메서드선언한후열거항목에서메서드를각각다르게구현한예입니다. Coin2.java 1: public enum Coin2 { 2: PENNY { 3: int getvalue() { 4: return 1; 5: } 6: }, 7: NICKEL { 8: int getvalue() { 9: return 5; 10: } 11: },
Chapter 컬렉션과제네릭 11 12: DIME { 13: int getvalue() { 14: return 10; 15: } 16: }, 17: QUARTER { 18: int getvalue() { 19: return 25; 20: } 21: }; 22: 23: abstract int getvalue(); 24: } 다음은추상메서드를갖는열거형을테스트하는코드입니다. EnumAbstractMethodExample.java 1: public class EnumAbstractMethodExample { 2: public static void main(string[] args) { 3: for(coin2 coin : Coin2.values()) { 4: System.out.println(coin + " 의값은 " + coin.getvalue()); 5: } 6: } 7: } 11.4.5. 생성자를갖는 enum 다음의예에서보는바와같이생성자를가질수있습니다. 생성자를이용하면많은열거 데이터를원하는형태의값으로사용하기가더쉬워집니다. 다음코드는열거형에생성자를정의한예입니다. Coin3.java 1: public enum Coin3 { 2: PENNY(1), 3: NICKEL(5), 4: DIME(10), 5: QUARTER(25); 6: 7: private int coinvalue; 8: 9: int getvalue() { 10: return coinvalue; 11: } 12: 13: Coin3(int value) { 14: coinvalue = value;
Java 자바야놀자 15: } 16: } 생성자를갖는열거형을사용하는방법은의외로간단합니다. 생성자라고해서보통의클 래스처름 new 를통해생성하지는않습니다. 열거형의생성자는직접호출할필요없이열 거데이터만참조하면됩니다. 다음은생성자를갖는열거형을테스트하는예입니다. EnumConstructorExample.java 1: public class EnumConstructorExample { 2: public static void main(string[] args) { 3: for(coin3 coin : Coin3.values()) { 4: System.out.println(coin + " 의값은 " + coin.getvalue()); 5: } 6: } 7: } 11.4.6. Java SE 5(JDK 1.5) 이전버전에서열거형사용 typesafe 하지않은열거형클래스 만일여러분이 C 또는 C++ 또는 C# 프로그래머라면아래의코드들이더익숙할것입니 다. typedef enum { WHITE, BLACK, YELLOW }color; typedef enum { CAMRY, HONDA, FORD }car; C 또는 C++ 에서는열거형으로정의된변수의값은정수형으로사용할수있었을것입니 다. 위의예에서는할당되는값들은 0 부터증가하여각각, WHITE == 0, BLACK == 1 그 리고 YELLOW == 2 값이할당되었을것입니다. 이로인해 switch 문에서도사용할수있
Chapter 컬렉션과제네릭 11 었고다른제어문들에서열거형의값을정수형으로대신사용할수있었습니다. 그러나아래의코드를보겠습니다. color mycolor = FORD; 이코드는자바에서는안되는일이지만 C언어에서는가능한일입니다. 그러나위의코드에는문제가있습니다. color와 car가섞여사용되고있습니다. 자동차열거형값을색상열거형값에할당하는경우입니다. 이러한코드가 C언어에서는아무런제약을받지않고컴파일오류없이실행이된다는것입니다. 이러한경우를보고 "Such type is not safe." 라고할수있습니다. enum 키워드가추가되기이전버전, 즉, Java SE 5(JDK 1.5) 이전버전의자바에서는열 거형을사용하기위해서는여러분은아래의코드처럼클래스를선언하고그안에변수들을 선언하는방식으로열거형을사용할수있을것입니다. public class PlayingCard { public static final int SUIT_CLUBS =0; public static final int SUIT_DIAMONDS =1; public static final int SUIT_HEARTS =2; public static final int SUIT_SPADES =3;... } 위의코드는 switch문에서사용하는데아무런문제가없을것입니다. 그러나 type safe하지않습니다. 뭔가미심쩍은부분이존재한다는것입니다. C언어에익숙한프로그래머들에게는그저좋게만보이는코드일수있습니다. 다음에서는클래스가상수를이용하여열거형자료를나타낼때 type safe하도록표현해보도록하겠습니다. typesafe 한열거형사용을위한클래스 다음은 Java SE 5(JDK 1.5) 이전버전에서의열거형을사용하기위해서클래스를선언할 때 type safe 하도록상수를선언한예입니다. Suit.java 1: public class Suit { 2: private final String name; 3: 4: public static final Suit CLUBS = new Suit("clubs"); 5: public static final Suit DIAMONDS = new Suit("diamonds"); 6: public static final Suit HEARTS = new Suit("hearts"); 7: public static final Suit SPADES = new Suit("spades"); 8: 9: private Suit(String name) {
Java 자바야놀자 10: this.name =name; 11: } 12: public String tostring() { 13: return name; 14: } 15: } 우선생성자가 private로선언되어있는것을확인하세요. 이클래스는 final로선언되어있지않아도자식클래스를가질수없습니다. 그리고상수들은 static으로선언되어있어객체생성없이클래스이름만으로참조가가능합니다. 이러한코드디자인은컴파일시형안정성 (type safety) 을보장해줍니다. 이제여러분은 Suit 클래스를아래와같은방법으로 C 언어의 enum 처럼사용할수있습니 다. 물론형안정성을갖도록사용하는것입니다. private static final Suit[] CARD_SUIT = { Suit.CLUBS, Suit.DIAMONDS, Suit.HEARTS, Suit.SPADES }; Suit suit = CARD_SUIT[0]; if (suit == Suit.CLUBS) {... }else if (suit == Suit.DIAMONDS) {... }else if (suit == Suit.HEARTS) {... }else if (suit == Suit.SPADES) {... }else{ throw new NullPointerException("Null Suit"); //suit ==null } 나중에여러분이 Suit 클래스를확장하게될지라도, 위의코드가포함된클래스는다시컴 파일을할필요가없을것입니다. 다음코드는 Suit 클래스를형안정성을갖도록사용하는예입니다. EnumSuitExample.java 1: public class EnumSuitExample { 2: private static final Suit[] CARD_SUIT = { 3: Suit.CLUBS, 4: Suit.DIAMONDS,
Chapter 컬렉션과제네릭 11 5: Suit.HEARTS, 6: Suit.SPADES 7: }; 8: 9: public static void main(string[] args) { 10: for(suit suit : CARD_SUIT) { 11: System.out.println(suit); 12: } 13: 14: System.out.println(); 15: Suit suit = CARD_SUIT[0]; 16: if(suit == Suit.CLUBS) { 17: System.out.println("clubs"); 18: }else if(suit == Suit.DIAMONDS) { 19: System.out.println("diamonds"); 20: }else if(suit == Suit.HEARTS) { 21: System.out.println("hearts"); 22: }else if(suit == Suit.SPADES) { 23: System.out.println("spades"); 24: }else { 25: throw new NullPointerException("Null Suit"); 26: } 27: } 28: } 순서비교가가능하고 typesafe 한열거형클래스 Comparable 인터페이스를구현하면순서비교가가능한열거형을만들수있습니다. 물 론그러기위해서는 compareto() 메서드를재정의해야합니다. Suit.java 1: public class Suit implements Comparable<Suit> { 2: private final String name; 3: 4: public static final Suit CLUBS =new Suit("clubs"); 5: public static final Suit DIAMONDS =new Suit("diamonds"); 6: public static final Suit HEARTS =new Suit("hearts"); 7: public static final Suit SPADES =new Suit("spades"); 8: 9: private static int nextordinal =0; 10: 11: private final int ordinal = nextordinal++; 12: 13: private Suit(String name) { 14: this.name = name; 15: } 16: public String tostring() {
Java 자바야놀자 17: return name; 18: } 19: 20: public int compareto(suit suit) { 21: return ordinal - suit.ordinal; 22: } 23: } 다음은비교가능하고 type safe 한열거형을구현하기위한클래스를테스트하는코드입 니다. 코드에서사용된 TreeSet 클래스는객체를저장할수있는클래스입니다. 클래스에 대한자세한내용은뒤에서다시설명됩니다. 이예제의중요한사항은 TreeSet 에 add 한 Suit 들이자동으로정렬이되어있다는것입니다. EnumSuitSortExample.java 1: import java.util.treeset; 2: 3: public class EnumSuitSortExample { 4: public static void main(string[] args) { 5: Suit clubs = Suit.CLUBS; 6: Suit diamonds = Suit.DIAMONDS; 7: Suit hearts = Suit.HEARTS; 8: Suit spades = Suit.SPADES; 9: 10: TreeSet<Suit> suits = new TreeSet<>(); 11: suits.add(spades); 12: suits.add(hearts); 13: suits.add(diamonds); 14: suits.add(clubs); 15: 16: for(suit suit : suits) { 17: System.out.println(suit); 18: } 19: } 20: } 위와같이클래스를이용하여 typesafe한열거형데이터를사용할수있지만 enum 형식을이용하여열거형을사용하면더많은이점이있습니다. JDK버전이 1.4이하에서컴파일 / 실행되어야하는상황이라면어쩔수없지만그렇지않은상황이라면 enum 형선언을하는것이더많은장점을제공합니다. 자바의열거형에대해서더자세하게알고싶으면다음사이트를참고하세요. http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html
Chapter 컬렉션과제네릭 11 11.5. Varargs(Variable-length arguments) 11.5.1. 가변인자 (Varargs) varargs는 Variable-length argument의약어입니다. 우리말로번역하면가변인자또는가변인수라고부릅니다. 가변인자의기능은 Java SE 5(JDK 1.5) 이후부터사용할수있습니다. 가변인자를잘사용하면정말로거추장스러운코드를일부정리할수있습니다. 다음과같은기본예제는 String 인수를다양하게가진로그메서드입니다. Log.log(String code) Log.log(String code, String arg) Log.log(String code, String arg1, String arg2) Log.log(String code, String[] args) 가변인자에관한설명에서흥미로운사실은앞의 4 개의메서드예제를새로운메서드예 제로교체하는경우얻어지는호환성입니다. 아래의코드를보시면파라미터변수의선언 유형뒤에말줄임표 (...) 가붙어있습니다. Log.log(String code, String... args) 모든가변인자는소스호환적이어서 log() 메서드의모든호출자를재컴파일하는경우 4 개의모든메서드를바로교체할수있습니다. 그러나이전버전과의호환성이필요한경우처음세개는그대로두어야합니다. 마지막메서드에서만 String 배열을가져오면같은값이되므로가변인자버전으로교체할수있습니다. 가변인자는배열을통해전달됩니다. 그러므로다음예제에서처럼 for 문을이용해모든 인자를쉽게처리할수있습니다. VariableLengthExample.java 1: public class VariableLengthExample { 2: 3: public static void main(string[] args) { 4: log("hello"); 5: log("variablelengthexample", "Hello"); 6: log("a", "b", "c"); 7: log(); 8: } 9: 10: public static void log(string... msg) {
Java 자바야놀자 11: System.out.print(" 로그 : "); 12: for(string message : msg) { 13: System.out.print(message + ", "); 14: } 15: System.out.println(); 16: } 17: } 11.5.2. 가변인자형변환 (Casting) 다음코드는 log메서드의인자로들어가는항목들중에서두개이상의인자가들어갈경우에는, 첫번째항목은 String이고두번째는 Exception인상황을예상할수있습니다. 그러한상황에 varargs를이용해서메서드를선언하면다음처럼구현할수있을것입니다. 그러면목록의첫번째 (0번인덱스 ) 데이터를 String으로형변환해서사용하고두번째 (1 번인덱스 ) 데이터를예외클래스로형변환해서사용해야할것입니다. Log.log(Object... objects) { String message = (String)objects[0]; if (objects.length > 1) { Exception e = (Exception)objects[1]; // Do something with the exception } } 이럴경우위의코드에비하여권장하는메서드원형은 varargs 파라미터에서별도로선언 된 String 및 Exception 을사용하여다음과같이선언하는것이더바람직할것입니다. Log.log(String message, Exception e, Object... objects) {... } 지나치게사용하려고하지마세요. 가변인자를사용하면 타입시스템 을망가뜨릴수있습니다. 강력한형지정 (strong typing) 이필요한경우사용하세요. PrintStream.printf() 메서드는이러한규칙에대한예외입니다. 이것은유형정보를나중에도수용할수있도록첫번째인수로제공합니다.
Chapter 컬렉션과제네릭 11 11.6. 마인드맵정리
Java 자바야놀자