본문 바로가기
Java/Java

컬렉션/Set<E>) TreeSet<E>, 주요 메소드, 크기 비교

by 박채니 2022. 4. 1.

안녕하세요, 코린이의 코딩 학습기 채니 입니다.

 

개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.


컬렉션이란?

- 동일한 타입을 묶어 관리하는 자료 구조

 

컬렉션 프레임워크란?

- 리스트, 스택, 큐, 트리 등의 자료구조에 정렬, 탐색 등의 알고리즘을 구조화해 놓은 프레임워크

- 여러 개의 데이터 묶음 자료를 효과적으로 처리하기 위해 구조화된 클래스 또는 인터페이스의 모음

 

컬렉션의 특성에 따라 구분하면 크게 List<E>, Set<E>, Map<K, V>로 나뉩니다.

메모리의 입출력 특성에 따라 기존 컬렉션 기능을 확장/조합한 Stack<E>, Queue<E>도 있습니다.(사진에선 누락)

 


☞ Set<E> 컬렉션 프레임워크

인덱스 정보를 포함하고 있지 않은, 집합의 개념

순서를 유지하지 않고 저장

중복 허용 하지 않음

- Null 값도 중복을 허용 하지 않으므로 하나의 Null 값만 저장

 

TreeSet<E>의 주요 메소드 (인덱스 관련 메소드 X)

구분 리턴타입 메소드명 기능
데이터 추가 boolean add(E element) 매개변수로 입력된 원소를 리스트에 추가
boolean addAll(Collection<? Extends E> c) 매개변수로 입력된 컬렉션 전체를 추가
데이터 삭제 boolean removed(Object o) 원소 중 매개변수 입력과 동일한 객체 삭제
void clear() 전체 원소 삭제
데이터 정보
추출
boolean isEmpty() Set<E> 객체가 비어 있는 지 여부를 리턴
boolean contains(Object o) 매개변수로 입력된 원소가 있는지 여부를 리턴
int size() 리스트 객체 내에 포함된 원소의 개수
Iterator<E> iterator() Set<E> 객체 내의 데이터를 연속해
꺼내는 Iterator 객체 리턴
Set<E> 객체
배열 변환
Object[] toArray() 리스트를 Object 배열로 변환
T[] toArray(T[] t) 입력매개변수로 전달한 타입의 배열로 변환
데이터 검색
E first() Set 원소 중 가장 작은 원솟값 리턴
E last() Set 원소 중 가장 큰 원솟값 리턴
E lower(E element) 매개변수로 입력된 원소보다 작은, 가장 큰 수
E highter(E element) 매개변수로 입력된 원소보다 큰, 가장 작은 수
E floor(E element) 매개변수로 입력된 원소보다 같거나 작은 가장 큰 수
E ceiling(E element) 매개변수로 입력된 원소보다 같거나 큰 가장 작은 수
데이터 꺼내기
E pollFirst() Set 원소들 중 가장 작은 원솟값을 꺼내 리턴
E pollLast() Set 원소들 중 가장 큰 원솟값을 꺼내 리턴
데이터
부분 집합
생성

SortedSet<E> headSet(E toElement) toElement 미만인 모든 원소로 구성된 Set을 리턴
(toElement 미포함)
NavigableSet<E> headSet(E toElement, boolean inclusive) toElement 미만/이하인 모든 원소로 구성된 Set을 리턴
(inclusive=true이면 toElement 포함,
inclusive=false이면 toElement 미포함)
SortedSet<E> tailSet(E fromElement) toElement 이상인 모든 원소로 구성된 Set을 리턴
(fromElement 포함)
NavigableSet<E> tailSet(E fromElement, boolean inclusive) fromElement 초과/이상인 모든 원소로 구성된 Set을 리턴
(inclusive=true이면 fromElement 포함,
inclusive=false이면 fromElement 미포함)
SortedSet<E> subSet(E fromElement, E toElement) fromElement 이상 toElement 미만인 원소들로 이뤄진 Set을 리턴
(fromElement 포함, toELement 미포함)
NavigableSet<E> subSet(E fromElement, boolean frominclusive, E toElement, boolean toinclusive) fromElement 초과/이상 toElement 미만/이하인 원소들로 이뤄진 Set을 리턴
(fromclusive=ture/false이면 fromElement 포함/미포함,
toinclusive=true/false이면 toElement 포함/미포함)
데이터 정렬 NavigableSet<E> descendingSet() 내림차순의 의미가 아니라 현재 정렬 기준을 반대로 전환

 


☞ TreeSet<E>

- 데이터 입력 순서와 상관없이 크기순으로 출력

- Navigable Set<E>와 SortedSet<E>를 부모 인터페이스로 두고 있음

- TreeSet<E> 생성자로 객체를 생성해도 Set<E> 타입으로 선언하면 추가된 정렬/검색 기능 사용 불가

 

TreeSet<E> 인터페이스 객체 생성

Set<제네릭 타입 지정> 참조변수 = new TreeSet<제네릭 타입 지정>();	//Set<E> 메소드만 사용 가능
TreeSet<제네릭 타입 지정> 참조변수 = new TreeSet<제네릭 타입 지정>();
		
