CHAPTER 07 제네릭과 컬렉션
7.1 컬렉션과 제네릭
7.2 제네릭 컬렉션 활용
7.3 제네릭 만들기
Set<E>, List<E>,Queue<E>),Map<K, V> 등ArrayList<E>, Vector<E>, LinkedList<E>, HashMap<K, V> 등
<E>, <K>, <V> : 타입 매개변수 요소 타입을 일반화한 타입 Vector<E>Vector<Integer>Vector<String>
Vector<E>Vector<Integer> 컬렉션 내부 구성 : 리스트 지원
Vector<Integer> v = new Vector<Integer>();
v.add(4); // -> v.add(Integer.valueOf(4)) 로 자동 박싱함
//**0번 인덱스의 Integer 타입의 정보가 int 타입으로 자동 언박싱됨**, k=4
int k = v.get(0); //int k = (Integer)v.get(0).intValue();
//제네릭의 타입 매개 변수를 기본 타입으로 구체화할 수는 없음
Vector<int> v = new Vector<int>(); //오류 <Integer> 컬렉션 활용 (Vector 의 주요 메소드 포함)import java.util.Vector; //import 해주기
public class VectorEx {
public static void main(String[] args) {
// TODO Auto-generated method stub
//--- 정수 값만 다루는 제네릭 벡터 생성 ---
// 정수 클래스 데이터 타입의 객체 10개(디폴트)를 보관할 수 있는 메모리 생성, 참조변수 v로 관리
Vector<Integer> v = new Vector<Integer>();
//<--- 요소 삽입 v.add() --->
v.add(5); //5 삽입 -> [자동박싱] v.add(Integer.valueOf(5))
v.add(4);
v.add(-1);
// v -> {5(Integer 객체), 4, -1}
//<--- 벡터 중간에 삽입하기 --->
v.add(2, 100); //4와 -1 사이에 정수 100 삽입
//v로 관리하는 영역의 2번 인덱스 위치에, 클래스 타입 Integer 100 삽입
//기존 내용들은 한 칸 씩 밀려난다.
// v -> {5, 4, 100, -1}
//v.size() : 벡터가 포함하고 있는 요소의 개수
System.out.println("The number of element objects in the vector: " + v.size()); //4
//v.capacity() : 벡터의 현재 용량
System.out.println("The current capacitiy of the vector: " + v.capacity()); //10
// --- 모든 요소 정수 출력하기 ---
for(int i=0; i < v.size(); i++) {
int n = v.get(i); //지정된 index 의 요소 반환 (예외처리 지원하지 않음)
System.out.println(n);
}
//--- 벡터 속의 모든 정수 더하기 ---
int sum = 0;
for(int i = 0; i < v.size(); i++) {
int n = v.elementAt(i); //지정된 index의 요소 반환(예외처리 디폴트로 지원)
sum += n;
}
System.out.println("The sum of the integers in a vector: " + sum);
//--- 벡터 삭제하기 ---
v.remove(1);
//마지막 요소
int last = v.lastElemnet();
//모든 요소 삭제
v.removeAllElement();
}
}
//result
The number of element objects in the vector: 4
The current capacitiy of the vector: 10
5
4
100
-1
The sum of the integers in a vector: 108
Vector<Point> 컬렉션 활용import java.util.Vector;
class Point{
private int x,y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
//'toString 재정의' [6장 참조]
public String toString() {
return "(" + x + "," + y + ")";
}
};
public class PointVectorEx {
public static void main(String[] args) {
// TODO Auto-generated method stub
// Point 객체를 요소로만 가지는 벡터 생성
Vector<Point> v = new Vector<Point>();
//3개의 Point 객체 삽입
v.add(new Point(2,3)); //이후 정수 2개를 처리하는 생성자 호출
v.add(new Point(-5,20)); //셍상지 호출 후 만들어진 정보를 add 로 v에 추가
v.add(new Point(30,-8));
//벡터에 있는 Point 객체 모두 검색하여 출력
for(int i=0; i<v.size(); i++) {
//해시 코드 값이 리턴되는것 꼭 기억
Point p = v.get(i); //벡터에서 인덱스 i 번째에 해당하는 Point 객체의 해시코드 값을 p 에 할당
System.out.println(p); //p.toString() 으로 자동 변환되고, toString() 을 오버라이딩
// 따라서 참조변수 p가 관리하는 Point 인스턴스의 x,y 좌표 출력
}
}
}
//result
(2,3)
(-5,20)
(30,-8)
//java 7 이전
Vector<Integer> v = new Vector<Integer>();
//java 7 이후
// - 컴파일러의 타입 추론 기능 추가
// - < > (다이아몬드 연산자)에 타입 매개변수 생략
Vector<Integer> v = new Vector<>();
//java 10 이후
// - var 키워드 도입, 컴파일러의 지역 변수 타입 추론 기능
var v = new Vector<Integer>();
<E>
ArrayList<E> 클래스의 주요 메소드//ArrayList 생성
ArrayList<String> a = new ArrayList<String>(7);
//요소 삽입
a.add("Hello"); //0번 인덱스
a.add("Hi"); //1번 인덱스
a.add("Java"); //2번 인겓스
//요소 개수 n, 용량 측정 capacity() 메소드는 없다.
int n = a.size(); //n=3
//요소 중간 삽입
a.add(2, "Sahni"); // a.add(5,"~") -> a.size() 보다 큰 위치에 삽입 불가
//요소 알아내기
String str = a.get(1); //1번 인덱스의 "Hi" 를 받는것이 아닌 "Hi" 를 관리하는 해시값을 str 에 제공
//요소 삭제
a.remove(1); // a.remove(4); -> 요류
//모든 요소 삭제
a.clear();
import java.util.*;
public class ArrayListEx {
public static void main(String[] args) {
// TODO Auto-generated method stub
//문자열만 삽입가능한 ArrayList 컬렉션 생성 -> 크기 지정하지 않으면 디폴트 크기 10
ArrayList<String> a = new ArrayList<String>(); //또는 var a = new ArrayList<String>();
//키보드로부터 4개의 이름 입력받아 ArrayList 에 삽입
Scanner scanner = new Scanner(System.in);
for(int i=0; i<4; i++) {
System.out.print("Enter a name >> ");
String s = scanner.next(); //키보드로부터 이름 입력
a.add(s); //ArrayList 컬렉션에 순차적으로 삽입
}
//ArrayList 에 들어 있는 모든 이름 출력
for(int i=0; i<a.size(); i++) {
String name = a.get(i); //ArrayList a 로 참조하는, 리스트 객체의 i 번째 인덱스 문자열 얻어오기
// 인덱스 i 에 해당되는 객체(인스턴스)의 해쉬코드 값을 String 클래스의 참조변수인 name 에 할당
System.out.print(name + " "); // name 참조 변수로 관리하는 문자열 Mike ..출력
}
int longestIndex = 0;
// 가장 긴 이름 출력
for(int i=0; i<a.size(); i++) {
if(a.get(longestIndex).length() < a.get(i).length())
longestIndex = i;
}
System.out.println("\n The longest name is : " + a.get(longestIndex));
}
}
//result
Enter a name >> Mike
Enter a name >> Jane
Enter a name >> Ashley
Enter a name >> Helen
Mike Jane Ashley Helen
The longest name is : Ashley
Iterator<E> 인테페이스Vector<E>, ArrayList<E>, LinkedList<E>, HashMap<K, V> 가 상속받는 인터페이스Iterator<E> 인터페이스 메소드Vector<Integer> v = new Vector<Integer>();
Iterator<Integer> it = v.iterator();
while(it.hasNext()) { //모든 요소 방문
int n = it.next(); //다음 요소 리턴
} 
import java.util.*;
public class IteratorEx {
public static void main(String[] args) {
// TODO Auto-generated method stub
Vector<Integer> v = new Vector<Integer>(); //정수 값만 다루는 제네릭 벡터 생성, 디폴트 크기 10
v.add(5); //Integer 객체 5 삽입
v.add(4); // " 4 삽입
v.add(-1); // " -1 삽입
v.add(2,100); //4와 -1 사이에 정수 100 삽입
//Iterator 를 이용한 모든 정수 출력하기
//벡터 내부의 정보에 접근할 수 있는 iterator 클래스의 참조변수 it
Iterator<Integer> it = v.iterator(); //Iterator 객체 얻기
while(it.hasNext()) { //나를 기준으로 다음 위치의 벡터가 존재하는지
int n = it.next(); //it 으로 관리하는 영역으로 가서 다름 벡터값을 리턴하라.-> 5리턴 (첫 시도 기준)
System.out.println(n);
}
int sum = 0;
it = v.iterator(); // Iterator 객체 얻기
//(현재 it 위치가 v가 가리키는 벡터의 '마지막'위치이기 떄문에 새로 얻음)
while(it.hasNext()) { //iterator 를 이용하여 모든 정수 더하기
int n = it.next();
sum += n;
}
System.out.println("The sum of the integers in a vector : " + sum);
}
}
//result
5
4
100
-1
The sum of the integers in a vector : 108
HashMap<K, V>key 와 value 의 쌍으로 구성되는 요소를 다루는 컬렉션
삽입 및 검색이 빠른 특징
Set<K>keySet() : HashMap 에 있는 모든 키를 담은 Set<k> 컬렉션 리턴key 와 value 로 관리되는 클래스 데이터 타입은 각각 string, string
// 해시맵 생성
HashMap<String, String> h = new HashMap<String, String>();
// 해시맵에 (키, 값) 삽입
h.put("baby", "아기");
h.put("love", "사랑");
h.put("apple", "사과");
//키로 값 검색
String kor = h.get("love"); //kor = "사랑"
//키로 요소 삭제
h.remove("apple");
//요소 개수
int n = h.size(); //n=2
//
Set<String> keys = h.keySet(); //h로 관리하는 해시맵의 모든 키를 담은 Set<k> 컬렉션 리턴
[주의] 정보가 순차적으로 메모리에 할당되는 것이 아니라
hash() 메소드에 의해 다른 key와 구별이 되는 특정한 메모리 주소 정보가 할당됨
import java.util.*;
public class HashMapDicEx {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 영어 단어와 한글 단어의 쌍을 저장하는 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", "사과");
// dic 컬렉션에 들 있는 모든 (key, value) 쌍 출력
Set<String> keys = dic.keySet(); //(중요한 부분) dic로 관리하는 해시맵에 접근하여 모든 키 문자열을 담은 Set<k> 컬렉션 리턴
Iterator<String> it = keys.iterator();
while(it.hasNext()) { //key, value 에 저장되어 있는 문자열 출력 (it 으로 순차적 접근을 한다)
String key = it.next();
String value = dic.get(key);
System.out.println("(" + key + "," + value + ")");
}
//밑에는 iterator 를 사용하지 않고 무한루프를 돌리면서 탈출할 수 있는 조건을 걸어둠
//it 으로 순차적으로 접근하는줄 알았지만, 우리 눈에 보이지 않게 알아서 순차적으로 접근한다.(iterator 사용 하지 않는 부분)
//kor==null : 해당 key 가 없다면 null
// 영어 단어를 입력 받고, 한글 단어 검색
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.print("찾고 싶은 단어는?");
String eng = scanner.next(); //eng 는 내가 입력받은 문자열
if(eng.equals("exit")) {
System.out.println("종료합니다...");
break;
}
//해시맵에서 '키' eng 의 값 'kor' 검색
String kor = dic.get(eng); //해시맵에서 찾을 수 없는 경우에는 null 리턴
if(kor == null)
System.out.println(eng + "는 없는 단어 입니다.");
else
System.out.println(kor);
}
scanner.close();
}
}
//result
(love,사랑)
(apple,사과)
(baby,아기)
찾고 싶은 단어는?apple
사과
찾고 싶은 단어는?babo
babo는 없는 단어 입니다.
찾고 싶은 단어는?exit
종료합니다...
내부 인덱스 확인하여 String 클래스 객체 "정원석" 의 참조값 리턴하여 name 에 할당
name -> "정원석" 에 연계되는 value 70 을 리턴
import java.util.*;
public class HashMapScoreEx {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 사용자 이름과 점수를 기록하는 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());
//모든 사람의 점수 출력
//javaScore 에 들어 있는 모든 (key,value) 쌍 출력
// key 문자열을 가진 집합 Set 컬렉션 리턴
// HaspMap 메모리 공간에 접근하기 위하여 javaScore.keySet() 메소드 활용
Set<String> keys = javaScore.keySet();
//key 문자열을 순서대로 접근할 수 있는 Iterator 리턴
Iterator<String> it = keys.iterator();
//while(it.hasNext()) {} 위치
while(it.hasNext()) {
//첫번쨰 상황 : 내부 인덱스를 확인하여 String 클래스 객체 "정원석" 의 참조값 리턴하여 name 에 할당
String name = it.next();
int score = javaScore.get(name); //key 인 "정원석" 에 연계되는 value=70 을 리턴
System.out.println(name + " : " + score);
}
}
}
//result
HashMap 의 요소 개수 : 5
정원석 : 70
한원선 : 99
한홍진 : 97
이영희 : 98
황기태 : 34
import java.util.*;
class Student { //학생을 표현하는 클래스
int id;
String tel;
public Student(int id, String tel) {
this.id = id;
this.tel = tel;
}
//private 이였다면 이 메소드들은 우회하여 접근해야 할것이다.
public int getId() {return id;}
public String getTel() {return tel;}
}
public class HashMapStudentEx {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 학샐 이름과 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"));
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.print("검색할 이름?");
String name = scanner.nextLine(); //t사용자로부터 이름 입력
if(name.equals("exit"))
break; //while 문 벗어나 프로그램 종료
Student student = map.get(name); //이름에 해당하는 Student 객체 검색, 못찾으면 null 리턴
if(student == null)
System.out.println(name + "은/는 없는 사람입니다.");
else
System.out.println("id :" + student.getId() + "전화:" + student.getTel());
}
System.out.print("프로그램 종료!"); //코드 추가함!
scanner.close();
}
}
//result
검색할 이름?이재문
id :2전화:010-222-2222
검색할 이름?김남윤
id :3전화:010-333-3333
검색할 이름?이하이
이하이은/는 없는 사람입니다.
검색할 이름?exit
프로그램 종료!
LinkedList<E>
Vector vs ArrayList vs LinkedList 차이
1) Vector 와 List 의 차이점
- 동기화 처리 : 한개의 공용 자원을 두개의 스레드가 사용할때 반드시 한번에 하나씩 접근하도록
- 한개의 자원을 하나의 스레드가 사용할 떄는 굳이 동기화를 고려하지 않아도 됨
- Vector 는 무조건 동기화를 지원을 하기 떄문에, 단일 스레드를 사용하는 경우에는 ArrayList 나 LinkedList 보다 성능이 떨어진다.
2) Vector
- 자바 초기부터 제공하던 컬렉션
3) ArrayList
- 데이터를 배열의 복사에 의한 방법을 내부적으로 수행
- 데이터의 추가/삭제 시에 임시 배열을 내부적으로 생성하고 추가/삭제를 수행함
- -> 대용량 데이터 사용시에는 임시 배열에 복사를 수행해야 하는 관계로 성능이 떨어질 수 있다
- 각 데이터에 대한 인덱스를 이용할 수 있다는 장점이 있다.
4) LinkedList
- 시작과 끝 위치 정보를 가지고 있다.
- 현재 자료는 다음 자료의 위치 정보를 가지고 있으며, 내부적으로 모든 자료의 인덱스 정보를 가지고 있지는 않다
- 새로 샹성된 정보는 서로서로 연결된 참조값만 관리
- 데이터의 추가/삭제는 위치 정보만의 수정만으로 가능하기 떄문에 데이터의 추가/삭제 시 매우 유용
- 단점 : 순차적으로 자료를 찾는 과정에서 느려질 수 있다.
- 메모리를 생성하고 합치고 이런 과정이 없다!
Collection 클래스 활용[풀이]
import java.util.*;
public class CollectionsEx {
static void printList(LinkedList<String> list) { //myList 라는 참조변수를 인자 list 라는 참조변수가 전달 받아 함수 역할 수행
Iterator<String> iterator = list.iterator(); //LinkedList 는 0,1,2 와 같은 인덱스로 관리 불가! 입력할 때만 사용 가능
while(iterator.hasNext()) { //따라서, 시작점에서 출발하여 다음 정보가 있는지 순차적으로 확인
String e = iterator.next();
String separator;
if(iterator.hasNext())
separator = "->";
else
separator = "\n";
System.out.print(e+separator);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
LinkedList<String> myList = new LinkedList<String>();
myList.add("트랜스포머");
myList.add("스타워즈");
myList.add("매트릭스");
myList.add(0,"터미네이터");
myList.add(2,"아바타");
Collections.sort(myList); //요소 정렬
//sort(): static 메소드 이므로 클래스 이름으로 바로 호출
printList(myList); //정렬된 요소 출력
Collections.reverse(myList); //요소의 순서를 반대로
printList(myList); //요소 출력
int index = Collections.binarySearch(myList, "아바타")+1; //"d아바타" 의 인텍스 검색
System.out.println("아바타는" + index + "번째 요소입니다.");
}
}
//result
매트릭스->스타워즈->아바타->터미네이터->트랜스포머
트랜스포머->터미네이터->아바타->스타워즈->매트릭스
아바타는3번째 요소입니다.



<String> 의 소스 코드

스택을 제네릭 클래스로 작성하고, 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 {
public static void main(String[] args) {
// TODO Auto-generated method stub
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());
}
}
//result
LA
busan
seoul
5
3
1
[주의]
[풀이]
class GStack<T>{
int tos;
Object [] stck;
public GStack() {
tos = 0;
stck = new Object[2]; //추기 -> 2 로 변경
}
public void push(T item) {
if(tos == 2) { //추기 -> 2 로 변경
System.out.println("Stack is full, Unable to save[" + item + "]"); //추기
return;
}
stck[tos] = item;
tos++;
}
public T pop() {
if(tos == 0) {
System.out.println("Stack is empty:"); //추기
return null;
}
tos--;
return (T)stck[tos]; //stck[tos] 의 해시 코드 리턴
}
}
public class MyStack {
public static void main(String[] args) {
// TODO Auto-generated method stub
GStack<String> stringStack = new GStack<String>();
stringStack.push("seoul");
stringStack.push("busan");
stringStack.push("LA");
System.out.println("--1. Content stored in the stack --"); //추기
for(int n=0; n<2; n++) //3 을 2 로 수정
System.out.println(stringStack.pop());
System.out.println(); //추기
GStack<Integer> intStack = new GStack<Integer>(); //동적인 메모리 영역에 현재 설계한 Gstack 객체 매모리를 확보
// 기본 생성자가 자동 호출되어
// tos 가 0으로 초기화, oBject 객체가 관리하는 2개의 배열 참조 공간을 생성하고, 배열을 관리할 수 있는 stack[] 참조변수로 할당
intStack.push(1);
intStack.push(3);
intStack.push(5);
System.out.println("--2. Content stored in the stack --"); //추기
for(int n=0; n<3; n++)
System.out.println(intStack.pop());
}
}
//result
Stack is full, Unable to save[LA]
--1. Content stored in the stack --
busan
seoul
Stack is full, Unable to save[5]
--2. Content stored in the stack --
3
1
Stack is empty:
null
[바뀐코드 풀이]
GStack<String> stringStack = new GStack<String>();


따로 설명 안함, 우리가 해보기
분석하는 코드로 과제 나감
[과제 일부 7-2. 7-8]
length 가 1보다 크면 Not character 라고 해서 return 종료하도록 하고
알파벳 범위에 있는지 설정하고, 그 범위를 벗어나면 Invalid 출력하도록
switch 로 ch => case = A 로 접근
해시맵을 활용하는 방법
똑같은 이름이존재하면 해당 정보를 업데이트 부분이 중요