본문 바로가기

STUDY/국비과정

[JAVA 웹 개발 공부] 국비지원 27일차 - 컬렉션 프레임워크, HashSet, LinkedList, LinkedHashSet, HashMap

컬렉션 프레임워크(Collections Framework)

 

 

 

1. 컬렉션 프레임워크

자바에서 컬렉션 프레임워크(collection framework)란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미한다.
즉, 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것이다.
이러한 컬렉션 프레임워크는 자바의 인터페이스(interface)를 사용하여 구현된다.

 

2. 컬렉션 프레임워크의 핵심 인터페이스와 특징

인터페이스
특징
구현클래스
List
순서가 있는 데이터의 집합. 데이터의 중복을 허용한다.
예) 대기자 명단
ArrayList, LinkedList, Stack, Vector 등
Set
순서를 유지하지 않는 데이터의 집합. 데이터의 중복을 허용하지 않는다.
예) 양의 정수집합, 소수의 집합
HashSet, TreeSet 등
Map
키(key)와 값(value)의 쌍(pair)으로 이루어진 데이터의 집합
순서는 유지되지 않으며, 키는 중복을 허용하지 않고, 값은 중복을 허용한다.
예) 우편번호, 지역번호(전화번호)
HashMap, TreeMap, Hashtable, Properties 등

 

3. 컬렉션 클래스의 특징

렉션 특징
ArrayList 배열 기반, 데이터의 추가와 삭제에 불리. 순차적인 추가삭제는 제일 빠름. 임의의 요소에 대한 접근성(accessibility)이 뛰어남.
LinkedList 연결기반. 데이터의 추가와 삭제에 유리. 임의의 요소에 대한 접근성이 좋지 않다.
HashMap 배열과 연결이 결합된 형태. 추가, 삭제, 검색, 접근성이 모두 뛰어남. 검색에는 최고성능을 보인다.
TreeMap 연결기반. 정렬과 검색(특히 범위검색)에 적합. 검색성능은 HashMap 보다 떨어짐.
Stack Vector를 상속받아 구현
Queue LinkedList가 Queue인터페이스를 구현
Properties Hashtable을 상속받아 구현
HashSet HashMap을 이용해서 구현
TreeSet TreeMap을 이용해서 구현
LinkedHashMap
LinkedHashSet
HashMap과 HashSet에 저장순서유지기능을 추가

 

4. 컬렉션 프레임워크의 핵심 인터페이스와 특징