Set<String> treeSet1 = new TreeSet<>();	//Set<E> 메소드만 사용 가능
TreeSet<String> treeSet2 = new TreeSet<>();

 

메소드 사용해보기

데이터 추가

TreeSet<Integer> treeSet = new TreeSet<>();
for(int i = 50; i > 0; i-=2)
	treeSet.add(i);

System.out.println(treeSet);

@콘솔출력값
[2, 4, 6, 8, .... 44, 46, 48, 50]

 

데이터 검색

//first
System.out.println("first : " + treeSet.first());
		
//last
System.out.println("last : " + treeSet.last());
		
//lower(E element)
//원소보다 작은 가장 큰수
System.out.println("lower : " + treeSet.lower(26)); 
		
//higher(E element)
//원소보다 큰 가장 작은 수
System.out.println("higher : " + treeSet.higher(26)); 
		
//floor(E element)
//원소보다 같거나 작은 가장 큰수
System.out.println("floor : " + treeSet.floor(25));
System.out.println("floor : " + treeSet.floor(26)); 
		
//ceiling(E element)
//원소보다 같거나 큰 가장 작은 수
System.out.println("ceiling : " + treeSet.ceiling(25));
System.out.println("ceiling : " + treeSet.ceiling(26));	

@콘솔출력값
first : 2
last : 50
lower : 24
higher : 28
floor : 24
floor : 26
ceiling : 26
ceiling : 26

 

데이터 꺼내기

//pollFirst()
//작은 값부터 꺼내 리턴
int treeSize = treeSet.size();	//크기
for(int i = 0; i < treeSize; i++)
	System.out.print(treeSet.pollFirst() + " ");
System.out.println("\n"+treeSet.size());
		
//pollLast()
//큰 값부터 꺼내 리턴
for(int i = 50; i > 0; i-=2)
	treeSet.add(i);
treeSize = treeSet.size();
for(int i = 0; i < treeSize; i++)
	System.out.print(treeSet.pollLast() + " ");
System.out.println("\n"+treeSet.size());

@콘솔출력값
2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 
0
50 48 46 44 42 40 38 36 34 32 30 28 26 24 22 20 18 16 14 12 10 8 6 4 2 
0

 

데이터 부분 집합 생성

for(int i = 50; i > 0; i-=2)
	treeSet.add(i);
		
// 1) SortedSet<E> headSet(E toElement)
//매개변수 원소 미만으로 구성된 Set
SortedSet<Integer> sSet = treeSet.headSet(20);
System.out.println(sSet.toString());
		
// 2) NavigableSet<E> headSet(E toElement, boolean inclusive)
//boolean 여부에 따라 원소값 포함/미포함하여 이하/미만으로 구성된 Set
NavigableSet<Integer> nSet = treeSet.headSet(20, false);	//false는 미포함
System.out.println(nSet.toString());

nSet = treeSet.headSet(20, true);	//true는 포함
System.out.println(nSet.toString());
		
// 3) SortedSet<E> tailSet(E toElement)
//매개변수 원소 이상으로 구성된 Set
sSet = treeSet.tailSet(20);
System.out.println(sSet.toString());
		
// 4) NavigableSet<E> taileSet(E toElement, boolean inclusive)
//boolean 여부에 따라 원소값 포함/미포함하여 이상/초과로 구성된 Set
nSet = treeSet.tailSet(20, false);	//false는 미포함
System.out.println(nSet.toString());

nSet = treeSet.tailSet(20, true);	//true는 포함
System.out.println(nSet.toString());

@콘솔출력값
1) [2, 4, 6, 8, 10, 12, 14, 16, 18]
2) [2, 4, 6, 8, 10, 12, 14, 16, 18]
2) [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
3) [20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]
4) [22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]
4) [20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50]

 

// 5) SortedSet<E> subSet(E fromElement, E toElement)
// 첫번째 매개변수 이상 ~ 두번째 매개변수 미만
sSet = treeSet.subSet(10, 20);
System.out.println(sSet.toString());
		
// 6) NavigableSet<E> subSet(E fromElement, boolean frominclusive, E toElement, boolean toinclusive)
// boolean 여부에 따라 원솟값 포함/미포함하여 이상/초과 ~ 이하/미만으로 구성된 Set
nSet = treeSet.subSet(10, true, 20, false);	//10 = 포함, 20 = 미포함
System.out.println(nSet.toString());
		
nSet = treeSet.subSet(10, false, 20, true);	//10 = 미포함, 20 = 포함
System.out.println(nSet.toString());

@콘솔출력값
5) [10, 12, 14, 16, 18]
6) [10, 12, 14, 16, 18]
6) [12, 14, 16, 18, 20]

 

데이터 정렬

//NavigableSet<E> descendingSet()
System.out.println("정렬 전 " + treeSet);
		
NavigableSet<Integer> desc = treeSet.descendingSet();
System.out.println("정렬 후 " + desc);
		
desc = desc.descendingSet();
System.out.println("다시 뒤집기 " + desc);

