1. Set 인터페이스
- 저장 순서가 유지되지 않습니다.
- 중복 저장 불가능합니다.
- 수학의 집합과 같은 개념입니다.(순서가 상관없고 중복이 허용되지 않음)
- 구현 클래스로는 HashSet, LinkedHashSet, TreeSet이 있습니다.
2. Set 인터페이스 주요 기능
[객체 추가]
boolean add(E e) → 특정 객체 추가
[객체 검색]
boolean contains(Object o) → 특정 객체가 존재하는지 여부
boolean isEmpty() → 비어있는지 확인
int size() → 저장된 객체 수 리턴
Iterator<E> iterator() → 반복자 리턴(루핑 시 활용)
[객체 삭제]
void clear() → 저장된 모든 객체 삭제
boolean remove(Object o) → 특정 객체 삭제
3. Iterator 인터페이스 주요 기능
boolean hasNext() → 가지고 올 객체가 있으면 true 없으면 false 리턴
E next() → 컬렉션에서 객체를 하나 가지고 옵니다.
void remove() → Set 컬렉션에서 객체를 제거합니다. (Iterator 메소드이지만 해당 컬렉션에서 객체를 제거합니다.)
[루핑(looping) 예시]
// String 타입을 갖는 HashSet 컬렉션 생성
Set<String> hashSet = new HashSet<>();
hashSet.add("a");
hashSet.add("b");
hashSet.add("c");
hashSet.add("d");
hashSet.add("e");
// Iterator 이용한 루핑
Iterator<String> iterator = hashSet.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
// foreach 이용한 루핑
for(String alpha : hashSet) {
System.out.println(alpha);
}
// 람다, 메소드 레퍼런스 이용한 루핑
hashSet.forEach(System.out::println);
4. HashSet
객체를 순서 없이 저장하고 동일한 객체는 중복 저장하지 않습니다.
[중복 저장하지 않는 방법]
1. 객체를 저장 전 hashCode() 메소드를 이용해 객체의 해시 코드를 얻은 후 이미 저장되어있는 객체들의 해시 코드와 비교합니다.
2. 동일한 해시 코드가 있다면 equals() 메소드로 두 객체를 비교해 동일한 객체일 경우 중복 저장을 하지 않습니다.
예시 1 (String 객체를 타입으로 갖는 HashSet)
String 클래스에는 이미 특정 메소드가 구현되어 있습니다. 따라서 중복 저장되지 않습니다.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
public int hashCode() {
...
}
public boolean equals(Object anObject) {
...
}
}
// String 타입을 갖는 HashSet 컬렉션 생성
Set<String> hashSet = new HashSet<>();
hashSet.add("a");
hashSet.add("a");
hashSet.add("b");
hashSet.add("b");
hashSet.add("c");
// 람다, 메소드 레퍼런스 이용한 루핑
hashSet.forEach(System.out::println); // result a b c
예시 2 (사용자 정의 객체를 타입으로 갖는 HashSet)
사용자 정의 클래스에 특정 메소드를 오버 라이딩하여 구현해줘야 합니다.
import java.util.HashSet;
import java.util.Set;
public class SetTest {
public static void main(String[] args) {
Set<Person> hashSet = new HashSet<>();
hashSet.add(new Person("kim", "010-1234-1234"));
hashSet.add(new Person("lee", "010-1234-1234"));
hashSet.add(new Person("jang", "010-1111-1111"));
hashSet.add(new Person("shin", "010-1111-1111"));
hashSet.add(new Person("suk", "010-1234-1231"));
// 람다, 메소드 레퍼런스 이용한 루핑
hashSet.forEach(h -> {
System.out.println(h.getName() + " : " + h.getPhone());
});
/**
* result
* jang : 010-1111-1111
* kim : 010-1234-1234
* suk : 010-1234-1231
*/
}
// 핸드폰 번호가 같으면 같은 객체로 판단
static class Person {
private String name;
private String phone;
public Person(String name, String phone) {
this.name = name;
this.phone = phone;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
@Override
public boolean equals(Object o) {
if(o instanceof Person) {
Person person = (Person) o;
return person.getPhone().equals(getPhone());
} else {
return false;
}
}
@Override
public int hashCode() {
return getPhone().hashCode();
}
}
}
5. LinkedHashSet
LinkedHashSet도 마찬가지로 중복된 데이터를 저장할 수 없다. 그러나 입력된 순서대로 데이터를 관리한다.
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("a");
linkedHashSet.add("a");
linkedHashSet.add("b");
linkedHashSet.add("b");
linkedHashSet.add("c");
linkedHashSet.add("c");
linkedHashSet.add("d");
linkedHashSet.add("d");
linkedHashSet.add("e");
linkedHashSet.add("e");
linkedHashSet.forEach(System.out::println);
/**
* result
* a
* b
* c
* d
* e
*/
6. TreeSet
- 검색 기능이 강화된 컬렉션 프레임워크 구현체
- TreeSet에는 객체가 저장되는 동시에 자동으로 오름차순으로 정렬됩니다.
- TreeSet은 정렬을 위해 Comparable을 구현한 객체를 요구합니다.
- Integer, Double, String은 모두 Comparable를 구현하고 있으며 사용자 정의 클래스를 사용하려면 Comparable을 구현해야 합니다.
- 이진트리 구조를 가지고 있습니다.
[이진트리]
- 여러 개의 노드가 트리 형태로 연결된 구조
- 루트 노드에서부터 시작해 각 노드 최대 2개의 노드를 연결할 수 있는 구조
- 위아래로 연결된 두 노드 = 부모-자식 관계 (부모 노드, 자식 노드)
- 하나의 부모 노드는 최대 두 개의 자식 노드와 연결될 수 있습니다.
- 부모 노드의 값 보다 작은 노드는 왼쪽
- 부모 노드의 값 보다 큰 노드는 오른쪽
- 왼쪽 마지막 노드가 제일 작은 값
- 오른쪽 마지막 노드가 가장 큰 값
- 오름차순(왼쪽 마지막 노드에서부터 오른쪽 마지막 노드까지 [왼쪽]→[부모]→[오른쪽] 순으로 읽음)
- 내림차순(오른쪽 마지막 노드에서부터 왼쪽 마지막 노드까지 [오른쪽]→[부모]→[왼쪽] 순으로 읽음)
- 그룹핑에 매우 유리합니다.
[예시]
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(4);
treeSet.add(2);
treeSet.add(9);
treeSet.add(7);
treeSet.add(1);
TreeSet 기능
TreeSet 같은 경우엔 Set 인터페이스 타입에 대입해서 사용해도 되지만 TreeSet 타입으로 대입해야 TreeSet이 가지고 있는 검색 관련 기능들을 사용할 수 있습니다.
Set<Integer> treeSet1 = new TreeSet<>();
TreeSet<Integer> treeSet2 = new TreeSet<>();
[검색 관련 기능]
E first() → 제일 작은 객체를 반환
E last() → 제일 큰 객체를 반환
E lower(E e) → 주어진 객체보다 작은 데이터 중 최댓값 반환(주어진 객체보다 바로 아래 객체를 반환)
E higher(E e) → 주어진 객체보다 큰 데이터 중 최솟값 반환(주어진 객체보다 바로 위 객체를 반환)
E floor(E e) → 주어진 객체와 같은 값이 있으면 반환, 없다면 주어진 객체보다 작은 데이터 중 최댓값 반환
E ceiling(E e) → 주어진 객체와 같은 값이 있으면 반환, 없다면 주어진 객체보다 큰 데이터 중 최소값 반환
E pollFirst() → 제일 작은 객체를 반환하고 컬렉션에서 제거
E pollLast() → 제일 큰 객체를 반환하고 컬렉션에서 제거
※ 문자열(String) 같은 경우엔 문자의 유니코드 값으로 비교합니다.
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(4);
treeSet.add(2);
treeSet.add(9);
treeSet.add(7);
treeSet.add(1);
System.out.println(treeSet.first()); // 1
System.out.println(treeSet.last()); // 9
System.out.println(treeSet.lower(4)); // 2
System.out.println(treeSet.higher(4)); // 7
System.out.println(treeSet.floor(3)); // 2
System.out.println(treeSet.ceiling(3)); // 4
System.out.println(treeSet.pollFirst()); // 1
System.out.println(treeSet.pollLast()); // 9
[정렬 관련 기능]
Iterator<E> descendingIterator() → 내림차순으로 정렬된 Iterator를 리턴
NavigableSet<E> descendingSet() → 내림차순으로 정렬된 NavigableSet를 리턴
※ 오름차순으로 정렬하고 싶으면 descendingSet() 메소드를 두 번 호출합니다.
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(4);
treeSet.add(2);
treeSet.add(9);
treeSet.add(7);
treeSet.add(1);
// 내림차순
NavigableSet<Integer> navigableSetDesc = treeSet.descendingSet();
navigableSetDesc.forEach(System.out::println); // 9 7 4 2 1
System.out.println("---------------------------------------------");
// 오름차순
NavigableSet<Integer> navigableSetAsc = navigableSetDesc.descendingSet();
navigableSetAsc.forEach(System.out::println); // 1 2 4 7 9
System.out.println("---------------------------------------------");
// 다른 방법 //
// 기본값은 오름차순
treeSet.forEach(System.out::println);
// treeSet.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
System.out.println("-------------------------------------");
// Stream API sorted를 활용하여 내림차순 정렬
treeSet.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
[범위 검색 관련 기능]
NavigableSet<E> headSet(E toElement, boolean inclusive)
→ 주어진 객체보다 작은 객체들을 NavigableSet으로 반환, 주어진 객체 포함 여부는 두 번째 매개 값으로 설정
NavigableSet<E> tailSet(E fromElement, boolean inclusive)
→ 주어진 객체보다 큰 객체들을 NavigableSet으로 반환, 주어진 객체 포함 여부는 두 번째 매개 값으로 설정
NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)
→ 시작과 끝으로 주어진 객체 사이의 객체들을 NavigableSet으로 반환, 시작과 끝 객체의 포함 여부는 두 번째 네 번째 매개 값으로 설정
TreeSet<Integer> treeSet = new TreeSet<>();
// 1 2 4 7 9
treeSet.add(4);
treeSet.add(2);
treeSet.add(9);
treeSet.add(7);
treeSet.add(1);
// 4보다 작은 객체들 반환(4 포함)
NavigableSet<Integer> headSet1 = treeSet.headSet(4, true);
headSet1.forEach(System.out::println); // 1 2 4
System.out.println("-----------------------------------");
// 4보다 작은 객체들 반환(4 미포함)
NavigableSet<Integer> headSet2 = treeSet.headSet(4, false);
headSet2.forEach(System.out::println); // 1 2
System.out.println("-----------------------------------");
// 1~9 사이의 객체 반환(1,9 포함)
NavigableSet<Integer> subSet2 = treeSet.subSet(1, true, 9, true);
subSet2.forEach(System.out::println); // 1 2 4 7 9
System.out.println("-----------------------------------");
// 1~9 사이의 객체 반환(1,9 미포함)
NavigableSet<Integer> subSet1 = treeSet.subSet(1, false, 9, false);
subSet1.forEach(System.out::println); // 2 4 7
'Backend > Java' 카테고리의 다른 글
[Java] LIFO 스택, FIFO 큐 (Stack, Queue) (0) | 2022.02.11 |
---|---|
[Java] Map 인터페이스 특징 및 구현체 (HashMap, Hashtable, LinkedHashMap, Properties, TreeMap) (0) | 2022.02.11 |
[Java] List 인터페이스 특징 및 구현체 (ArrayList, LinkedList, Vector) (0) | 2022.02.09 |
[Java] 자바 컬렉션 프레임워크 소개 (List, Set, Map) (0) | 2022.02.08 |
[Java] 리턴 값이 있는 스레드 (Callable Interface) (0) | 2022.01.19 |