JAVA

컬렉션 프레임워크(List, Set, Map)

황민준 2022. 9. 24. 20:04

collection frameworkcollection interface를 최상위로 하는 자료구조 interface이다.

 

 

전체 구조

 

컬렉션 프레임워크의 장점

다양한 자료구조를 가지고 있고 가변적인 크기를 통해 유연한 사용을 할 수 있다.

 

컬렉션 프레임워크 특징

컬렉션 프레임워크에서 데이터 추가, 삭제, 검색 방법이 비슷하다

왜냐하면 최상위의 컬렉션 프레임워크가 하위의 List, Set interface를 구현하기 때문이다.

이 때 인터페이스 구현 시 장점은? 필수 method를 강제할 수 있다.

 

ArrayList

index를 사용한다는 점에서 Array와 비슷하다.

가변적인 크기를 가지고 있어 크기가 무제한으로 늘어난다.

값을 추가/삭제 하면 index1씩 당겨지거나 미뤄지게 된다.

따라서 빈번한 값의 추가/삭제는 무리가 간다.

배열은 같은 인덱스에 중복해서 값을 넣을 경우 값이 덮어쓰게 된다.

반면에 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");
		}
		
	}

}

 

ArrayListVector의 차이점

모든 기능은 똑같지만 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()); // 리스트가 비워졌는지 참/거짓으로 반환
		
	}

}

 

ArrayListLinkedList의 차이점

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("끝");
		}
		
	}

}