Collection 인터페이스 2

Jan 29, 2024
Collection 인터페이스 2

1. Set 인터페이스 ★ + 해쉬테이블

순서에 상관없이 데이터만 저장하고 싶은 경우 사용하는 자료구조 중복된 요소를 허용하지 않는 데이터 구조(unique)로, 동일한 요소 중복 저장 XXX -> 유일성 검사, Set은 검색 속도가 빨라 요소의 존재 유무를 확인하는 데에도 유용하다. ex) {1, 2, 3}이라는 Set이 있다고 가정해 보자. 이 Set에 1을 추가하려고 하면 이미 1이라는 요소가 존재하기 때문에 중복이 허용되지 않아 추가가 불가능!
💡
HashSet, TreeSet, LinkedHashSet의 3가지 구현이 제공된다. (클래스들) HashSet : 내부적으로 해시 테이블을 사용하여 요소들을 저장하고 관리. 성능면에서 가장 우수! but 원소들의 순서가 일정하지 않은 단점 O
 

 
💡
[ 해쉬 테이블 ] 은 요소를 고유한 키(Key)와 연결하여 저장하는 자료구조. 하지만 HashSet에서는 요소를 키-값 쌍으로 저장하지 않는다. HashSet은 요소 자체를 키로 사용하여 중복을 허용하지 않고 저장한다.
💡
[ 해쉬 테이블 == 해싱 테이블이란? ] 키-값 쌍으로 데이터를 저장하는 자료구조로, 각 요소의 해시 코드(Hash Code)를 기반으로 요소를 저장하고 검색한다.
 

 
[ HashSet 코드 ]
package ex08; import java.util.HashSet; public class Test10 { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); set.add("Milk"); set.add("Bread"); set.add("Butter"); set.add("Cheese"); set.add("Ham"); set.add("Ham"); System.out.println(set); if (set.contains("Ham")) { //true니까 sout 작동이 되겠지 System.out.println("Ham도 포함되어 있음"); } } }
notion image
💡
HashSet은 순서를 보장하지 않고 중복된 요소를 허용하지 않는 자료구조 따라서 데이터가 입력된 순서대로 출력되지 않고, 순서 상관없이 출력 HashSet은 순서를 보장하진 않지만, 실행할 때마다 결과값의 순서가 바뀌지도 않는다. HashSet 내부의 해시 함수나 버킷(요소들이 저장되는 공간)의 크기 등에 의해 결정되기 때문!
 
[ addAll() - 합집합, retainAll() - 교집합 ] - Set 메소드
notion image
addAll() 메서드 : 현재 컬렉션에 다른 컬렉션의 모든 요소를 추가하는 역할 retainAll() 메서드 : 현재 컬렉션과 주어진 컬렉션의 교집합인 요소들만을 유지하는 역할
 
[ 집합 코드 ]
package ex01; import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class Test10 { public static void main(String[] args) { Set<Integer> s1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 7, 9)); Set<Integer> s2 = new HashSet<>(Arrays.asList(2, 4, 6, 8)); s1.addAll(s2); System.out.println(s1); } }
notion image
 

 
package ex01; import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class Test10 { public static void main(String[] args) { Set<Integer> s1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 7, 9)); Set<Integer> s2 = new HashSet<>(Arrays.asList(2, 4, 6, 8)); s1.retainAll(s2); System.out.println(s1); } }
notion image
 
