Set

  • Java
  • 2021년 6월 1일

Set은 자바의 Collection중에 객체를 중복하지 않고 하나만 저장하는 자료구조로 List와 다르게 저장순서(index)를 따로 저장하지 않기 때문에 이를 통해 접근할 수 없다.

Set interface 제공 메서드

메서드리턴 값설명
add(E e)boolean객체 추가성공하면 true
addAll(Collection c)boolean컬렉션을 추가하면 데이터들을 Set에 맞게 저장
remove(Object o)boolean객체 삭제
contains(Object o)boolean객체가 포함되었는지 여부
eqauls(Object o)boolean같은지 비교
clear()voidSet 초기화
iterator()Iteratorset 요소 접근하기 위한 Iterator객체 반환



1. HashSet

Set을 구현한 구현체중에 하나로 Key를 Hash와 하여 저장하는 Set이다.

//선언
Set<Integer> set = new HashSet<>();
//add
System.out.println(set.add(1)); //true
System.out.println(set.add(1)); //false
System.out.println(set.add(0)); //true
System.out.println(set.add(-1)); //true
System.out.println(set.add(2)); //true

//iterator를 이용한 모든 key 접근
Iterator<Integer> it = set.iterator();
while(it.hasNext()){
    System.out.println(it.next()); //0, -1, 1, 2
}

//remove
System.out.println(set.remove(1));    //true

//contains
System.out.println(set.contains(1));  //false

it = set.iterator();
while(it.hasNext()){
    System.out.println(it.next()); //2
}

HashSet은 Set에서 제공하는 메서드외에는 별도로 제공하는 메서드가 없고 내부적으로는 HashMap으로 동작하기 때문에 접근속도가 빠르다. 그리고 별도의 index를 가지지 않기 때문에 모든 데이터를 순회하고 싶을때는 Iterator를 이용해서 접근하면 된다.


//HashSet 내부
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();

public HashSet() {
    map = new HashMap<>();
}

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

위는 HashSet의 내부를 일부 가져온 것인데 살펴보면 내부 필드로 HashMap을 가지고 있어 이를 이용(합성)해서 HashSet을 구현하고 있고 add와 같은 메서드도 Map의 put메서드를 이용해서 데이터를 추가하는 것을 볼 수 있다.

여기서 한가지 더 유심히 볼 부분이 PRESENT라는 객체인데 Set은 key만을 저장하는 자료구조이기 때문에 value는 어떤 객체가 오든 상관이없다. 그래서 HashSet을 만든 개발자는 어떤 HashSet에서든 모든 데이터는 value값을 동일한 value값을 가르키도록 static으로 PRESENT라는 객체를 생성해 메모리를 절약하는식으로 구성을 하였다.



2. LinkedHashSet

HashSet과 거의 동일하지만 데이터의 입력된 순서대로 저장을하고 관리를 한다. 기본적으로는 HashSet보다는 성능이 조금 뒤쳐지지만, HashMap의 capacity가 커지는 경우에는 HashSet보다는 성능이 좋다.

Set<Integer> set = new LinkedHashSet<>();
System.out.println(set.add(1)); //true
System.out.println(set.add(0)); //true
System.out.println(set.add(-1)); //true
System.out.println(set.add(2)); //true

Iterator<Integer> it = set.iterator();
while(it.hasNext()){
    System.out.println(it.next()); //1, 0, -1, 2
}

LinkedHashSet도 Set의 메서드만을 갖고 있고, HashSet과 비교해해보면 데이터를 추가한 순서대로 출력하는 것을 볼 수 있다.



3. TreeSet

TreeSet은 정렬된 상태의 Set을 사용하고 싶을때 사용할 수 있는 구현체로 SortedSet을 상속받은 NavigableSet을 구현한 구현체이다.

◾ 제공 메서드

메서드리턴 값설명
add(E e)boolean객체 추가성공하면 true
addAll(Collection c)boolean컬렉션을 추가하면 데이터들을 Set에 맞게 저장
remove(Object o)boolean객체 삭제
contains(Object o)boolean객체가 포함되었는지 여부
eqauls(Object o)boolean같은지 비교
clear()voidSet 초기화
iterator()Iteratorset 요소 접근하기 위한 Iterator객체 반환
descendingIterator()Iterator현재 정렬 순서의 반대로의 Iterator객체 반환
ceiling(E e)E객체보다 크거나 같은 객체중 가장 가까운 객체 return
higher(E e)E객체보다 큰 객체중 가장 가까운 객체 return
floor(E e)E객체보다 작거나 같은 객체중 가장 가까운 객체 return
lower(E e)E객체보다 작은 객체중 가장 가까운 객체 return
first()E첫번째 객체 return
last()E마지막 객체 return
headSet(E e)SortedSet객체보다 큰 객체들만 모아 Set return
headSet(E,boolean)NavigableSetheadSet()에서 객체E를 포함할지 결정해서 return
subSet(E from,E to)SorteSetfrom는 포함하고 to는 포함하지 않고 to까지의 객체 Set return
subSet(E from,boolean, E to,boolean)NavigableSetfrom부터 to 각각 같은거 포함할지 결정해서 Set return
tailSet(E e)SortedSet객체보다 작거나 같은 객체Set return
tailSet(E,boolean)NavigableSet객체를 포함할지 결정해 Set return
pollFirst()E첫번째 객체 삭제하면서 return
pollLast()E마지막 객체 삭제하면서 return

