도서/자바의 신

[도서/자바의 신] #23 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Part 2(Set과 Queue)

yulee_to 2022. 12. 30. 01:22

자바의 신

✔️이 글은 [자바의 신 - 이상민 지음] 도서를 바탕으로 정리한 글입니다. 


Set이 왜 필요하지?

Set은 순서에 상관없이, 어떤 데이터가 중복없이 존재하는지를 확인하기 위한 용도로 사용된다.

 

Set 인터페이스를 구현한 주요 클래스

  • HashSet : 순서가 필요 없는 데이터를 해시 테이블에 저장. Set 중 성능이 가장 좋음
  • TreeSet : 저장된 데이터의 값에 따라 정렬됨. red-black이라는 트리 타입으로 값이 저장되며, HashSet보다 조금 느림
  • LinkedHashSet : 연결된 목록 타입으로 구현된 해시 테이블에 데이터를 저장. 저장된 순서에 따라 값이 정렬되지만 성능이 가장 안좋음

데이터 정렬 작업을 거치는 클래스가 성능이 안좋다. 

HashSet에 대해서 파헤쳐 보자

HashSet 클래스의 상속 관계

java.lang.Object
    ㄴjava.util.AbtractCollection<E>
        ㄴ java.util.AbstractSet<E>
            ㄴ java.util.HashSet<E>

AbstractSet에는 equals(), hashCode() 메소드와 추가한 메소드 removeAll()만 구현되어 있다.

Set은 중복을 허용하지 않으므로 데이터가 같은지 확인하는 것이 주요 작업이라 equals()와 hashCode() 구현부분이 중요하다.

removeAll()은 컬렉션을 매개변수로 받아 그 컬렉션에 포함된 모든 데이터를 지울 때 사용한다. 

 

HashSet이 구현한 인터페이스에는 Serializable, Cloneable, Collection<E>, Iterable<E>, Set<E>가 있다. 

 

List에는 구현되어 있지만 Set에는 없는 메소드

  • 순서가 매개변수로 넘어가는 메소드
  • 수행 결과가 데이터 위치와 관련된 메소드

HashSet의 생성자들도 여러 종류가 있다

로드 팩터(load factor)는 (데이터 개수) / (저장공간)을 의미한다. 데이터의 개수가 증가해 로드 팩터보다 커지면, 저장 공간의 크기는 증가되고 해시 재정리 작업을 해야 한다. 해시 재정리 작업은 내부에 갖고 있던 자료구조를 다시 생성해야해서 성능에 악영향을 끼친다.

로드 팩터 값이 클수록 공간은 넉넉해지지만, 탐색 시간은 증가한다. 

 

HashSet의 생성자

  • HashSet() : 데이터를 저장할 수 있는 16개의 공간과 0.75의 로드팩터를 갖는 객체 생성
  • HashSet(Collection<? extends E> c) : c객체의 데이터를 담는 객체 생성
  • HashSet(int initialCapacity) : 매개변수 값 만큼의 데이터 저장 공간과 0.75 로드팩터를 갖는 객체 생성
  • HashSet(int initialCapacity, float loadFactor) : 첫번째 매개변수 값 만큼의 데이터 저장 공간과 두번째 매개변수로 받은 만큼의 로드팩터를 갖는 객체 생성

HashSet의 주요 메소드를 살펴보자

HashSet의 주요 메소드

  • add(E e) : 데이터 추가
  • clear() : 데이터 모두 삭제
  • clone() : HashSet 객체 복제하지만 담겨있는 데이터는 복제하지 않음
  • contains(Object o) : 지정한 객체 존재하는지 확인
  • isEmpty() : 비어있는지 확인
  • iterator() : 데이터 꺼내기 위한 Iterator 객체 리턴
  • remove(Object o) : o객체 삭제
  • size() : 저장된 데이터 개수 리턴

Queue는 왜 필요할까?

Queue는 FIFO(First In First Out)의 용도로 사용되는 인터페이스로 먼저 들어온 것을 먼저 내보내준다.

Deque는 Java 6에서 추가된 인터페이스로 Double Ended Queue의 약자이며, Queue의 기능을 모두 포함하지만 Queue와는 달리 맨 앞과 맨 뒤 둘다에서 값을 넣거나 빼는 작업을 수행하는 데 용이하게 되어 있다. 

LinkedList를 파헤쳐보자

LinkedList의 상속관계

java.lang.Object
    ㄴ java.util.AbstractCollection<E>
        ㄴ java.util.AbstractList<E>
            ㄴ java.util.AbstractSequentialList<E>
                ㄴ java.util.LinkedList<E> 

AbstractSequentialList는 AbstractList와 add(), set(), remove() 등의 메소드에 대한 구현 내용이 다르다. 

 

LinkedList 클래스가 구현한 인터페이스에는 Serializable, Cloneable, Iterable<E>, Collection<E>, Deque<E>, List<E>, Queue<E>가 있다.

List와 Queue의 기능을 모두 구현하고 Deque 인터페이스도 구현해 맨 앞과 맨 뒤의 데이터를 쉽게 처리할 수 있다. 

LinkedList의 생성자와 주요 메소드를 살펴보자

