1. CollectionFramework란?
자바에서 컬렉션 프레임워크(collection framework)란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미함. 즉, 데이터를 저장하는 자료 구조와 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현해 놓은 것. 자바의 인터페이스(interface)를 사용하여 구현됨.
2. Collection Framework 주요 인터페이스
- List 인터페이스
- Set 인터페이스
- Map 인터페이스
List와 Set 인터페이스는 모두 Collection 인터페이스를 상속받지만, Map 인터페이스는 구조상의 차이로 인해 별도로 정의됨. 즉, List 인터페이스와 Set 인터페이스의 공통된 부분을 Collection 인터페이스에서 정의됨. 참고: Java Documentation : The Collections Framework
인터페이스 | 설명 | 구현 클래스 |
---|---|---|
List<E> | 순서가 있는 데이터의 집합으로, 데이터의 중복을 허용함. | Vector, ArrayList, LinkedList, Stack, Queue |
Set<E> | 순서가 없는 데이터의 집합으로, 데이터의 중복을 허용하지 않음. | HashSet, TreeSet |
Map<K, V> |
키와 값의 한 쌍으로 이루어지는 데이터의 집합으로, 순서가 없음. 이때 키는 중복을 허용하지 않지만, 값은 중복될 수 있음. |
HashMap, TreeMap, Hashtable, Properties |
3. Set Collection Class
Set 인터페이스를 구현한 모든 Set 컬렉션 클래스는 다음과 같은 특징을 가짐.
- 요소의 저장 순서를 유지하지 않음
- 같은 요소의 중복 저장을 허용하지 않음
대표적인 Set 컬렉션 클래스에 속하는 클래스
- HashSet<E>
- TreeSet<E>
4. HashSet<E> 클래스
JDK 1.2부터 제공된 HashSet 클래스는 해시 알고리즘(hash algorithm)을 사용하여 검색 속도가 매우 빠르며, Set 컬렉션 클래스에서 가장 많이 사용되는 클래스 중 하나임. 내부적으로 HashMap 인스턴스를 이용하여 요소를 저장.
Set 인터페이스를 구현하므로, 요소를 순서에 상관없이 저장하고 중복된 값은 저장하지 않음.
cf) 만약 요소의 저장 순서를 유지해야 한다면 JDK 1.4부터 제공하는 LinkedHashSet 클래스 사용.
- HashSet에서 add() 메서드를 사용하여 해당 HashSet에 이미 존재하는 요소를 추가하려고 하면, 해당 요소를 저장하지 않고 false를 반환.
- 해당 HashSet에 이미 존재하는 요소인지를 파악하기 위해서는 내부적으로 다음과 같은 과정을 거침
- 해당 요소에서 hashCode() 메소드를 호출하여 반환된 해시값으로 검색할 범위를 결정
- 해당 범위 내의 요소들을 equals() 메서드로 비교
- 따라서 HashSet에서 add() 메서드를 사용하여 중복 없이 새로운 요소를 추가하기 위해서는 hashCode()와 equals() 메소드를 상황에 맞게 오버라이딩해야 함.
- 해당 HashSet에 이미 존재하는 요소인지를 파악하기 위해서는 내부적으로 다음과 같은 과정을 거침
- Set 인터페이스 주요 메서드
메소드 | 설명 |
---|---|
boolean add(E e) | 해당 집합(set)에 전달된 요소를 추가함. (선택적 기능) |
void clear() | 해당 집합의 모든 요소를 제거함. (선택적 기능) |
boolean contains(Object o) | 해당 집합이 전달된 객체를 포함하고 있는지를 확인함. |
boolean equals(Object o) | 해당 집합과 전달된 객체가 같은지를 확인함. |
boolean isEmpty() | 해당 집합이 비어있는지를 확인함. |
Iterator<E> iterator() | 해당 집합의 반복자(iterator)를 반환함. |
boolean remove(Object o) | 해당 집합에서 전달된 객체를 제거함. (선택적 기능) |
int size() | 해당 집합의 요소의 총 개수를 반환함. |
Object[] toArray() | 해당 집합의 모든 요소를 Object 타입의 배열로 반환함. |
5. 해시 코딩테스트 예시
1-1) 문제 보기
문제 설명
당신은 폰켓몬을 잡기 위한 오랜 여행 끝에, 홍 박사님의 연구실에 도착했습니다. 홍 박사님은 당신에게 자신의 연구실에 있는 총 N 마리의 폰켓몬 중에서 N/2마리를 가져가도 좋다고 했습니다.
홍 박사님 연구실의 폰켓몬은 종류에 따라 번호를 붙여 구분합니다. 따라서 같은 종류의 폰켓몬은 같은 번호를 가지고 있습니다. 예를 들어 연구실에 총 4마리의 폰켓몬이 있고, 각 폰켓몬의 종류 번호가 [3번, 1번, 2번, 3번]이라면 이는 3번 폰켓몬 두 마리, 1번 폰켓몬 한 마리, 2번 폰켓몬 한 마리가 있음을 나타냅니다. 이때, 4마리의 폰켓몬 중 2마리를 고르는 방법은 다음과 같이 6가지가 있습니다.
- 첫 번째(3번), 두 번째(1번) 폰켓몬을 선택
- 첫 번째(3번), 세 번째(2번) 폰켓몬을 선택
- 첫 번째(3번), 네 번째(3번) 폰켓몬을 선택
- 두 번째(1번), 세 번째(2번) 폰켓몬을 선택
- 두 번째(1번), 네 번째(3번) 폰켓몬을 선택
- 세 번째(2번), 네 번째(3번) 폰켓몬을 선택
이때, 첫 번째(3번) 폰켓몬과 네 번째(3번) 폰켓몬을 선택하는 방법은 한 종류(3번 폰켓몬 두 마리)의 폰켓몬만 가질 수 있지만, 다른 방법들은 모두 두 종류의 폰켓몬을 가질 수 있습니다. 따라서 위 예시에서 가질 수 있는 폰켓몬 종류 수의 최댓값은 2가 됩니다.
당신은 최대한 다양한 종류의 폰켓몬을 가지길 원하기 때문에, 최대한 많은 종류의 폰켓몬을 포함해서 N/2마리를 선택하려 합니다. N마리 폰켓몬의 종류 번호가 담긴 배열 nums가 매개변수로 주어질 때, N/2마리의 폰켓몬을 선택하는 방법 중, 가장 많은 종류의 폰켓몬을 선택하는 방법을 찾아, 그때의 폰켓몬 종류 번호의 개수를 return 하도록 solution 함수를 완성해주세요.
제한사항
- nums는 폰켓몬의 종류 번호가 담긴 1차원 배열입니다.
- nums의 길이(N)는 1 이상 10,000 이하의 자연수이며, 항상 짝수로 주어집니다.
- 폰켓몬의 종류 번호는 1 이상 200,000 이하의 자연수로 나타냅니다.
- 가장 많은 종류의 폰켓몬을 선택하는 방법이 여러 가지인 경우에도, 선택할 수 있는 폰켓몬 종류 개수의 최댓값 하나만 return 하면 됩니다.
입출력 예
nums | result |
---|---|
[3,1,2,3] | 2 |
[3,3,3,2,2,4] | 3 |
[3,3,3,2,2,2] | 2 |
입출력 예 설명
입출력 예 #1
문제의 예시와 같습니다.
입출력 예 #2
6마리의 폰켓몬이 있으므로, 3마리의 폰켓몬을 골라야 합니다.
가장 많은 종류의 폰켓몬을 고르기 위해서는 3번 폰켓몬 한 마리, 2번 폰켓몬 한 마리, 4번 폰켓몬 한 마리를 고르면 되며, 따라서 3을 return 합니다.
입출력 예 #3
6마리의 폰켓몬이 있으므로, 3마리의 폰켓몬을 골라야 합니다.
가장 많은 종류의 폰켓몬을 고르기 위해서는 3번 폰켓몬 한 마리와 2번 폰켓몬 두 마리를 고르거나, 혹은 3번 폰켓몬 두 마리와 2번 폰켓몬 한 마리를 고르면 됩니다. 따라서 최대 고를 수 있는 폰켓몬 종류의 수는 2입니다.
1-2) 문제 풀이
import java.util.*;
class Solution {
public int solution(int[] nums) {
int answer = nums.length/2;
HashSet<Integer> phonCatMonSet = new HashSet<>();
for(int num : nums){
phonCatMonSet.add(num);
}
if (phonCatMonSet.size() < answer) {
answer = phonCatMonSet.size();
}
return answer;
}
}
2-1) 문제 보기
문제 설명
전화번호부에 적힌 전화번호 중, 한 번호가 다른 번호의 접두어인 경우가 있는지 확인하려 합니다.
전화번호가 다음과 같을 경우, 구조대 전화번호는 영석이의 전화번호의 접두사입니다.
- 구조대 : 119
- 박준영 : 97 674 223
- 지영석 : 11 9552 4421
전화번호부에 적힌 전화번호를 담은 배열 phone_book 이 solution 함수의 매개변수로 주어질 때, 어떤 번호가 다른 번호의 접두어인 경우가 있으면 false를 그렇지 않으면 true를 return 하도록 solution 함수를 작성해주세요.
제한 사항
- phone_book의 길이는 1 이상 1,000,000 이하입니다.
- 각 전화번호의 길이는 1 이상 20 이하입니다.
- 같은 전화번호가 중복해서 들어있지 않습니다.
입출력 예제
phone_book | return |
---|---|
["119", "97674223", "1195524421"] | false |
["123","456","789"] | true |
["12","123","1235","567","88"] | false |
입출력 예 설명
입출력 예 #1
앞에서 설명한 예와 같습니다.
입출력 예 #2
한 번호가 다른 번호의 접두사인 경우가 없으므로, 답은 true입니다.
입출력 예 #3
첫 번째 전화번호, “12”가 두 번째 전화번호 “123”의 접두사입니다. 따라서 답은 false입니다.
알림
2021년 3월 4일, 테스트 케이스가 변경되었습니다. 이로 인해 이전에 통과하던 코드가 더 이상 통과하지 않을 수 있습니다.
2-2) 문제 풀이
import java.util.HashSet;
class Solution {
public boolean solution(String[] phone_book) {
HashSet<String> phoneSet = new HashSet<>();
for(String phone : phone_book){
phoneSet.add(phone);
}
//모든 전화번호들에 대해, 해당 번호의 앞 1글자부터 시작해서 끝까지 loop를 돌며 해당하는 부분과 동일한 문자열이 HashSet내부에 있는지 확인
for(String phone: phone_book){
for(int i = 1; i <phone.length(); i++){
if(phoneSet.contains(phone.substring(0,i))) {
return false;
}
}
}
return true;
}
}
3-1) 문제 보기
문제 설명
수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.
제한사항
- 마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
- completion의 길이는 participant의 길이보다 1 작습니다.
- 참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
- 참가자 중에는 동명이인이 있을 수 있습니다.
입출력 예
participant | completion | return |
---|---|---|
["leo", "kiki", "eden"] | ["eden", "kiki"] | "leo" |
["marina", "josipa", "nikola", "vinko", "filipa"] | ["josipa", "filipa", "marina", "nikola"] | "vinko" |
["mislav", "stanko", "mislav", "ana"] | ["stanko", "ana", "mislav"] | "mislav" |
입출력 예 설명
예제 #1
"leo"는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.
예제 #2
"vinko"는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.
예제 #3
"mislav"는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.
※ 공지 - 2023년 01월 25일 테스트케이스가 추가되었습니다.
3-2) 문제 풀이
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
class Solution {
public String solution(String[] participant, String[] completion) {
String answer = "";
HashMap<String, Integer> nameMap = new HashMap<>();
for(String name : participant){
nameMap.put(name, nameMap.getOrDefault(name,0)+1);
}
for(String name : completion){
nameMap.put(name, nameMap.get(name)-1);
}
Iterator<Map.Entry<String, Integer>> iter = nameMap.entrySet().iterator();
while(iter.hasNext()){
Map.Entry<String, Integer> entry = iter.next();
if (entry.getValue() != 0){
answer = entry.getKey();
break;
}
}
return answer;
}
}
'Web_Backend > Java' 카테고리의 다른 글
[JAVA] 자바에서 객체지향형 프로그래밍(OOP)과 함수형 프로그래밍(FP), java 코드로 보는 SOLID(단일 책임 원칙, 개방/폐쇄 원칙, 리스코프 치환 원칙, 인터페이스 분리 원칙, 의존 역전 원칙) (0) | 2024.01.12 |
---|---|
[JAVA] System.out.print~의 사용을 지양해야만 하는 이유를 알아보자(feat,, IO, 휘발성, 출력레벨, 성능저하..) (2) | 2024.01.08 |
[JAVA] 재귀함수 vs 반복문 (0) | 2023.06.20 |
[JAVA] GC(Garbage collector, Garbage collection) (0) | 2023.06.15 |
[JAVA] Stream API (0) | 2023.05.26 |
야나의 코딩 일기장 :) #코딩블로그 #기술블로그 #코딩 #조금씩,꾸준히
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!