클래스
분류
종류
설명
Collection
List
ArrayList
ArrayList 클래스는 가장 많이 사용되는 컬렉션 클래스 중 하나이다.
ArrayList 클래스는 배열을 이용하기 때문에 데이터를 순차적으로 저장하며, 인덱스를 이용해 배열 요소에 빠르게 접근할 수 있다.
하지만 배열은 크기를 변경할 수 없는 인스턴스이므로, 크기를 늘리기 위해서는 새로운 배열을 생성하고 기존의 요소들을 옮겨야 하는 복잡한 과정을 거쳐야 한다.
물론 이 과정은 자동으로 수행되지만, 요소의 추가 및 삭제 작업에 걸리는 시간이 매우 길어지는 단점을 가진다.
LinkedList
LinkedList 클래스는 ArrayList 클래스가 배열을 이용하여 요소를 저장함으로써 발생하는 단점을 극복하기 위해 고안되었다.
LinkedList 클래스는 내부적으로 연결 리스트(linked list)를 이용하여 요소를 저장한다. 연결 리스트는 저장된 요소가 비순차적으로 분포되며, 이러한 요소들 사이를 링크(link)로 연결하여 구성한다.
linked list의 각 요소들은 자신과 연결된 다음 요소에 대한 참조(주소값)와 데이터로 구성되어 있어서 요소를 저장하거나 삭제할때 다음 요소의 참조만 변경하면 되므로 처리속도가 매우 빠르다.
하지만 linked list는 현재 요소에서 이전 요소로 접근하기가 매우 어렵다.
이 점을 보완한 것이 이중 연결 리스트(doubly linked list)이다.
이중 연결 리스트는 단순히 linked list에 이전 요소에 대한 참조변수를 하나 더 추가한 것이다. 각 요소에 대한 접근과 이동이 쉽기 때문에 많이 사용된다.
ArrayList
vs
LinkedList
*순차적으로 추가/삭제하는 경우에는 ArrayList가 LinkedList보다 빠르다.
*중간 데이터를 추가/삭제하는 경우에는 LinkedList가 ArrayList보다 빠르다.
*ArrayList가 LinkedList보다 접근시간(읽기)이 빠르다.
*ArrayList는 순차적인 추가삭제는 더 빠르며, 비효율적으로 메모리가 사용된다.
*LinkedList는 데이터가 많을수록 접근성이 떨어진다.
Vector
Vector 클래스는 JDK 1.0부터 사용해 온 ArrayList 클래스와 같은 동작을 수행하는 클래스이다. 현재의 Vector 클래스는 ArrayList 클래스와 마찬가지로 List 인터페이스를 상속받는다.
Stack
Vector 클래스를 상속받는 하위클래스이다. 후입선출(LIFO, last-in-first-out) 구조를 가진 데이터 구조이다. 즉 가장 마지막에 넣은 것이 가장 상위(우선순위)에 있게 되는 클래스이다. 사실 Stack 은 많이 사용되는 클래스는 아니지만, 마지막에요청받은 데이터를 바로 확인하려는 경우 등 특별한 목적을 위해 사용되기도 한다.
Set
HashSet
HashSet은 Set 인터페이스를 구현한 가장 대표적인 컬렉션이다.
HashSet 클래스는 해시 알고리즘(hash algorithm)을 사용하여 검색 속도가 매우 빠르다.
이러한 HashSet 클래스는 내부적으로 HashMap 인스턴스를 이용하여 요소를 저장한다. HashSet 클래스는 Set 인터페이스를 구현하므로, 요소를 순서에 상관없이 저장하고 중복된 값은 저장하지 않는다. 만약 요소의 저장 순서를 유지해야 한다면 LinkedHashSet 클래스를 사용하면된다.
TreeSet
TreeSet 클래스는 데이터가 정렬된 상태로 저장되는 이진 검색 트리(binary search tree)의 형태로 요소를 저장한다. 이진 검색 트리는 데이터를 추가하거나 제거하는 등의 기본 동작 시간이 매우 빠르다. TreeSet 클래스는 NavigableSet 인터페이스를 기존의 이진 검색 트리의 성능을 향상시킨 레드-블랙 트리(Red-Black tree)로 구현한다.
TreeSet 클래스는 Set 인터페이스를 구현하므로, 요소를 순서에 상관없이 저장하고 중복된 값은 저장하지 않는다.
Map
HashMap
HashMap 클래스는 Map 컬렉션 클래스에서 가장 많이 사용되는 클래스 중 하나이다. HashMap 클래스는 해시 알고리즘(hash algorithm)을 사용하여 검색 속도가 매우 빠르다.
HashMap 클래스는 Map 인터페이스를 구현하므로, 중복된 키로는 값을 저장할 수 없다. 하지만 같은 값을 다른 키로 저장하는 것은 가능하다.
Hashtable
Hashtable 클래스는 HashMap 클래스와 같은 동작을 하는 클래스이다.
현재의 Hashtable 클래스는 HashMap 클래스와 마찬가지로 Map 인터페이스를 상속받는다.
TreeMap
TreeMap은 이진검색트리의 형태로 키와 값의 쌍으로 이루어진 데이터를 저장한다. 그래서 검색과 정렬에 적합한 컬렉션 클래스이다.
검색에 관련한 대부분의 경우에서 HashMap이 TreeMap보다 더 뛰어나다.
다만 범위검색이나 정렬이 필요한 경우에는 TreeMap을 사용하는 것이 좋다.

 

 

 

HashSet 연습

 

