일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- 아싸의 생일
- 플러터
- AWS
- 개강해짐
- 얘들아 잘 지내니
- 뽀모도로 타이머
- 모바일 청첩장
- 수제비2022 정리
- 자가격리
- 생일축하해 나 자신
- 2022 정보처리기사
- 스프링 MVC
- 다음에 또 만나자
- 정보처리기사2022
- pem키 분실
- CRUDS
- 정보처리기사 2022
- 레이튼 교수와 이상한 마을
- 확진
- FLUTTER
- 재택치료
- 다들 안잊어서
- N-Queen
- 교수님 과제 이제 그만..
- 교육봉사
- 지독한 컨셉충
- 대외활동
- 수제비 2022
- 대학생
- 다행이야...ㅎ
- Today
- Total
Rei’s Tech diary
Set - LinkedHashSet 이론과 구현 본문
📌 LinkedHashSet<E>
- Set<E> 인터페이스를 구현한 클래스이자, HashSet<E>의 자식 클래스
- 수집한 원소를 집합 형태로 관리하여 저장공간(capacity)을 동적관리
- 중복 불가
- 입력 순서와 출력 순서는 동일하다. (HashSet과 차이점)
📌 Set<E> 주요 메서드
구분 | 리턴타입 | 메서드 이름 | 기능 |
데이터 추가 |
boolean | add(E element) | 매개변수로 입력된 원소를 리스트에 추가 |
boolean | addAll(Collection <? Extends E> c | 매개변수로 입력된 컬렉션 전체를 추가 | |
데이터 삭제 |
boolean | remove<Object o) | 원소 중 매개변수입력과 동일한 객체 삭제 |
void | clear() | 전체 원소 삭제 | |
데이터 정보추출 |
boolean | isEmpty() | Set 객체가 비워져있는지 여부 리턴 |
boolean | contains(Object o) | 매개변수로 입력된 원소가 있는지 여부를 리턴 | |
int | size() | 리스트 객체 내에 포함된 원소의 개수 | |
iterator<E> | iterator() | Set 객체내의 데이터를 연속하려 꺼내는 Iterator 객체 리턴 | |
Set 객체 배열 반환 |
Object[] | toArray() | 리스트를 Object 배열로 변환 |
T[] | toArray(T[] t) | 입력매개변수로 전달한 타입의 배열로 변환 |
📌 LinkedHashSet<E>
- 입력과 출력의 순서가 동일
예시코드)
public class LinkedHashSetMethod {
public static void main(String[] args) {
Set<String> linkedSet1 = new LinkedHashSet<>();
//#1. add(E element)
linkedSet1.add("가");
linkedSet1.add("나");
linkedSet1.add("가");
System.out.println(linkedSet1.toString()); //[가, 나]
//#2. addAll(다른 set 객체)
Set<String> linkedSet2 = new LinkedHashSet<>();
linkedSet2.add("나");
linkedSet2.add("다");
linkedSet2.addAll(linkedSet1);
System.out.println(linkedSet2.toString()); //[나, 다, 가] 입력 순서대로 출력
//#3. remove(Object o)
linkedSet2.remove("나");
System.out.println(linkedSet2.toString()); //[다, 가]
//#4. clear
linkedSet2.clear();
System.out.println(linkedSet2.toString());
//#5. isEmpty
System.out.println(linkedSet2.isEmpty()); //true
//#6. contains (Object o)
Set<String> linkedSet3 = new LinkedHashSet<>();
linkedSet3.add("가");
linkedSet3.add("나");
linkedSet3.add("다");
System.out.println(linkedSet3.contains("나")); //true
System.out.println(linkedSet3.contains("라")); //false
//#7. size
System.out.println(linkedSet3.size()); //3
//#8. iterator
Iterator<String> iterator = linkedSet3.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//#9 toArray : 배열로 바꾸기
Object[] objArray = linkedSet3.toArray();
System.out.println(Arrays.toString(objArray));
//#10. toArray(T[] t)
String[] strArray1 = linkedSet3.toArray(new String[0]); //컴파일러가 알아서 저장공간을 늘려준다.
System.out.println(Arrays.toString(strArray1)); //[가, 나, 다]
String[] strArray2 = linkedSet3.toArray(new String[5]);
System.out.println(Arrays.toString(strArray2)); //[가, 나, 다, null, null]
}
}
💡 LinkedHashSet 구현
- LinkedHashSet을 다음과 같이 구현해보았다.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
public class MyLinkedHashSet<E> {
private final Map<E, Node<E>> map;
private Node<E> head;
private Node<E> tail;
private static class Node<E> {
E value;
Node<E> next;
Node<E> prev;
Node(E value){
this.value = value;
}
}
public MyLinkedHashSet(){
map = new HashMap<>();
head = null;
tail = null;
}
public boolean add(E element){
if(map.containsKey(element)){
return false;
}
Node<E> newNode = new Node<>(element);
map.put(element, newNode);
if(tail == null){
head = newNode;
tail = newNode;
}else{
tail.next = newNode;
newNode.prev = tail;
tail = newNode;
}
return true;
}
public boolean remove(E element){
Node<E> node = map.remove(element);
if(node == null){
return false; //element not found
}
if(node.prev != null){
node.prev.next = node.next;
}else{
head = node.next;
}
if(node.next != null){
node.next.prev = node.prev;
}else{
tail = node.prev;
}
return true;
}
public boolean contains(E element){
return map.containsKey(element);
}
public int size(){
return map.size();
}
//입출력 순서 동일
public void printElements(){
Node<E> current = head;
while(current != null){
System.out.println(current.value);
current = current.next;
}
System.out.println();
}
public void clear(){
map.clear();
head = null;
tail = null;
}
public Iterator<E> iterator(){
return new CustomIterator();
}
private class CustomIterator implements Iterator<E> {
private Node<E> currentNode = head;
@Override
public boolean hasNext() {
return currentNode != null;
}
@Override
public E next() {
if(!hasNext()){
throw new NoSuchElementException();
}
E value = currentNode.value;
currentNode = currentNode.next;
return value;
}
}
}
필드 및 생성자
- Map을 사용하려 값과 해당 노드를 앞뒤 노드와 연결한다.
- head : 첫번째 노드를 가리키는 포인터
- tail : 마지막 노드를 가리키는 포인터
- Node<E> 클래스 : 노드의 값, 앞 뒤 정보를 저장
- value : 노드가 저장하는 값
- next : 다음 노드를 가리키는 포인터
- prev : 이전 노드를 가리키는 포인터
private final Map<E, Node<E>> map;
private Node<E> head;
private Node<E> tail;
private static class Node<E> {
E value;
Node<E> next;
Node<E> prev;
Node(E value){
this.value = value;
}
}
public MyLinkedHashSet(){
map = new HashMap<>();
head = null;
tail = null;
}
요소 추가 (add)
- 이미 집합에 존재하는 값이 있다면 추가하지 않는다.
- 새로운 노드를 생성하여, map에 value와 해당 노드를 추가한다.
public boolean add(E element){
if(map.containsKey(element)){
return false;
}
Node<E> newNode = new Node<>(element);
map.put(element, newNode);
if(tail == null){// 첫 번째 요소일 경우
head = newNode; // head와 tail 모두 새 노드를 가리킴
tail = newNode;
}else{// 그 외의 경우
tail.next = newNode;// tail의 next를 새 노드로 설정
newNode.prev = tail;// 새 노드의 prev를 현재 tail로 설정
tail = newNode;// tail을 새 노드로 갱신
}
return true;
}
요소 삭제 (remove)
- Map에서 value에 해당하는 노드를 제서한다.
- 값이 존재하지 않으면 false 반환
public boolean remove(E element){
Node<E> node = map.remove(element);
if(node == null){
return false; //element not found
}
if(node.prev != null){ // node가 첫 번째 노드가 아니라면
node.prev.next = node.next; // 이전 노드가 다음 노드를 가리키도록 수정
}else{ // 첫 번째 노드를 제거할 경우
head = node.next; // head를 node의 다음 노드로 갱신
}
if(node.next != null){ // node가 마지막 노드가 아니라면
node.next.prev = node.prev; // 다음 노드가 이전 노드를 가리키도록 수정
}else{ // 마지막 노드를 제거할 경우
tail = node.prev; // tail을 node의 이전 노드로 갱신
}
return true;
}
값이 존재하는지 확인 (contains)
- Map에 해당 값이 존재하는지 확인한다.
public boolean contains(E element){
return map.containsKey(element);
}
집합 크기 반환 (size)
- Map의 size를 반환한다.
public int size(){
return map.size();
}
집합의 요소를 삽입 순서대로 출력
- LinkedHashSet의 특징인 집합의 요소를 삽입 순서대로 출력하기 위해서, head부터 포인터를 옮겨가며 출력한다.
public void printElements(){
Node<E> current = head;
while(current != null){
System.out.println(current.value);
current = current.next;
}
System.out.println();
}
모든 요소 제거 clear()
- Map의 모든 요소를 제거한다.
- 이때 head와 tail도 null로 초기화한다.
public void clear(){
map.clear();
head = null;
tail = null;
}
Iterator 구현
- Iterator 인터페이스를 구현하여 Iterator를 구현한다.
public Iterator<E> iterator(){
return new CustomIterator(); // Iterator를 반환
}
private class CustomIterator implements Iterator<E> {
private Node<E> currentNode = head; // 현재 노드를 head로 초기화
@Override
public boolean hasNext() {
return currentNode != null; // currentNode가 null이 아니면 더 많은 요소가 있음
}
@Override
public E next() {
if(!hasNext()){
throw new NoSuchElementException(); // 더 이상 요소가 없다면 예외발생
}
E value = currentNode.value; // 현재 노드의 값을 반환
currentNode = currentNode.next; // 다음 노드로 이동
return value;
}
}
'프로그래밍 > Data Structure' 카테고리의 다른 글
Map - HashMap 이론과 구현 (2) | 2024.12.30 |
---|---|
Set - TreeSet 이론과 구현 (0) | 2024.12.28 |
Set - HashSet 이론과 구현 (3) | 2024.12.27 |
List - LinkedList 이론과 구현 (2) | 2024.12.24 |
List - Vector 이론과 구현 (2) | 2024.12.24 |