@콘솔출력값
정렬 전 [2, 4, 6, 8, 10, 12, 14, 16, .... 36, 38, 40, 42, 44, 46, 48, 50]
정렬 후 [50, 48, 46, 44, 42, 40, .... 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2]
다시 뒤집기 [2, 4, 6, 8, 10, 12, 14, .... 34, 36, 38, 40, 42, 44, 46, 48, 50]

 


☞ 데이터 크기 비교

//Integer 비교
TreeSet<Integer> intSet = new TreeSet<>();
intSet.add(20);
intSet.add(10);
System.out.println(intSet);
	
//String 비교
TreeSet<String> strSet = new TreeSet<>();
strSet.add("다라");
strSet.add("가나");
System.out.println(strSet);

@콘솔출력값
[10, 20]
[가나, 다라]

TreeSet<E>의 가장 큰 특징은 데이터를 크기 순으로 정렬한다고 했습니다.

입력 순서와 상관없이 두 데이터의 크기를 비교하여 "10, 20", "가나, 다라"를 순서대로 출력한 것을 확인할 수 있습니다.

 

이는 자바가 이미 클래스에 크기 비교 기준을 작성해놓았기 때문에 크기를 쉽게 비교하여 출력하는 것입니다.

 

그렇다면 만약, 클래스를 직접 생성하여 크기를 비교한다면 어떻게 될까요?

public class MyData {
	int data1;
	int data2;
	
	public MyData(int data1, int data2) {
		this.data1 = data1;
		this.data2 = data2;
	}
}

두 개의 데이터를 받는 클래스를 생성하여 추가해보겠습니다.

 

TreeSet<MyData> treeSet = new TreeSet<>();
MyData myData1 = new MyData(2, 5);
MyData myData2 = new MyData(3, 3);
treeSet.add(myData1);	//예외발생
treeSet.add(myData2);	//예외발생
System.out.println(treeSet);	//예외발생

이 두 객체 중 어떤 것이 더 클까요?

첫 번째 수가 큰 것은 myData2, 두 수의 합이 큰 것은 myData1... 정확한 기준 산정을 하지 못하기 때문에 예외가 발생되었습니다.

 

따라서 크기의 기준을 정해줘야 하는데요,

이것은 개발자의 마음대로 첫번째 수가 크다면 큰 수, 합이 크다면 큰 수, 두 번째 수가 크다면 큰 수 등등으로 기준을 정해줄 수 있습니다.

 

크기 비교 기준 제공 방법 ①

Comparable<T> 제네릭 인터페이스 구현 → compareTo() 추상 메소드 구현

public class MyData implements Comparable<MyData>{
	int data1;
	int data2;
	
	public MyData(int data1, int data2) {
		this.data1 = data1;
		this.data2 = data2;
	}
	
	@Override
	public int compareTo(MyData o) {
		if(data1 < o.data1)
			return -1;
		else if(data1 == o.data1)
			return 0;
		else
			return 1;
	}
}

※ 자신의 객체가 매개변수 o보다 작을 때는 음수, 같을 때는 0, 클 때는 양수를 리턴해주면 됩니다.

 

제네릭 인터페이스의 제네릭 타입 변수를 MyData로 대입했기 때문에, compareTo()의 입력 타입으로는 동일한 타입의 객체가 들어올 것입니다.

이후 자신의 필드값(data1)과 입력매개변수 필드 값(o.data1)을 비교하여 작을 때, 같을 때, 클 때 각각 -1, 0, 1을 리턴하였습니다.

data1의 크기에 따라 객체의 크기가 결정되게 만들어보았습니다.

 

TreeSet<MyData> treeSet = new TreeSet<>();
MyData myData1 = new MyData(2, 3);
MyData myData2 = new MyData(3, 3);
treeSet.add(myData1);
treeSet.add(myData2);
		
for(MyData m : treeSet)
	System.out.println(m.data1);
    
@콘솔출력값
2
3

 

크기 비교 기준 제공 방법 ②

생성자 매개변수로 Comparator<T>객체 제공 → compare() 메소드 구현

public class MyData {
	int data1;
	int data2;
	
	public MyData(int data1, int data2) {
		this.data1 = data1;
		this.data2 = data2;
	}
}

다시 MyData 클래스는 원복 시켜놓았습니다.

 

TreeSet<MyData> treeSet = new TreeSet<MyData>(new Comparator<MyData>() {
	@Override
	public int compare(MyData o1, MyData o2) {
		if(o1.data1 < o2.data1)
			return -1;
		else if(o1.data1 == o2.data1)
			return 0;
		else
			return 1;
	}
}) ;
		
MyData myData1 = new MyData(2, 3);
MyData myData2 = new MyData(3, 3);
treeSet.add(myData1);
treeSet.add(myData2);
		
for(MyData m : treeSet)
	System.out.println(m.data1);
    
@콘솔출력값
2
3

새롭게 Comparator 인터페이스 구현 클래스를 생성해도 괜찮습니다.

 

 

 

※ Do it! 자바 완전 정복을 참고하여 포스팅하였습니다.