*HashSet 생성

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Main {
	public static void main(String[] args) {
		Set<String> set = new HashSet<>();
		set.add("둘리");
		set.add("가나다");
		set.add("도우너");
		set.add("길동");
		set.add("둘리");
		set.add("둘리"); // 중복 안됨 
		System.out.println(set.add("둘리")); // false
		System.out.println(set.size()); // 4
		System.out.println(set.toString()); // [둘리, 길동, 가나다, 도우너]
		// set은 순서가 없음 index 개념이 없음 
		// 집합
//		set.get(0);
		
		Iterator<String> iterator = set.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.next());
		}
		
		for (String elem : set) {
			System.out.println(elem);
		}
	}
}

 

*HashSet 합집합, 교집합 구하기

import java.util.HashSet;
import java.util.Set;

public class Main2 {
	public static void main(String[] args) {
		Set<String> set1 = new HashSet<>();
		set1.add("가");
		set1.add("나");
		set1.add("다");
		
		Set<String> set2 = new HashSet<>();
		set2.add("다");
		set2.add("라");
		set2.add("마");
		
		// 합집합 구하기
		Set<String> union = new HashSet<>();
		union.addAll(set1);
		union.addAll(set2);
		System.out.println(union.toString()); // [가, 다, 나, 마, 라]
		System.out.println(union.contains("가")); // true
		
		// 교집합 구하기
		Set<String> duplicate = new HashSet<>();
		duplicate.addAll(set1);
		duplicate.retainAll(set2); // 오른쪽 존재하는 원소빼고는 다 제거
		System.out.println(duplicate); // [다]
	}
}

 

*로또 중복되지 않는 정수(1 ~ 45사이)  6개 뽑기

// 중복되지 않는 정수(1 ~ 45사이) 6개를 원소로 가지는 집합 만들기
Set<Integer> lotto = new HashSet<>();
Random random = new Random();

while (lotto.size() < 6) {
    int number = random.nextInt(45) + 1;
    lotto.add(number);
}

List<Integer> list = new ArrayList<>(lotto);
Collections.sort(list); // 리스트 정렬
System.out.println(list); // [4, 10, 19, 28, 32, 43]

 

 

 

LinkedList 연습

 

LinkedList는 ArrayList 보다 추가나 삭제가 상대적으로 빠르나, link로 연결되어 있어서 원소를 살펴보는 것은 상대적으로 느리다. LinkedList와 ArrayList는 똑같으나 속도의 차이가 있다.

import java.util.LinkedList;
import java.util.List;

public class Main {
	public static void main(String[] args) {
		List<String> list = new LinkedList<>();
		list.add("가");
		list.add("가");
		list.add("나");
		list.add("다");
		
		System.out.println(list); // [가, 가, 나, 다]
        
		list.remove(1);
		System.out.println("삭제후:" + list); // 삭제후:[가, 나, 다]
		
		list.add(0, "젤처음 추가");
		System.out.println("추가후:" + list); // 추가후:[젤처음 추가, 가, 나, 다]
	}
}

 

 

 

Queue 연습

 

*줄세우기(제일 먼저 들어간 친구가 제일 앞)

import java.util.LinkedList;
import java.util.Queue;

public class Main2 {
	public static void main(String[] args) {
		Queue<String> queue = new LinkedList<>();
		queue.add("첫번째 대기");
		queue.offer("두번째 대기");
		queue.add("세번째 대기");
		
		System.out.println(queue); // [첫번째 대기, 두번째 대기, 세번째 대기]
	}
}

 

 

*queue.remove(); // 예외

queue.poll(); // null

Deque 후입선출

import java.util.LinkedList;
import java.util.Queue;

public class Main2 {
	public static void main(String[] args) {
		Queue<String> queue = new LinkedList<>();
		queue.add("첫번째 대기");
		queue.offer("두번째 대기");
		queue.add("세번째 대기");
		
		System.out.println(queue); // [첫번째 대기, 두번째 대기, 세번째 대기]
		String first = queue.remove();
		String second = queue.poll();
		
		System.out.println(first);
		System.out.println(second);
		
		System.out.println("현재 대기:" + queue); // 현재 대기:[세번째 대기]
		
//		queue.remove(); // 예외던짐
//		queue.poll(); // null
		System.out.println("가장 앞 녀석:" + queue.element()); // 가장 앞 녀석:세번째 대기
		System.out.println("현재 대기:" + queue);
	}
}

 

 

 