[ 중복된 단어 검출하기 ]
package ex08; import java.util.HashSet; import java.util.Set; public class Test12 { public static void main(String[] args) { Set<String> s = new HashSet<>(); String[] sample = {"사과", "사과", "바나나", "토마토"}; for (String a : sample) { if (!s.add(a)) { //add에 안들어가는 애는 중복된 단어로 판단하겠다 System.out.println("중복된 단어 : " + a); } } System.out.println("중복되지 않은 단어 개수 : " + s.size()); System.out.println("중복되지 않은 단어 : " + s); } }
notion image
 

 
notion image
notion image
 
[ Set으로 로또 ] 수정이요
package ex14; import java.util.*; public class StreamEx01 { public static void main(String[] args) { // HashMap<String, Object> data = new HashMap<>(); Set<Integer> lotto = new HashSet<>(); Random r = new Random(); while (true) { int num = r.nextInt(45)+1; lotto.add(num); if (lotto.size() == 6) { break; } List<Integer> arr = new ArrayList<>(lotto); Collections.sort(arr); arr.stream().forEach(i -> System.out.println(i + " ")); } } }
코드 잘못됨 수정하기
 

2. Map 인터페이스 (Collection 인터페이스 구현하지 않음) ★

키(key)와 값(value)을 하나의 쌍으로 묶어서 데이터를 저장한다. ex) 사전과 같은 자료구조. 사전처럼 단어가 있고(key), 단어에 대한 설명(value) 키가 제시되면 Map은 값을 반환한다. (키 - Id, 값 - 비번) 1. 중복된 키는 허용하지 않는다. (unique) (Map의 내부 동작 원리와 설계에 기인) 2. 하나의 키에는 하나의 값만 연결된다. 3. 순서를 보장하지 않는다. 즉, 키-값 쌍은 순서가 유지되지 않을 수 있다. 4. null 값을 키로 사용할 수 있다. 하지만, 중복을 허용하지 않기 때문에 null 키는 하나만 존재해야 한다.
💡
HashMap ★ 클래스는 순서를 보장하지 않으며, 키와 값은 null 값을 가질 수 있다. 또한, 키는 중복될 수 없지만 값은 중복될 수 있다.
💡
* Map 인터페이스 → 자료 구조를 정의하는 인터페이스 * map() 메소드 → Stream에서 제공되는 메소드. 가공하는 것
 
[ Map 코드 ]
package ex08; import java.util.HashMap; import java.util.Map; public class Test13 { public static void main(String[] args) { Map<String, String> map = new HashMap<>(); map.put("kim", "1234"); map.put("park", "pass"); map.put("lee", "word"); System.out.println(map.get("lee")); //키를 이용해서 값을 뽑음 > 값만 출력 for (String key : map.keySet()) { String value = map.get(key); System.out.println("key = " + key + ", value = " + value); } map.remove("park"); //자릿수가 아니라 키값을 적어서 삭제해야 함 map.put("choi", "password"); System.out.println(map); } }
notion image
* put() 메소드를 사용하여 키-값 쌍을 추가 * get() 메소드를 사용하여 키에 해당하는 값을 출력 * keySet() 메소드를 사용하여 map의 모든 키(Key)를 가져온 후, for-each 루프를 사용하여 각 키에 대한 값을 출력 * remove() 메소드를 사용하여 "3"이라는 키에 해당하는 키-값 쌍을 제거
for-each 루프에서 map.keySet()을 사용하면 맵의 키(key)들을 가져올 수 있다. 그리고 map.get(key)를 사용하여 해당 키에 대응하는 값을 가져올 수 있다. (map.get(key)를 통해 값만 가져와서 출력) ex) map.put("kim", "1234");와 같이 put() 메소드를 사용하여 키 "kim"과 값 "1234"를 저장하면, map.get("kim")을 호출하면 값 "1234"를 반환 Map 인터페이스를 구현한 클래스들은 키와 값을 연결하여 저장하고, 키를 사용하여 값을 검색·호출하고, 키-값 쌍을 수정하거나 삭제하는 기능을 제공!
 

 
notion image
Map 인터페이스에는 keySet() 메소드가 있으며, 이 메소드는 Set<K>를 반환한다. 따라서, keySet() 메소드는 맵에 저장된 모든 키(Key)를 담고 있는 Set 객체를 반환 keySet()이 반환하는 객체가 중복되지 않는 고유한 키(key)들의 집합을 나타내기 위해 Set<T>를 반환한다. > keySet()은 요소를 출력하니까, 중복 요소를 제외하기 위해서 Set<T> 반환이 필요
notion image
 
[ Map에 저장된 데이터를 꺼내는 코드 ]
1. foreach 구문과 keySet()을 사용하는 방법
for (String key : map.keySet()) { System.out.println("key=" + key + ", value=" + map.get(key)); }
for (var key : map.keySet()) { System.out.println("key=" + key + ", value=" + map.get(key)); } * var 자료형도 사용 가능~!!
for (String key : map.keySet()) { String value = map.get(key); System.out.println("key : " + key + ", value = " + value); }
 
2. Stream 라이브러리를 사용하는 방법
map.forEach((key, value) -> { System.out.println("key=" + key + ", value=" + map.get(key)); });
 
3. 반복자를 사용하는 방법
Iterator<String> it = map.keySet().iterator(); //객체의 키(key)들을 모은 Set 컬렉션에서 Iterator를 생성 while (it.hasNext()) { String key = it.next(); System.out.println("key=" + key + ", value=" + map.get(key)); }
notion image
이터레이터 : 컬렉션의 요소에 접근하고, 요소를 추가하거나 삭제하는 등의 작업을 한다. hasNext() : 다음 요소가 존재하는지 여부를 반환 next() : 다음 요소를 반환하고, 커서를 다음 위치로 이동 (= 다음 요소를 가져온다) remove(): 현재 요소를 삭제
 
HashMap 중요! ★

3. Queue 인터페이스

큐는 FIFO(First-In, First-Out) 방식으로 동작하며, 데이터를 먼저 추가한 순서대로 처리
💡
ArrayDeque, LinkedList, PriorityQueue 클래스 LinkedList : 큐 기능을 활용하고자 한다면, LinkedList 클래스를 이용하여 Queue 인터페이스의 기능을 구현할 수 있다.
 
[ 큐의 메소드 ]
add(E element) - boolean : 큐에 지정된 요소를 추가한다. 새로운 원소의 추가가 큐의 용량을 넘어가면 예외를 throw 한다.
offer(E element) : 큐에 지정된 요소를 추가한다. 용량 초과 시 false를 반환 - boolean
remove(): 큐의 첫 번째 요소를 제거하고 반환. 큐가 비어있는 경우 예외를 throw 한다. poll(): 큐의 첫 번째 요소를 제거하고 반환. 큐가 비어있는 경우 null을 반환.
element(): 큐의 첫 번째 요소를 삭제하지 않고 반환. 큐가 비어있는 경우 예외를 throw 한다. peek(): 큐의 첫 번째 요소를 삭제하지 않고 반환. 큐가 비어있는 경우 null을 반환
 
[ 큐 인터페이스 예제 ]
package ex08; import java.util.LinkedList; import java.util.Queue; public class Test14 { public static void main(String[] args) { Queue<Integer> q = new LinkedList<>(); for (int i = 0; i < 5; i++) { q.add(i); } System.out.println("큐의 요소 : " + q); int e = q.remove(); System.out.println("삭제된 요소 : " + e); System.out.println(q); } }
notion image
 

5-1. 우선 순위 큐란?

요소들이 우선순위에 따라 처리되는 자료구조 일반적인 큐와는 달리, 요소들은 우선순위에 따라 정렬되어 저장되고 처리 기본적으로 작은 숫자가 높은 우선순위를 가지기에 작은 숫자가 우선적으로 처리되는 형태 ex) 작업 스케줄링에서 주로 사용
💡
PriorityQueue 클래스(Queue 인터페이스를 구현하고 있음)를 사용하여 우선순위 큐를 구현할 수 있다.
 
[ 우선 순위 큐 예시 ]
package ex08; import java.util.PriorityQueue; public class Test15 { public static void main(String[] args) { PriorityQueue<Integer> pq = new PriorityQueue<>(); pq.add(30); pq.add(80); pq.add(20); System.out.print(pq); System.out.println(); System.out.println("삭제된 원소 : " + pq.remove()); //작은 숫자가 우선순위가 높다 } }
notion image
 
[ 큰 값이 우선순위가 높은 형태로 처리 ]
PriorityQueue 생성자에 Comparator를 지정하여 순서를 반대로 설정할 수 있다. PriorityQueue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder()); 같이 생성하면 큰 값이 우선순위가 높은 형태로 처리된다. * Comparator : 자바에서 두 개의 객체를 비교하는 데 사용되는 인터페이스
notion image
 

Share article

codingb