LinkedList는 List, Deque, Queue 인터페이스를 모두 구현한 클래스이다.

 

LinkedList의 생성자

  • LinkedList() : 비어있는 LinkedList 객체 생성
  • LinkedList(Collection<? extends E> c) : c 객체의 데이터를 LinkedList에 담아서 객체 생성

일반적인 배열 타입 클래스와는 달리 앞 뒤로 데이터가 연결되는 구조라 미리 공간을 만들어 둘 필요가 없어 크기를 지정하는 생성자가 없다.

 

LinkedList의 데이터 추가 메소드

  • addFirst(Object), offerFirst(Object), push(Object) : 가장 앞에 데이터 추가
  • add(Object), addLast(Object), offer(Object), offerLast(Object) : 가장 뒤에 데이터 추가
  • add(int, Object) : 특정 위치에 데이터 추가
  • set(int, Object) : 특정 위치의 데이터 변경, 기존에 있던 데이터 리턴
  • addAll(Collection) : 컬렉션의 데이터 추가
  • addAll(int, Collection) : 지정된 위치에 컬렉션의 데이터 추가

여러 인터페이스를 구현해 중복된 기능을 하는 메소드가 많다.

같은 기능을 하는 메소드를 호출해도 결국 굵은 글씨로 표시한 메소드가 수행된다. 

 

LinkedList에서 특정 위치의 데이터를 꺼내는 메소드

  • getFirst(), peekFirst(), peek(), element() : 맨 앞 데이터 리턴
  • getLast(), peekLast() : 맨 뒤 데이터 리턴
  • get(int) : 지정한 위치에 있는 데이터 리턴

맨 앞 데이터 리턴하는 메소드도 결국 내부적으로는 getFirst()를 호출하므로 이 메소드 사용을 권장한다.

 

LinkedList에 어떤 객체가 포함되었는지 확인하는 메소드

  • contains(Object) : 매개변수로 넘긴 데이터 존재하면 true
  • indexOf(Object) : 매개변수로 넘긴 데이터 위치를 앞에서부터 검색해 리턴, 없을 경우 -1
  • lastIndexOf(Object) : 매개변수로 넘긴 데이터의 위치를 끝에서부터 검색해 리턴, 없을 경우 -1

LinkedList에서 데이터 삭제해주는 메소드

  • remove(), removeFirst(), poll(), pollFirst(), pop() : 가장 앞에 있는 데이터 삭제하고 삭제한 데이터 리턴
  • pollLast(), removeLast() : 가장 끝 데이터 삭제하고 삭제한 데이터 리턴
  • remove(int) : 지정된 위치에 있는 데이터 삭제하고 삭제한 데이터 리턴
  • remove(Object) , removeFirstOccurrence(Object) : 매개변수 객체와 동일한 데이터 중 앞에서부터 가장 처음에 발견한 데이터 삭제
  • removeLastOccurrence(Object) : 매개변수 객체와 동일한 데이터 중 끝에서부터 가장 처음에 발견한 데이터 삭제

가장 앞과 가장 끝 데이터를 삭제하는 각 메소드들은 내부적으로 removeFirst()와 removeLast()를 호출한다. 

 

그 외 메소드

  • size() : 저장된 데이터 개수 리턴
  • clear() : 모든 데이터 삭제
  • clone() : 모든 데이터 복제
  • toArray() : 데이터들을 배열로 만듦

Iterator 객체는 LinkedList 객체를 하나씩 검색하기 위해 사용된다.

ListIterator는 Iterator가 다음 데이터만을 검색할 수 있다는 단점을 보완하여 이전 데이터도 검색할 수 있는 이터레이터이다. 이전 데이터는 previous()를 사용하면 된다. 

  • listIterator(int) : 지정된 위치부터 데이터 검색하기 위한 ListIterator 객체를 리턴
  • descendingIterator() : LinkedList의 데이터를 끝에서부터 검색하기 위한 Iterator 객체 리턴

정리해 봅시다

1. 순서와 상관 없는 여러 데이터를 하나의 객체에 저장할 때 사용하는 Collection의 하위 인터페이스는 무엇인가요?

Set

2. HashSet 클래스는 생성자를 통하여 저장 가능한 데이터의 초기 크기를 지정할 수 있나요?

YES

3. HashSet 클래스의 객체에 데이터를 추가하는 메소드는?

add()

4. HashSet 클래스의 객체에 어떤 데이터가 존재하는지 확인하는 메소드는?

contains()

5. HashSet 클래스의 객체에 어떤 데이터를 삭제하는 메소드는?

remove()

6. Queue는 FIFO를 처리하기 위한 클래스의 인터페이스입니다. FIFO는 무슨 단어의 약자인가요?

First In First Out의 약어로 선입선출을 의미한다. 

7. Deque는 무슨 단어의 약어이며, 용도는 무엇인가요?

Double Ended Queue의 약어이며, 맨 앞과 맨 뒤 양쪽에서 데이터를 추가, 삭제하기 위한 용도로 사용된다.

8. LinkedList 클래스의 특징은?

LinkedList는 List, Deque, Queue 인터페이스를 모두 구현한 클래스이다. 

728x90