LinkedHashSet 연습

 

import java.util.LinkedHashSet;
import java.util.Set;

public class Main3 {
	public static void main(String[] args) {
		Set<String> set = new LinkedHashSet<>();
		
		set.add("가");
		set.add("가");
		set.add("가");
		set.add("가");
		set.add("가");
		set.add("나");
		set.add("나");
		set.add("다");
		set.add("라");
		
		System.out.println(set); // [가, 나, 다, 라]
		// 추가한 순서를 기억한다. 인덱스는 없음.
	}
}

 

 

 

Comparable, Comparator

 

*책 정렬하기

public class Book implements Comparable<Book> {
	private String title;
	private int price;
	public Book(String title, int price) {
		super();
		this.title = title;
		this.price = price;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	@Override
	public String toString() {
		return "Book [title=" + title + ", price=" + price + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + price;
		result = prime * result + ((title == null) ? 0 : title.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Book other = (Book) obj;
		if (price != other.price)
			return false;
		if (title == null) {
			if (other.title != null)
				return false;
		} else if (!title.equals(other.title))
			return false;
		return true;
	}
	@Override
	public int compareTo(Book o) {
		return this.price - o.price;
	}
}

 

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Main5 {
	public static void main(String[] args) {
		Set<Book> set = new TreeSet<>();
		set.add(new Book("책1", 3000));
		set.add(new Book("책2", 4000));
		set.add(new Book("책2", 4000));
		set.add(new Book("책3", 2000));
		
		System.out.println(set); 
		// [Book [title=책3, price=2000], Book [title=책1, price=3000], Book [title=책2, price=4000]]
		
		Comparator<Book> comparator = new Comparator<Book>() {
			@Override
			public int compare(Book o1, Book o2) {
				return o1.getTitle().compareTo(o2.getTitle());
			}
		};
		Set<Book> set2 = new TreeSet<>(comparator);
		set2.add(new Book("책1", 3000));
		set2.add(new Book("책2", 4000));
		set2.add(new Book("책2", 4000));
		set2.add(new Book("책3", 2000));
		
		System.out.println(set2);
		// [Book [title=책1, price=3000], Book [title=책2, price=4000], Book [title=책3, price=2000]]
		
		System.out.println(set.equals(set2)); // true
	}
}

 

 

 

HashMap 연습

 

HashMap은 Key들이 일정한 순서가 없음 중복되지않게 나열해준다.

import java.util.HashMap;
import java.util.Map;

public class Main6 {
	public static void main(String[] args) {
		// 1 김승규
		// 2 윤종규
		// 12 송범근
		// 21 조현우
		// 20 권경원
		// 15 김문환
		// 5 정우영
		// 25 정우영
		Map<Integer, String> player = new HashMap<Integer, String>(); // <>안 생략 가능
		player.put(1, "김승규");
		player.put(21, "조현우");
		player.put(5, "정우영");
		player.put(25, "정우영");
		
		System.out.println(player.size()); // 4
		
		System.out.println(player.get(5)); // 정우영
		System.out.println(player.get(1)); // 김승규
		System.out.println(player.containsKey(27)); // 키값이 존재하는지 알아보기 false
		
		player.put(1, "새밸류");
		System.out.println(player.get(1)); // 새밸류
	}
}

 

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Main6 {
	public static void main(String[] args) {
		// 1 김승규
		// 2 윤종규
		// 12 송범근
		// 21 조현우
		// 20 권경원
		// 15 김문환
		// 5 정우영
		// 25 정우영
		Map<Integer, String> player = new HashMap<Integer, String>(); // <>안 생략 가능
		player.put(1, "김승규");
		player.put(21, "조현우");
		player.put(5, "정우영");
		player.put(25, "정우영");
		
		Set<Entry<Integer, String>> entrySet = player.entrySet();
		for (Entry<Integer, String> e : entrySet) {
			System.out.println(e.getKey() + "=" + e.getValue());
		}
		System.out.println(entrySet); // [1=김승규, 21=조현우, 5=정우영, 25=정우영]
	}
}

 

import java.util.HashMap;
import java.util.Map;

public class Main7 {
	public static void main(String[] args) {
		String line = "Hello. I'm a java developer";
		Map<Character, Integer> map = new HashMap<>();
		for (int i = 0; i < line.length(); i++) {
			char c = line.charAt(i);
			
			if (!map.containsKey(c)) {
				map.put(c, 1);
			} else {
				int count = map.get(c);
				count++;
				map.put(c, count);
			}
		}
		System.out.println(map.toString());
	}
}
{ =4, a=3, d=1, e=4, '=1, H=1, I=1, j=1, l=3, m=1, .=1, o=2, p=1, r=1, v=2}

 

 

*메뉴 주문받기

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;

// 중국집
// 짜장면 : 6000
// 짬뽕 : 7000
// 볶음밥 : 6500

// 주문
// 짜장면 1개
// 짬뽕 2개
// 볶음밥 3개

// 총 가격 출력

public class Main8 {
	public static void main(String[] args) {
		Map<String, Integer> menu = new HashMap<>();
		menu.put("짜장면", 6000);
		menu.put("짬뽕", 7000);
		menu.put("볶음밥", 6500);
		menu.put("삼선짜장", 6500);
		
		Scanner scan = new Scanner(System.in);
		System.out.println("<메뉴>");
		
		for (Entry<String, Integer> entry : menu.entrySet()) {
			System.out.println(entry.getKey() + ":" + entry.getValue());
		}
		System.out.println("-----------");
		
		Map<String, Integer> order = new HashMap<>();
		Iterator<String> iter = menu.keySet().iterator();
		while(iter.hasNext()) {
			String name = iter.next();
			System.out.print(name + "개수:");
			int count = scan.nextInt();
			
			order.put(name, count);
		}
		int sum = 0;
		for (Entry<String, Integer> e : order.entrySet()) {
			sum += menu.get(e.getKey()) * e.getValue();
		}
		System.out.println(sum);
	}
}
<메뉴>
짬뽕:7000
볶음밥:6500
짜장면:6000
삼선짜장:6500
-----------
짬뽕개수:1
볶음밥개수:2
짜장면개수:2
삼선짜장개수:2
45000

*entrySet() 메서드는 key와 value의 값 모두 출력

*keySet() 메서드는 key의 값만 출력

*Iterator는 자바의 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법

 

 

*모스부호 변환기

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

// 사용자 문자열(영단어) 입력 -> 모스부호 변환기
public class Main10 {
	public static void main(String[] args) {
		Map<Character, String> map = new HashMap<>();
		map.put('A', "ㆍ-");
		map.put('B', "-ㆍㆍㆍ");
		map.put('C', "-ㆍ-ㆍ");
		map.put('D', "-ㆍㆍ");
		map.put('E', "ㆍ");
		map.put('F', "ㆍㆍ-ㆍ");
		map.put('G', "--ㆍ");
		map.put('H', "ㆍㆍㆍㆍ");
		map.put('I', "ㆍㆍ");
		map.put('J', "ㆍ---");
		map.put('K', "-ㆍ-");
		map.put('L', "ㆍ-ㆍㆍ");
		map.put('M', "--");
		map.put('N', "-ㆍ");
		map.put('O', "---");
		map.put('P', "ㆍ--ㆍ");
		map.put('Q', "--ㆍ- ");
		map.put('R', "ㆍ-ㆍ ");
		map.put('S', "ㆍㆍㆍ");
		map.put('T', "- ");
		map.put('U', "ㆍㆍ-");
		map.put('V', "ㆍㆍㆍ-");
		map.put('W', "ㆍ-- ");
		map.put('X', "-ㆍㆍ- ");
		map.put('Y', "-ㆍ-- ");
		map.put('Z', "--ㆍㆍ");
		
		Scanner scan = new Scanner(System.in);
		System.out.print("영단어 입력: ");
		String input = scan.next();
		String upper = input.toUpperCase();
		
		for (int i = 0; i < input.length(); i++) {
			System.out.println(map.get(upper.charAt(i)));
		}
	}
}
영단어 입력: sos
ㆍㆍㆍ
---
ㆍㆍㆍ