◾ 사용 예

TreeSet<Integer> set = new TreeSet<>(Arrays.asList(4,2,3,-1,0,8,4,2,7,10));
System.out.println(set);    //[-1, 0, 2, 3, 4, 7, 8, 10]

//근접한 요소 접근
System.out.println(set.ceiling(4));   //4
System.out.println(set.higher(4));    //7
System.out.println(set.floor(2));     //2
System.out.println(set.floor(2));     //0

System.out.println();

//부분집합 추출
System.out.println(set.headSet(3));             //[-1, 0, 2]
System.out.println(set.headSet(3,true));        //[-1, 0, 2, 3]
System.out.println(set.subSet(3,8));            //[3, 4, 7]
System.out.println(set.subSet(3,false,8,true)); //[4, 7, 8]
System.out.println(set.tailSet(4));             //[4, 7, 8, 10]
System.out.println(set.tailSet(4,false));       //[7, 8, 10]

System.out.println();

//Iterator
Iterator it = set.iterator();
while(it.hasNext()){
    System.out.print(it.next() + " ");    //-1 0 2 3 4 7 8 10
}
System.out.println();

//역순 Iterator
it = set.descendingIterator();          //10 8 7 4 3 2 0 -1
while(it.hasNext()){
    System.out.print(it.next() + " ");
}
System.out.println();


//내림차순 TreeSet 선언
TreeSet<Integer> descSet = new TreeSet<>(Collections.reverseOrder());
descSet.addAll(Arrays.asList(4,2,3,-1,0,8,4,2,7,10));
System.out.println(descSet);    //[10, 8, 7, 4, 3, 2, 0, -1]

TreeSet은 이름에서 유추할 수 있듯이 내부적으로 Tree로 구성되어있는 Set이다. 그렇기 때문에 특정 원소를 기준으로 다음,이전값이나 부분집합을 구할 수 있는 것인데 Tree는 데이터에따라 잘못하면 편향트리가 되어 접근하는데 O(N)이 소모되기 때문에 Balancing을 해주어야한다. 그래서 TreeSet도 내부를 봐보면 HashSet이 HashMap을 이용해서 합성한 것 처럼 TreeSet도 TreeMap을 이용해서 합성해 구현되어 있다.


//TreeSet 내부구조 중 일부

private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();

public TreeSet() {
  this(new TreeMap<>());
}

public boolean add(E e) {
  return m.put(e, PRESENT)==null;
}

TreeMap은 SortedMap의 구현체로 내부적으로 Red Black Tree로 구현되어 있어 balancing을 수행한다. 이런 이유로 HashSet은 O(1)만에 데이터를 접근할 수 있지만 TreeSet은 접근도 O(logN), 삽입/수정도 balancing수행으로 O(logN)만큼의 시간이 소요된다.

특정한 목적이 있지 않는이상 HashSet을 이용하는 것이 performance에 좋다.





Reference

https://docs.oracle.com/javase/8/docs/api/

Tags :

Related Posts

5분 와인

5분 와인

  • Books
  • 2021년 4월 21일

제목에서 그대로 보이듯이 와인에 대해 깊고 많은 역사를 알려주는 책이 아닌 집에서 보관방법, 와인 구매장소, 마트에서 좋은 와인 고르기, 선물용 와인 등 과 같이 가벼운 내용위주의 책들이라 간단하게 보기 좋고 책에서 언급하는 대로 아는 체,있어보이는 척 하기에 괜찮은 책이다. 샴페인을 한번 먹어본 이후로 화이트와인과 스파클링 와인에 빠져서 화이트와인을...

Read More
표준 입출력

표준 입출력

GoLang의 표준 입출력은 다른 언어와 같이 터미널이 기본이며, 파일등으로 수정이 가능하고 fmt패키지에서 제공을 한다. 입출력은 BitStream형태로 되어있다. 1. 표준 출력 1) 함수 함수 기능 Print() 입력값들을 출력 Println() 마지막에 개행문자를 포함한 입력값들을 출력 Printf() c의 printf와 같이 특정 포맷에 맞게 출력 2) 포맷 서식 포맷형태 설명 %d 정...

Read More
[SPSP] Dijkstra 알고리즘

[SPSP] Dijkstra 알고리즘

그래프 중에서 최단 경로를 찾는 알고리즘중에 하나로 하나의 정점에서 다른 모든 정점까지의 최단경로를 구하는 알고리즘 (single-source shortest path algorithmm)으로 우선순위 큐의 방법을 이용하는 알고리즘이다. 가장 최적의 vertex를 한개씩 선택하며 최단 경로를 찾는 방법으로 relax의 개념을 이용하며 relax는 현재 계산된 v노드까지의 거리보다...

Read More