컬렉션 (collection) 의개념 Collections, Generic 514760-1 2016 년가을학기 11/24/2016 박경신 컬렉션 요소 (element) 라고불리는가변개수의객체들의저장소 객체들의컨테이너라고도불림 요소의개수에따라크기자동조절 요소의삽입, 삭제에따른요소의위치자동이동 고정크기의배열을다루는어려움해소 다양한객체들의삽입, 삭제, 검색등의관리용이 컬렉션을위한인터페이스와클래스 Collection<E> Map<K, V> Set<E> List<E> Queue<E> 인터페이스 클래스 HashSet<E> ArrayList<E> Vector<E> LinkedList<E> HashMap<K, V> Stack<E> 컬렉션과제네릭 컬렉션은제네릭 (generics) 기법으로구현됨 컬렉션의요소는객체만가능 기본적으로 int, char, double 등의기본타입사용불가 JDK 1.5 부터자동박싱 / 언박싱기능으로기본타입사용가능 제네릭 특정타입만다루지않고, 여러종류의타입으로변신할수있도록클래스나메소드를일반화시키는기법 <E>, <K>, <V> : 타입매개변수 요소타입을일반화한타입 제네릭클래스사례 제네릭벡터 : Vector<E> E 에특정타입으로구체화 정수만다루는벡터 Vector<Integer> 문자열만다루는벡터 Vector<String>
제네릭의기본개념 제네릭 Stack<E> 클래스의 JDK 매뉴얼 JDK 1.5 에서도입 (2004 년기점 ) 모든종류의데이터타입을다룰수있도록일반화된타입매개변수로클래스나메소드를작성하는기법 C++ 의템플릿 (template) 과동일 정수스택 정수만저장 class Stack<E> {... void push(e element) {... E pop() {...... 제네릭스택 특정타입으로구체화... void push(integer element) {... Integer pop() {......... void push(string element) {... String pop() {...... 문자열스택 20-345 123 10 Good C# C++ Java 문자열만저장 Vector<E> Vector<E> 의특성 java.util.vector <E> 에서 E 대신요소로사용할특정타입으로구체화 여러객체들을삽입, 삭제, 검색하는컨테이너클래스 배열의길이제한극복 원소의개수가넘쳐나면자동으로길이조절 Vector 에삽입가능한것 객체, null 기본타입은박싱 / 언박싱으로 Wrapper 객체로만들어저장 Vector 에객체삽입 벡터의맨뒤에객체추가 벡터중간에객체삽입 Vector 에서객체삭제 임의의위치에있는객체삭제가능 : 객체삭제후자동자리이동 Vector<Integer> 컬렉션내부구성 add() 를이용하여요소를삽입하고 get() 을이용하여요소를검색합니다 Vector<Integer> v = new Vector<Integer>();
타입매개변수사용하지않는경우경고발생 Vector<E> 클래스의주요메소드 Vector 로만사용하면경고발생 Vector<Integer> 나 Vector<String> 등타입매개변수를사용하여야함 11
컬렉션과자동박싱 / 언박싱 JDK 1.5 이전 기본타입데이터를 Wrapper 클래스를이용하여객체로만들어사용 Vector<Integer> v = new Vector<Integer>(); v.add(new Integer(4)); 컬렉션으로부터요소를얻어올때, Wrapper 클래스로캐스팅필요 Integer n = (Integer)v.get(0); int k = n.intvalue(); // k = 4 JDK 1.5 부터 자동박싱 / 언박싱이작동하여기본타입값사용가능 Vector<Integer> v = new Vector<Integer> (); v.add(4); // 4 new Integer(4) 로자동박싱 int k = v.get(0); // Integer 타입이 int 타입으로자동언박싱, k = 4 제네릭의타입매개변수를기본타입으로구체화할수는없음 Vector<int> v = new Vector<int> (); // 오류 예제 : 정수값만다루는 Vector<Integer> 정수값만다루는제네릭벡터를생성하고활용하는사례를보인다. 다음코드에대한결과는무엇인가? import java.util.vector; public class VectorEx { // 정수값만다루는제네릭벡터생성 Vector<Integer> v = new Vector<Integer>(); v.add(5); // 5 삽입 v.add(4); // 4 삽입 v.add(-1); // -1 삽입 // 벡터중간에삽입하기 v.add(2, 100); // 4 와 -1 사이에정수 100 삽입 System.out.println(" 벡터내의요소객체수 : " + v.size()); System.out.println(" 벡터의현재용량 : " + v.capacity()); 예제 : Point 클래스의객체들만저장하는벡터만들기 // 모든요소정수출력하기 for(int i=0; i<v.size(); i++) { int n = v.get(i); System.out.println(n); 5 4 100-1 벡터에있는정수합 : 108 // 벡터속의모든정수더하기 int sum = 0; for(int i=0; i<v.size(); i++) { int n = v.elementat(i); sum += n; System.out.println(" 벡터에있는정수합 : + sum); (x, y) 한점을추상화한 Point 클래스를만들고 Point 클래스의객체만저장하는벡터를작성하라. import java.util.vector; class Point { private int x, y; public Point(int x, int y) { this.x = x; this.y = y; public String tostring() { return "(" + x + "," + y + ")";
예제 : Point 클래스의객체들만저장하는벡터만들기 public class PointVectorEx { // Point 객체를요소로만가지는벡터생성 Vector<Point> v = new Vector<Point>(); // 3 개의 Point 객체삽입 v.add(new Point(2, 3)); v.add(new Point(-5, 20)); v.add(new Point(30, -8)); (2,3) (-5,20) (30,-8) // 벡터에있는 Point 객체모두검색하여출력 for(int i=0; i<v.size(); i++) { Point p = v.get(i); // 벡터에서 i 번째 Point 객체얻어내기 System.out.println(p); // p.tostring() 을이용하여객체 p 출력 ArrayList<E> ArrayList<E> 의특성 java.util.arraylist, 가변크기배열을구현한클래스 <E> 에서 E 대신요소로사용할특정타입으로구체화 ArrayList 에삽입가능한것 객체, null 기본타입은박싱 / 언박싱으로 Wrapper 객체로만들어저장 ArrayList 에객체삽입 / 삭제 리스트의맨뒤에객체추가 리스트의중간에객체삽입 임의의위치에있는객체삭제가능 벡터와달리스레드동기화기능없음 다수스레드가동시에 ArrayList 에접근할때동기화되지않음 개발자가스레드동기화코드작성 ArrayList<String> 컬렉션의내부구성 ArrayList<E> 클래스의주요메소드 ArrayList<String> = new ArrayList<String>(); add() 를이용하여요소를삽입하고 get() 을이용하여요소를검색합니다
21 예제 : ArrayList 에문자열을달기 키보드로문자열을입력받아 ArrayList 에삽입하고가장긴이름을출력하라. import java.util.*; public class ArrayListEx { // 문자열만삽입가능한 ArrayList 컬렉션생성 ArrayList<String> a = new ArrayList<String>(); // 키보드로부터 4 개의이름입력받아 ArrayList 에삽입 Scanner scanner = new Scanner(System.in); for(int i=0; i<4; i++) { System.out.print(" 이름을입력하세요 >>"); String s = scanner.next(); // 키보드로부터이름입력 a.add(s); // ArrayList 컬렉션에삽입 예제 : ArrayList 에문자열을달기 // ArrayList에들어있는모든이름출력 for(int i=0; i<a.size(); i++) { 이름을입력하세요 >>Mike // ArrayList의 i 번째문자열얻어오기이름을입력하세요 >>Jane String name = a.get(i); 이름을입력하세요 >>Ashley 이름을입력하세요 >>Helen System.out.print(name + " "); Mike Jane Ashley Helen 가장긴이름은 : Ashley // 가장긴이름출력 int longestindex = 0; for(int i=1; i<a.size(); i++) { if(a.get(longestindex).length() < a.get(i).length()) longestindex = i; System.out.println("\n가장긴이름은 : " + a.get(longestindex));
컬렉션의순차검색을위한 Iterator Iterator<E> 인터페이스 Vector<E>, ArrayList<E>, LinkedList<E> 가상속받는인터페이스 리스트구조의컬렉션에서요소의순차검색을위한메소드포함 Iterator<E> 인터페이스메소드 컬렉션의순차검색을위한 Iterator Vector<Integer> v = new Vector<Integer>(); Iterator<Integer> it = v.iterator(); while(it.hasnext()) { // 모든요소방문 int n = it.next(); // 다음요소리턴... 또는 iterator() 메소드 iterator() 를호출하면 Iterator 객체반환 Iterator 객체를이용하여인덱스없이순차적검색가능 for (int n : v) { 예제 : Iterator 를이용하여 Vector 의모든요소출력하고합구하기 Vector<Integer> 로부터 Iterator를얻어내고벡터의모든정수를출력하고합을구하라. import java.util.*; public class IteratorEx { // 정수값만다루는제네릭벡터생성 Vector<Integer> v = new Vector<Integer>(); v.add(5); // 5 삽입 v.add(4); // 4 삽입 v.add(-1); // -1 삽입 v.add(2, 100); // 4와 -1 사이에정수 100 삽입 // Iterator를이용한모든정수출력하기 Iterator<Integer> it = v.iterator(); // Iterator 객체얻기 while(it.hasnext()) { int n = it.next(); System.out.println(n); for (int n : v) { System.out.println(n); 예제 : Iterator 를이용하여 Vector 의모든요소출력하고합구하기 // Iterator를이용하여모든정수더하기 int sum = 0; it = v.iterator(); // Iterator 객체얻기 for (int n : v) { while(it.hasnext()) { sum += n; int n = it.next(); sum += n; System.out.println(" 벡터에있는정수합 : " + sum); 5 4 100-1 벡터에있는정수합 : 108
HashMap<K,V> HashMap<K,V> 키 (key) 와값 (value) 의쌍으로구성되는요소를다루는컬렉션 java.util.hashmap K 는키로사용할요소의타입, V 는값으로사용할요소의타입지정 키와값이한쌍으로삽입 키는해시맵에삽입되는위치결정에사용 값을검색하기위해서는반드시키이용 삽입및검색이빠른특징 요소삽입 : put() 메소드 요소검색 : get() 메소드 예 ) HashMap<String, String> 생성, 요소삽입, 요소검색 HashMap<String, String> 의내부구성과 put(), get() 메소드 HashMap<String, String> map = new HashMap<String, String>(); HashMap<String, String> h = new HashMap<String, String>(); h.put("apple", " 사과 "); // "apple" 키와 " 사과 " 값의쌍을해시맵에삽입 String kor = h.get("apple"); // "apple" 키로값검색. kor 는 " 사과 HashMap<K,V> 의주요메소드
예제 : HashMap 을이용하여영어단어와한글단어를쌍으로저장하고검색하는사례 영어단어와한글단어를쌍으로 HashMap 에저장하고영어단어로한글단어를검색하는프로그램을작성하라. import java.util.*; public class HashMapDicEx { // 영어단어와한글단어의쌍을저장하는 HashMap 컬렉션생성 HashMap<String, String> dic = new HashMap<String, String>(); // 3 개의 (key, value) 쌍을 dic 에저장 dic.put("baby", " 아기 "); // "baby" 는 key, " 아기 " 은 value dic.put("love", " 사랑 "); dic.put("apple", " 사과 "); 예제 : HashMap 을이용하여영어단어와한글단어를쌍으로저장하고검색하는사례 // dic 컬렉션에들어있는모든 (key, value) 쌍출력 Set<String> keys = dic.keyset(); // key 문자열을가진 Set 리턴 Iterator<String> it = keys.iterator(); while(it.hasnext()) { String key = it.next(); String value = dic.get(key); System.out.println("(" + key + "," + value + ")"); // 영어단어를입력받고한글단어검색 Scanner scanner = new Scanner(System.in); for(int i=0; i<3; i++) { System.out.print(" 찾고싶은단어는?"); String eng = scanner.next(); System.out.println(dic.get(eng)); 예제 : HashMap 을이용하여영어단어와한글단어를쌍으로저장하고검색하는사례 예제 : HashMap 을이용하여자바과목의점수를기록관리하는코드작성 babo 를해시맵에서찾을수없기때문에 null 리턴 (love, 사랑 ) (apple, 사과 ) (baby, 아기 ) 찾고싶은단어는?apple 사과찾고싶은단어는?babo null 찾고싶은단어는?love 사랑 HashMap 을이용하여학생의이름과자바점수를기록관리해보자. import java.util.*; public class HashMapScoreEx { // 사용자이름과점수를기록하는 HashMap 컬렉션생성 HashMap<String, Integer> javascore = new HashMap<String, Integer>(); // 5 개의점수저장 javascore.put(" 한홍진 ", 97); javascore.put(" 황기태 ", 34); javascore.put(" 이영희 ", 98); javascore.put(" 정원석 ", 70); javascore.put(" 한원선 ", 99); System.out.println("HashMap 의요소개수 :" + javascore.size());
예제 : HashMap 을이용하여자바과목의점수를기록관리하는코드작성 // 모든사람의점수출력. // javascore 에들어있는모든 (key, value) 쌍출력 // key 문자열을가진집합 Set 컬렉션리턴 Set<String> keys = javascore.keyset(); // key 문자열을순서대로접근할수있는 Iterator 리턴 Iterator<String> it = keys.iterator(); while(it.hasnext()) { String name = it.next(); int score = javascore.get(name); System.out.println(name + " : " + score); HashMap 의요소개수 :5 한원선 : 99 한홍진 : 97 황기태 : 34 이영희 : 98 정원석 : 70 예제 : HashMap 을이용한학생정보저장 id 와전화번호로구성되는 Student 클래스를만들고, 이름을 키 로하고 Student 객체를 값 으로하는해시맵을작성하라. import java.util.*; class Student { // 학생을표현하는클래스 int id; String tel; public Student(int id, String tel) { this.id = id; this.tel = tel; 예제 : HashMap 을이용한학생정보저장 public class HashMapStudentEx { // 학생이름과 Student 객체를쌍으로저장하는 HashMap 컬렉션생성 HashMap<String, Student> map = new HashMap<String, Student>(); // 3 명의학생저장 map.put(" 황기태 ", new Student(1, "010-111-1111")); map.put(" 한원선 ", new Student(2, "010-222-2222")); map.put(" 이영희 ", new Student(3, "010-333-3333")); System.out.println("HashMap 의요소개수 :" + map.size()); 예제 : HashMap 을이용한학생정보저장 // 모든학생출력. map 에들어있는모든 (key, value) 쌍출력 // key 문자열을가진집합 Set 컬렉션리턴 Set<String> names = map.keyset(); // key 문자열을순서대로접근할수있는 Iterator 리턴 Iterator<String> it = names.iterator(); while(it.hasnext()) { String name = it.next(); // 다음키. 학생이름 Student student = map.get(name); System.out.println(name + " : " + student.id + " " + student.tel); HashMap의요소개수 :3 한원선 : 2 010-222-2222 황기태 : 1 010-111-1111 이영희 : 3 010-333-3333 출력된결과는삽입된결과와다르다는점을기억하기바람
LinkedList<E> // 모든학생출력. map 에들어있는모든 (key, value) 쌍출력 // key 문자열을가진집합 Set 컬렉션리턴 Set<String> names = map.keyset(); // key 문자열을순서대로접근할수있는 Iterator 리턴 for (String name : names) { Student student = map.get(name); System.out.println(name + " : " + student.id + " " + student.tel); HashMap 의요소개수 :3 한원선 : 2 010-222-2222 황기태 : 1 010-111-1111 이영희 : 3 010-333-3333 LinkedList<E> 의특성 java.util.linkedlist E 에요소로사용할타입지정하여구체와 List 인터페이스를구현한컬렉션클래스 Vector, ArrayList 클래스와매우유사하게작동 요소객체들은양방향으로연결되어관리됨 요소객체는맨앞, 맨뒤에추가가능 요소객체는인덱스를이용하여중간에삽입가능 맨앞이나맨뒤에요소를추가하거나삭제할수있어스택이나큐로사용가능 LinkedList<String> 의내부구성과 put(), get() 메소드 LinkedList<String> l = new LinkedList<String>(); Collections 클래스활용 Collections 클래스 java.util 패키지에포함 컬렉션에대해연산을수행하고결과로컬렉션리턴 모든메소드는 static 타입 주요메소드 컬렉션에포함된요소들을소팅하는 sort() 메소드 요소의순서를반대로하는 reverse() 메소드 요소들의최대, 최소값을찾아내는 max(), min() 메소드 특정값을검색하는 binarysearch() 메소드
예제 : Collections 클래스의활용 Collections 클래스를활용하여문자열정렬, 반대로정렬, 이진검색등을실행하는사례를살펴보자. import java.util.*; public class CollectionsEx { static void printlist(linkedlist<string> l) { Iterator<String> iterator = l.iterator(); while (iterator.hasnext()) { String e = iterator.next(); String separator; if (iterator.hasnext()) separator = "->"; else separator = "\n"; System.out.print(e+separator); 예제 : Collections 클래스의활용 LinkedList<String> mylist = new LinkedList<String>(); mylist.add(" 트랜스포머 "); mylist.add(" 스타워즈 "); mylist.add(" 매트릭스 "); mylist.add(0," 터미네이터 "); mylist.add(2," 아바타 "); Collections.sort(myList); // 요소정렬 printlist(mylist); // 정렬된요소출력 static 메소드이므로클래스이름으로바로호출 Collections.reverse(myList); // 요소의순서를반대로 printlist(mylist); // 요소출력 int index = Collections.binarySearch(myList, " 아바타 ") + 1; System.out.println(" 아바타는 " + index + " 번째요소입니다."); 예제 : Collections 클래스의활용 Custom 클래스에대한 sort 함수사용 소팅된순서대로출력 거꾸로출력 매트릭스 -> 스타워즈 -> 아바타 -> 터미네이터 -> 트랜스포머트랜스포머 -> 터미네이터 -> 아바타 -> 스타워즈 -> 매트릭스아바타는 3 번째요소입니다. 개인적으로만든클래스에대해서컬렉션에추가하고, Collections.sort 기능을이용해서정렬하고싶다면 java.lang.comparable 인터페이스를구현해주어야함 public interface Comparable<T> { int compareto(t o); CompareTo(T o) 메소드는현객체를인자로주어진 o 와비교해서순서를정한후에정수 (int) 값을반환함 만약현객체가주어진인자보다작다면음수를반환 만약현객체가주어진인자와동일하다면 0 을반환 만약현객체가주어진인자보다크다면양수를반환
Custom 클래스에대한 sort 함수사용 import java.util.arraylist; import java.util.collections; class A implements java.lang.comparable<a> { int num; String s; public A(String s, int n) { this.s = s; num = n; Custom 클래스에대한 sort 함수사용 public int compareto(a a) { if (s.compareto(a.s) == 0) { if (num > a.num) return 1; else if (num < a.num) return -1; else return 0; else { return s.compareto(a.s); public String tostring() { return "String: " + s + "\t num = " + num; Custom 클래스에대한 Collections.sort 함수사용 public class Main { ArrayList<A> list = new ArrayList<A>(); list.add(new A("Kim", 30)); list.add(new A("Cho", 20)); list.add(new A("Cho", 30)); list.add(new A("Lee", 20)); for (A b : list) { System.out.println(b); System.out.println("After sorting"); Collections.sort(list); for (A b : list) { System.out.println(b); val 의타입은 T 제네릭만들기 제네릭클래스와인터페이스 클래스나인터페이스선언부에일반화된타입추가 public class MyClass<T> { T val; void set(t a) { val = a; T get() { return val; 제네릭클래스레퍼런스변수선언 MyClass<String> s; List<Integer> li; Vector<String> vs; 제네릭클래스 MyClass 선언, 타입매개변수 T T 타입의값 a 를 val 에지정 T 타입의값 val 리턴
제네릭객체생성 구체화 (specialization) 구체화 제네릭타입의클래스에구체적인타입을대입하여객체생성 컴파일러에의해이루어짐 // 제네릭타입 T 에 String 지정 MyClass<String> s = new MyClass<String>(); s.set("hello"); System.out.println(s.get()); // "hello" 출력 // 제네릭타입 T 에 Integer 지정 MyClass<Integer> n = new MyClass<Integer>(); n.set(5); System.out.println(n.get()); // 숫자 5 출력 구체화된 MyClass<String> 의소스코드 제네릭객체생성 구체화 (specialization) public class MyClass<T> { T val; void set(t a) { val = a; T get() { return val; T 가 String 으로구체화 public class MyClass<String> { String val; // 변수 val 의타입은 String void set(string a) { val = a; // String 타입의값 a 를 val 에지정 String get() { return val; // String 타입의값 val 을리턴 구체화오류 타입매개변수에기본타입은사용할수없음 Vector<int> vi = new Vector<int>(); // 컴파일오류. int 사용불가수정 Vector<Integer> vi = new Vector<Integer>(); // 정상코드 타입매개변수 타입매개변수 < 과 > 사이에하나의대문자를타입매개변수로사용 많이사용하는타입매개변수문자 E : Element 를의미하며컬렉션에서요소를표시할때많이사용한다. T : Type 을의미한다. V : Value 를의미한다. K : Key 를의미 타입매개변수가나타내는타입의객체생성불가 //T a = new T(); // 오류!! 타입매개변수는나중에실제타입으로구체화 어떤문자도매개변수로사용가능
예제 : 제네릭스택만들기 스택을제네릭클래스로작성하고, String 과 Integer 형스택을사용하는예를보여라. class GStack<T> { int tos; Object [] stck; public GStack() { tos = 0; stck = new Object [10]; public void push(t item) { if(tos == 10) return; stck[tos] = item; tos++; public T pop() { if(tos == 0) return null; tos--; return (T)stck[tos]; 예제 : 제네릭스택만들기 public class MyStack { GStack<String> stringstack = new GStack<String>(); stringstack.push("seoul"); stringstack.push("busan"); stringstack.push("la"); for(int n=0; n<3; n++) System.out.println(stringStack.pop()); GStack<Integer> intstack = new GStack<Integer>(); intstack.push(1); intstack.push(3); intstack.push(5); for(int n=0; n<3; n++) System.out.println(intStack.pop()); LA busan seoul 5 3 1 제네릭과배열 제네릭에서배열의제한 제네릭클래스또는인터페이스의배열을허용하지않음 //GStack<Integer>[] gs = new GStack<Integer>[10]; // 오류!! 제네릭타입의배열도허용되지않음 //T[] a = new T[10]; // 오류!! 앞예제에서는 Object 타입으로배열생성후실제사용할때타입캐스팅 return (T)stck[tos]; // 타입매개변수 T 타입으로캐스팅 타입매개변수의배열에레퍼런스는허용 public void myarray(t[] a) {... 제네릭메소드 제네릭메소드선언가능 class GenericMethodEx { static <T> void tostack(t[] a, GStack<T> gs) { for (int i = 0; i < a.length; i++) { gs.push(a[i]); 제네릭메소드를호출할때는컴파일러가메소드의인자를통해이미타입을알고있으므로타입을명시하지않아도됨 String[] sa = new String[100]; GStack<String> gss = new GStack<String>(); GenericMethodEx.toStack(sa, gss); // 타입매개변수 T 를 String 으로유추함 sa 는 String[], gss 는 GStack<String> 타입이므로 T 를 String 으로유추