컬렉션 프레임워크(List, Set, Map)
collection framework는 collection interface를 최상위로 하는 자료구조 interface이다.
컬렉션 프레임워크의 장점
다양한 자료구조를 가지고 있고 가변적인 크기를 통해 유연한 사용을 할 수 있다.
컬렉션 프레임워크 특징
컬렉션 프레임워크에서 데이터 추가, 삭제, 검색 방법이 비슷하다
왜냐하면 최상위의 컬렉션 프레임워크가 하위의 List, Set interface를 구현하기 때문이다.
이 때 인터페이스 구현 시 장점은? 필수 method를 강제할 수 있다.
ArrayList
index를 사용한다는 점에서 Array와 비슷하다.
가변적인 크기를 가지고 있어 크기가 무제한으로 늘어난다.
값을 추가/삭제 하면 index가 1씩 당겨지거나 미뤄지게 된다.
따라서 빈번한 값의 추가/삭제는 무리가 간다.
배열은 같은 인덱스에 중복해서 값을 넣을 경우 값이 덮어쓰게 된다.
반면에 ArrayList는 같은 인덱스에 중복해서 값을 넣을 경우 계속 밀리게 되어 덮어쓰지 않는다.
list.size() : 크기를 반환한다.
list.get(index) : 인덱스의 값을 반환한다.
list.remove(index) : 인덱스의 값을 삭제하고 그 값을 반환한다.
list.indexOf(값) : 특정한 값이 있는 인덱스를 반환한다.
list.contains(값) : 값의 포함 여부를 참/거짓으로 반환한다.
list.set(인덱스,값) : 특정 인덱스의 값을 입력 값으로 변경한다.
list.clear() : 리스트를 비운다.
list.isEmpty() : 리스트가 비워져 있는지 참/거짓으로 반환한다.
package chap09.ex01.arList;
import java.util.ArrayList;
import java.util.List;
public class ArrayList01 {
public static void main(String[] args) {
//ArrayList<String> list = new ArrayList<String>();
List<String> list = new ArrayList<String>(3); // 크기 지정 가능, 안 해도 상관 없음
String[] arr = new String[3];
// 데이터 추가
arr[0] = "test";
list.add("collection"); //0
list.add("thread"); //1
list.add("java IO"); //2
list.add("NETWORK"); //3 <- 배열 같았으면 Exception 발생
list.add(3, "lamda"); //배열같은 경우에는 덮어쓰게 된다. 그런데 리스트에서는 ([3]lamda [4]NETWORK)
//리스트 개수 : list.size()
//특정 인덱스 값 꺼내오기 : list.get(index)
//이것들을 활용해서 리스트 안에 값을 하나씩 가져오기
for(int i=0 ; i<list.size() ; i++) {
System.out.print(list.get(i) + "\t");
}
System.out.println();
System.out.println("삭제한 값 : " + list.remove(2)); // 인덱스로 삭제하면 삭제한 값을 반환한다.
System.out.println("삭제 성공 여부 : " + list.remove("collection")); // 값으로 삭제하면 성공 여부를 반환한다.
for (String item : list) {
System.out.print(item + "\t");
}
}
}
Array에서 ArrayList로 변환하는 방법
Array에 담겨져 있는 모든 값을 한 번에 저장하기 위해서 Array -> List -> ArrayList 형태로 변환한다.
우선 List로 변환하기 위해서 Arrays.asList(배열)로 변환한다.
이 때 List는 인터페이스이기 때문에 값을 확인하는 것은 가능하지만 추가/삭제가 불가능하다.
추가/삭제를 하기 위해서 ArrayList로 변환해야 하는데 이 때 addAll(리스트)를 사용하면 모든 값을 한 번에 추가할 수 있다.
package chap09.ex01.arList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayList02 {
// array -> ArrayList 변환
public static void main(String[] args) {
// 가끔 배열을 ArrayList로 변환할 경우도 있다.
String[] arr = {"List","Set","Map"};
// Array -> List -> ArrayList
//1. Array -> List (interface)
List<String> list = Arrays.asList(arr);
System.out.println(list.size());
System.out.println(list.get(1));
//list.add("collection"); // List는 인터페이스이기 때문에 보는 것만 가능하고, 쓰지는 못한다.
//2. List -> ArrayList
// List에 있는 값을 뽑아서 ArrayList에 넣어줘야 한다.
ArrayList<String> arrList = new ArrayList<>();
arrList.addAll(list); // addAll로 List를 전부 한 번에 넣을 수 있다.
arrList.add("collection");
for (String item : arrList) {
System.out.print(item+"\t");
}
}
}
ArrayList와 Vector의 차이점
모든 기능은 똑같지만 ArrayList는 다중 유저의 사용을 허용하고 Vector는 다중 유저의 사용을 허용하지 않는다.
package chap09.ex01.arList;
import java.util.Vector;
public class ArrayList03 {
public static void main(String[] args) {
Vector<Integer> score = new Vector<Integer>();
score.add(70); //0
score.add(50); //1
score.add(80); //2
score.add(90); //3
score.add(100); //4
score.add(90); //5
// 검색
// indexOf() : 특정한 값을 찾아 인덱스를 반환
System.out.println(score.indexOf(90));
// contains() : 포함 여부를 참/거짓으로 반환
System.out.println(score.contains(30));
// 수정
score.set(3, 95); // 특정 인덱스를 특정 값으로 변경
//리스트 비우기
//[10][20][30][4][5]
score.clear(); // 리스트를 깔끔하게 비운다.
// 비워졌는지 확인
System.out.println(score.isEmpty()); // 리스트가 비워졌는지 참/거짓으로 반환
}
}
ArrayList와 LinkedList의 차이점
ArrayList는 배열 형식으로 저장이 되고 LinkedList는 각 노드에 데이터가 저장되고 다음 노드를 가리키는 포인터있어서 연결되어 있는 형식이다.
저장된 데이터가 많아질수록 LinkedList가 더 빠르다. -> 데이터가 추가/삭제될 경우 포인터가 가리키는 값만 바꾸면 되기 때문에
package chap09.ex02.link;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class BenchMark {
public static void main(String[] args) {
// A와 B 달리기 측정
// 선수 준비
ArrayList<String> arr = new ArrayList<String>();
LinkedList<String> lnk = new LinkedList<String>();
// 장소 준비 (데이터 100개 채우기)
for (int i = 0; i < 100; i++) {
arr.add("data");
lnk.add("data");
}
// 시계 준비 (걸린 시간 = 도착 시간 - 출발 시간)
long startTime = 0;
long endTime = 0;
// ArrayList 의 출발 시간 기록
startTime = System.currentTimeMillis(); // 현재 시간을 1000/1 초로 환산
// 달리기
for (int i = 1; i <= 100000; i++) {
arr.add(55, "add data");
}
// ArrayList 도착 시간 기록
endTime = System.currentTimeMillis();
System.out.println("ArrayList 걸린 시간 : " + (endTime - startTime) + " ms");
// LinkedList 의 출발 시간 기록
startTime = System.currentTimeMillis(); // 현재 시간을 1000/1 초로 환산
// 달리기
for (int i = 1; i <= 100000; i++) {
lnk.add(55, "add data");
}
// LinkedList 도착 시간 기록
endTime = System.currentTimeMillis();
System.out.println("LinkedList 걸린 시간 : " + (endTime - startTime) + " ms");
}
}
HashSet
Set은 중복된 값을 허용하지않고 값의 순서가 없다.
따라서 검색 기능을 사용할 수 없기 때문에 iterator를 사용해야 한다.
ArrayList와 마찬가지로 collection interface를 구현받기 때문에 사용하는 메소드가 거의 같다.
iterator 시키면 next()를 통해 값을 꺼내올 수 있다.
hasNext()를 통해 더 이상 꺼내올 값이 없는지 확인할 수 있다.
향상된 for문을 사용하면 더 편하게 꺼내올 수 있다.
package chap09.ex03.set;
import java.util.Iterator;
import java.util.Set;
class Member{
}
public class HashSet {
public static void main(String[] args) {
// 현재 클래스와 사용할 클래스 이름이 동일하면 자동으로 패키지명을 포함한 클래스명이 나타난다.
// 가급적이면 겹치지 않는 클래스 이름을 사용해야 한다.
Set<String> set = new java.util.HashSet<>();
/*데이터 추가 - java, jsp, oracle, mvc, java*/
set.add("java");
set.add("jsp");
set.add("oracle");
set.add("mvc");
set.add("java"); // 중복
System.out.println(set.size());
Set<Member> members = new java.util.HashSet<>();
members.add(new Member());
members.add(new Member());
members.add(new Member());
System.out.println(members.size());
// Set에서 하나씩 꺼내보기 - 순서가 없기 때문에 Set에서는 검색이 없다.
// 방법 1. set -> iterator화 시킨다. -> next()를 이용해서 꺼낸다.
Iterator<String> iter = set.iterator();
while (iter.hasNext()) {
String item = iter.next();
System.out.print(item+" | ");
}
System.out.println();
// 방법 2. for -> 향상된 for문을 사용하면 iterator와 관련된 작업을 생략할 수 있다.
for (String item : set) {
System.out.print(item+" | ");
}
}
}
HashMap
collection interface를 구현받지 않고 Map interface을 구현받기 때문에 조금 다른 메소드를 사용한다.
package chap09.ex04.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapMain {
public static void main(String[] args) {
// HashMap은 Map 인터페이스를 구현받고 있다.
Map<String, Integer> map = new HashMap<String, Integer>();
// 데이터 추가시 add가 아닌 put을 사용 -> collection interface를 구현받지 않기 때문에
map.put("kim", 63);
map.put("lee", 26);
map.put("park", 26); // 값의 중복은 허용
map.put("kim", 30); // 키의 중복은 허용하지 않는다. (중복 발생시 나중 것으로 덮어쓴다.)
// 크기
System.out.println("map : " + map.size());
// 특정 값 가져오기 : 키를 통해서 값을 찾는다.(Dictionary)
System.out.println("kim의 나이 : " + map.get("kim"));
// 특정 값 삭제하기 : 키를 통해서 값을 지운다. -> 어떤 값을 지웠는지 알려준다.
System.out.println(map.remove("kim"));
// 모든 값을 출력 1 : key를 Set으로 가져온다. -> iterator로 분리 -> key를 하나씩 가져와서 해당하는 value를
// 찾는다.
Set<String> keySet = map.keySet();
Iterator<String> keyIter = keySet.iterator();
while (keyIter.hasNext()) {
String key = keyIter.next();
System.out.print(key + " : " + map.get(key) + "\t");
}
System.out.println();
// 모든 값을 출력 2 : map을 entrySet으로 만든다. -> iterator로 분리(K,V) -> 키와 값을 각각 추출
Set<Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Entry<String, Integer>> entry = entrySet.iterator();
while (entry.hasNext()) {
Entry<String, Integer> item = entry.next();
String key = item.getKey();
int val = item.getValue();
System.out.print(key + " : " + val + "\t");
}
System.out.println();
// 모든 값을 출력 3 : key를 Set으로 가져온다. -> key를 하나씩 가져와서 해당하는 value를 찾는다.
for (String key : map.keySet()) {
System.out.print(key + " : " + map.get(key) + "\t");
}
if (!map.isEmpty()) {
map.clear();
}
}
}
Entry
Map 형태에서 키와 값 쌍으로 가져오는 형태를 엔트리라고 한다.
entrySet()을 통해 엔트리 형태로 가져올 경우 Set타입이기 때문에 iterator를 해야 값을 가져올 수 있다.
map.put(key,value) : 값을 추가한다.
map.get(key) : 키를 통해 값을 찾는다.
map.remove(key) : 키를 통해 값을 제거하고 값을 반환한다.
map.entrySet() : 엔트리 형식으로 값을 가져온다.
package chap09.ex04.map;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
public class HashTableMain {
public static void main(String[] args) {
Hashtable<String, Integer> map = new Hashtable<>();
//이름, 점수
map.put("홍길동1", 99);
map.put("홍길동2", 80);
map.put("홍길동3", 85);
map.put("홍길동4", 100);
map.put("홍길동5", 70);
map.put("홍길동6", 60);
map.put("홍길동7", 99);
map.put("홍길동8", 75);
map.put("홍길동9", 95);
map.put("홍길동10", 45);
//홍길동4의 점수
System.out.println(map.get("홍길동4"));
//홍길동4가 있는지 확인
System.out.println(map.containsKey("홍길동4"));
//99점의 학생이 존재하는가?
System.out.println(map.containsValue(99));
//특정 키를 가지고 해당 값을 찾는 법은 get()을 통해 가능
//특정 값을 가지고 해당 키를 찾는 방법은?
//99점의 학생 이름을 찾으세요.
//1. 키의 값을 하나씩 뽑아서 값을 찾아 대조하는 방식
for (String item : map.keySet()) {
if(map.get(item)==99) {
System.out.print(item + "\t");
}
}
System.out.println();
//2. 엔트리<키,값>를 뽑아서 값을 찾아 일치하는 값의 키를 가져오는 방식
Set<Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Entry<String, Integer>> entry = entrySet.iterator();
while(entry.hasNext()) {
Entry<String, Integer> item = entry.next();
if(item.getValue()==99) {
System.out.print(item.getKey() + "\t");
}
}
}
}
스택(stack)
- LIFO(Last In First Out) 형태의 자료구조다.
push()를 통해 값을 넣고 pop()을 통해 값을 뺀다.
peek()을 통해 값만 확인하고 다시 넣을 수 있다.
package chap09.ex05.stack;
public class Towel {
private String color;
public Towel(String color) {
this.color = color;
}
public String getColor() {
return color;
}
}
package chap09.ex05.stack;
import java.util.Stack;
public class TowelBox {
public static void main(String[] args) {
Stack<Towel> box = new Stack<Towel>();
// 수건넣기(push)
box.push(new Towel("red"));
box.push(new Towel("orange"));
box.push(new Towel("yellow"));
box.push(new Towel("green"));
box.push(new Towel("blue"));
box.push(new Towel("navy"));
box.push(new Towel("purple"));
System.out.println(box.size());
while(!box.isEmpty()) {
//Towel towel = box.pop();
//System.out.println(towel.getColor()+"색 수건을 꺼낸다. " + box.size()+"장 남음");
//메소드 체이닝
System.out.println(box.pop().getColor()+"색 수건을 꺼낸다. " + box.size()+"장 남음");
//peek() : 뽑아서 확인하고 다시 넣는다.
//System.out.println(box.peek().getColor()+"색 수건을 꺼낸다. " + box.size()+"장 남음");
}
}
}
큐(queue)
FIFO(First In First Out) 형태의 자료구조이다.
자바에선 LinkedList로 구현되어 있다.
offer()를 통해 값을 넣고 poll()을 통해 값을 뺀다.
package chap09.ex06.queue;
public class Job {
private String command;
private String to;
public Job(String command, String to) {
this.command = command;
this.to = to;
}
public String getCommand() {
return command;
}
public String getTo() {
return to;
}
}
package chap09.ex06.queue;
import java.util.LinkedList;
import java.util.Queue;
public class JobQueue {
public static void main(String[] args) {
Queue<Job> queue = new LinkedList<Job>();
// 해야할 일을 큐에 넣어보자
queue.offer(new Job("send SMS", "Alice"));
queue.offer(new Job("send Mail", "Bryan"));
queue.offer(new Job("send SMS", "Criss"));
queue.offer(new Job("send Talk", "Denis"));
queue.offer(new Job("send SMS", "Erick"));
try {
while(!queue.isEmpty()) {
//peek() : stack과 마찬가지로 빼서 확인 후 다시 넣는다.
Job job = queue.poll();
String command = job.getCommand();
String to = job.getTo();
System.out.println(command + " to " +to);
System.out.println("잔여 작업 : " + queue.size());
}
} catch (Exception e) {
System.out.println("객체 추출 구문을 확인해주세요.");
e.printStackTrace();
} finally {
System.out.println("끝");
}
}
}