일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- abstract
- array
- ArrayList
- Class
- collection
- database
- db
- DDL
- default
- DML
- Exception
- Generic
- HashSet
- Interface
- Java
- Java 입문
- java 자료형
- java 클래스
- JavaBean
- javabeans
- JAVA의 특징
- LinkedList
- linkedset
- list
- mariadb
- Private
- protected
- public
- RuntimeException
- set
- Today
- Total
GeehDev
[Java] 컬렉션 - 2) Set (HashSet, LinkedHashSet, TreeSet) 본문
컬렉션의 경우 내용이 많아
- [Java] 컬렉션 - 1) 컬렉션과 List(ArrayList, LinkedList, Vector)
- [Java] 컬렉션 - 2) Set (HashSet, LinkedHashSet, TreeSet)
- [Java] 컬렉션 - 3) Set(HashMap, HashTable, LinkedHashMap, TreeMap) - 작성중
의 3개의 시리즈로 구성됩니다.
Set
인덱스(순서)가 없는 데이터 집합으로, 순서가 없이 원소 자체만으로 원소를 구분하기 때문에 데이터간 중복을 허락하지 않습니다.
리스트와 마찬기지로 저장공간을 동적으로 관리할 수 있습니다.
💡 Set의 데이터 중복확인 매커니즘
Set에서는 데이터 중복을 허용하지 않는다. 그렇다면 어떻게 중복을 체크할까?1)
hashCode()
메서드를 이용하여 해쉬코드가 동일한지 비교
2)equal()
메서드를 이용해서 동일 여부를 비교
위 두 과정에 대해 모두 동일하다고 판단되어야 중복 데이터로 확인합니다.
사용자 정의 클래스의 경우 위 매커니즘에 따라 동일한 클래스이지만 각 각의 객체로 생성한 경우 Set의 입장에서는 각 각 다른 값이 됩니다.// 사용자 정의 클래스 class A { int value; A(int value) { this.value = value; } } // Set에 넣어보기 public class Test { public static void main(String[] args) { Set<A> testSet = new HashSet<A>(); A a1 = new A(5); A a2 = new A(5); testSet.add(a1); testSet.add(a2); // 결과 : 5, 5는 같은 값이지만, 동일한 클래스로 각 각 다른 객체로 생성하여 데이터 중복 메커니즘에서 걸러지지 않음 // // 1) `hashCode()` 메서드를 이용하여 해쉬코드가 동일한지 비교 System.out.println(a1.equals(a2)); // 출력 : false // 2) `equal()` 메서드를 이용해서 동일 여부를 비교 System.out.println("a1 : " + a1.hashCode() + " a2 : " + a2.hashCode()); // 출력 : a1 : 509886383 a2 : 1854778591 = > 해쉬코드 서로 다름 } }
따라서 사용자 정의 클래스의 경우 중복값 체크가 필요하다면,
hashCode()
와equal()
메서드에 오버라이딩하여 중복값 체크를 할 수 있도록 해야합니다.class A { int value; A(int value) { this.value = value; } // hashCode() 오버라이딩 @Override public int hashCode() { return hash(value); } // equals() 오버라이딩 @Override public boolean equals(Object e) { if (e instanceof A) { if(this.value == ((A)e).value) { return true; } } return false; } } // Set에 넣어보기 public class Main { public static void main(String[] args) { Set<A> testSet = new HashSet<A>(); A a1 = new A(5); A a2 = new A(5); testSet.add(a1); testSet.add(a2); // 데이터 중복확인 기준인 equals()와 hashCode()를 오버라이딩 하여 각 각 객체지만 들어간 값으로 중복확인 가능하도록 정의 // // 1) `hashCode()` 메서드를 이용하여 해쉬코드가 동일한지 비교 System.out.println(a1.equals(a2)); // 출력 : true // 2) `equal()` 메서드를 이용해서 동일 여부를 비교 System.out.println("a1 : " + a1.hashCode() + " a2 : " + a2.hashCode()); // a1 : 36 a2 : 36 = > 해쉬코드 서로 동일 } }
HashSet
- 데이터를 구분할 때 데이터 그 자체로 구분합니다.
- 데이터 자체로 구분하기 때문에 데이터의 중복을 허용하지 않습니다.
- 인덱스가 없어 입력 순서와 출력 순서가 다를 수 있습니다.
- 객체 생성 시
생성자(저장용량)
으로 지정해줄 수 있으며, 미지정 시 16으로 설정됩니다.
// 사용법
HashSet<제네릭 타입지정> 참조변수 = new HashSet<제네릭 타입지정>();
HashSet<제네릭 타입지정> 참조변수 = new HashSet<제네릭 타입지정>(저장용량);
// 다형성에 따라 부모인터페이스인 Set 타입으로 객체 생성가능
Set<제네릭 타입지정> 참조변수 = new HashSet<제네릭 타입지정>();
Set<제네릭 타입지정> 참조변수 = new HashSet<제네릭 타입지정>(저장용량);
// 사용예시
Set<String> hSet = new HashSet<String>();
hSet.add("바보");
hSet.add("멍청이");
hSet.add("멍청이"); // 데이터 중복으로 데이터가 추가되지 않음.
hSet.add("말미잘");
System.out.println(hSet); // 출력 : [멍청이, 말미잘, 바보]
LinkedHashSet
- 데이터를 구분할 때 데이터 그 자체로 구분합니다.
- 데이터 자체로 구분하기 때문에 데이터의 중복을 허용하지 않습니다.
- 데이터의 입력된 순서를 저장하여 입력 순서와 출력 순서가 동일합니다. (인덱스정보 X)
// 사용법
LinkedHashSet<제네릭 타입지정> 참조변수 = new LinkedHashSet<제네릭 타입지정>();
LinkedHashSet<제네릭 타입지정> 참조변수 = new LinkedHashSet<제네릭 타입지정>(저장용량);
// 다형성에 따라 부모인터페이스인 Set 타입으로 객체 생성가능
Set<제네릭 타입지정> 참조변수 = new LinkedHashSet<제네릭 타입지정>();
Set<제네릭 타입지정> 참조변수 = new LinkedHashSet<제네릭 타입지정>(저장용량);
// 사용예시
Set<String> lHSet = new LinkedHashSet<String>();
lHSet.add("바보");
lHSet.add("멍청이");
lHSet.add("멍청이"); // 데이터 중복으로 데이터가 추가되지 않음.
lHSet.add("말미잘");
System.out.println(lHSet); // 출력 : [바보, 멍청이, 말미잘]
TreeSet
- 데이터를 구분할 때 데이터 그 자체로 구분합니다.
- 데이터 자체로 구분하기 때문에 데이터의 중복을 허용하지 않습니다.
- 데이터의 입력된 순서와 상관없이 데이터의 크기 순으로 출력 순서합니다. (인덱스정보 X)
=> 크기 비교에 대해 정의된 타입만 원소로 담을 수 있습니다. - 정렬기능이 추가된,
SortedSet
, 검색기능이 추가된NavigableSet
인터페이스의 구현 클래스로 추가된 정렬 및 검색기능을 사용하기 위해서는 객체 생성 시Set
이 아니라TreeSet
으로 생성해야만 합니다.
// 사용법
TreeSet<제네릭 타입지정> 참조변수 = new TreeSet<제네릭 타입지정>();
// 다형성에 따라 부모인터페이스인 Set 타입으로 객체 생성가능
// 다만, Set 타입으로 객체 생성 시 SortedSet, NavigableSet에서 추가된 정렬 및 검색 관련 메서드 사용 불가
Set<제네릭 타입지정> 참조변수 = new TreeSet<제네릭 타입지정>();
// 사용예시
TreeSet<String> tSet = new TreeSet<String>();
tSet.add("바보");
tSet.add("멍청이");
tSet.add("멍청이"); // 데이터 중복으로 데이터가 추가되지 않음.
tSet.add("말미잘");
System.out.println(tSet); // 출력 : [말미잘, 멍청이, 바보]
💡 사용자 정의 클래스의 크기비교
TreeSet
의 경우 크기 비교에 대해 정의된 타입만 원소로 담을 수 있습니다.
즉, 사용자 정의 클래스를 원소로 담기 위해서는 크기 비교 부분을 정의해줘야만 합니다.
(크기 비교 정의가 없을 시,class 클래스명 cannot be cast to class java.lang.Comparable
에러 발생)
방법 ① : Comparable<T> 인터페이스 구현// 사용자 정의 클래스 class A implements Comparable<A> { int value; A(int value) { this.value = value; } // Comparable<T> 인터페이스 내 CompareTo(T t) 추상 메서드 구현 @Override public int compareTo(A e) { if(this.value < e.value) { return -1; } else if(this.value == e.value) { return 0; } else { return 1; } } } // TreeSet에 넣어보기 public class Main { public static void main(String[] args) { Set<A> testSet = new TreeSet<A>(); A a1 = new A(5); A a2 = new A(3); A a3 = new A(1); A a4 = new A(9); testSet.add(a1); testSet.add(a2); testSet.add(a3); testSet.add(a4); }
방법 ② : TreeSet<E> 객체 생성 시 생성자 매개변수로 Comparator<T> 객체 제공// 사용자 정의 클래스 class A { int value; A(int value) { this.value = value; } } // TreeSet에 넣어보기 public class Main { public static void main(String[] args) { Set<A> testSet = new TreeSet<A>( new Comparator<A> () { // Comparator<T> 인터페이스 내 compare(T t) 추상 메서드 구현 public int compare(A a1, A a2) { if(a1.value < a2.value) { return -1; } else if(a1.value == a2.value) { return 0; } else { return 1; } } } ); A a1 = new A(5); A a2 = new A(3); A a3 = new A(1); A a4 = new A(9); testSet.add(a1); testSet.add(a2); testSet.add(a3); testSet.add(a4); } }
주요 메서드
Set 컬렉션의 경우 List와 다르게 인덱스를 관리하고 있지 않아, 인덱스 관련 메서드가 모두 사라진 형태입니다.
- 데이터 추가
NO 리턴타입 메서드명 설명 1 boolean add(E element)
- 매개변수로 입력된 원소를 Set에 추가합니다.
- 해당 리스트 객체를 생성할 때 제네릭 타입으로 지정한 타입만 추가할 수 있습니다.2 boolean addAll(Collection<? extends E> c)
- 매개변수로 입력된 컬렉션 전체를 Set에 추가합니다.
- 해당 메서드를 이용하여 해당 객체에 대하여 깊은 복사를 할 수 있습니다.
- 데이터 변경
Set컬렉션의 경우 인덱스가 없어 데이터를 특정데이터를 변경할 수 없습니다.
변경을 원할 시 해당 데이터 삭제 후 데이터를 추가하는 식으로 작업해야합니다.
- 데이터 삭제
NO 리턴타입 메서드명 설명 3 boolean remove(Object o)
- 원소 중 매개변수 입력과 동일한 객체를 삭제합니다.
- 매개변수 타입이 Object임으로 매개변수 입력 시 해당하는 원소의 타입과 동일한 객체로 입력해줘야합니다.4 void clear()
- 해당 Set의 전체 원소를 삭제합니다.
- 데이터 정보추출
NO 리턴타입 메서드명 설명 5 int size()
- Set 내 원소 개수를 반환해줍니다. 6 boolean isEmpty()
- Set 내 원소가 한개도 없으면 true, 1개 이상 있으면 false를 반환합니다.
- empty와 null은 완전히 다른 개념으로 empty는 Set이 비어있을 뿐 Set 자체는 존재한 경우 입니다.7 boolean contains(Object o)
- 매개변수로 입력한 원소가 Set 내에 있는지 여부를 있으며 true, 없으면 false로 리던 합니다.
- 매개변수 타입이 Object임으로 매개변수 입력 시 해당하는 원소의 타입과 동일한 객체로 입력해줘야합니다.8 Iterator<E>
iterator()
- Set 객체 내의 데이터를 연속으로 꺼내는 Iterator 객체를 리턴합니다.
-hasNext()
: 다음으로 가리킬 원소 존재여부를 boolean 값으로 리턴 /next()
: 다음 원소 위치로 가서 읽은 값을 리턴하는 메서드 등의 Iterator 내 메서드를 활용할 수 있습니다.
💡 Set 내 원소 추출하기
Set 내 원소를 이용하기 위해서는 상황에 맞게 아래 두 가지 방법을 활용할 수 있습니다.
- 위 처럼
iterator()
메서드로 Iterator 객체로 변환하여 next(), hasNext()등의 메서드를 활용하는 방법- for-each 구문 활용
// for-each 구문 Set<String> hSet8 = new HashSet<String>(); hSet8.add("가"); hSet8.add("나"); hSet8.add("다"); for(String e : hSet8) { System.out.print(e + " "); } // 출력 : 가 나 다
- 배열(Array)로 변환
NO 리턴타입 메서드명 설명 9 Object[] toArray()
- Set을 Object 타입의 배열로 변환합니다.
- Object[]이기 때문에 변환된 배열을 실제 사용시에는 해당하는 타입으로 다운캐스팅이 필요할 수 있습니다.10 T[] toArray(T[] t)
- Set을 입력매개변수로 받은 타입의 배열로 변환합니다.
- 입력매개변수의 배열 객체에서 배열의 크기를 입력해줘야합니다.(배열의 특징)
- 리스트의 사이즈보다 작은 크기를 입력 시 자동으로 리스트 사이즈만큼으로 확장하여 배열로 변환합니다.
- 리스트의 사이즈보다 큰 크기를 입력 시 남은 자리에 null이 들어간 배열로 변환합니다.
🌳TreeSet만의 메서드
SortedSet
,NavigableSet
의 인터페이스를 구현한TreeSet
에서 정렬 및 검색 관련 메서드를 이용할 수 있습니다.
이 때 추가된 정렬 및 검색기능을 사용하기 위해서는 객체 생성 시Set
이 아니라TreeSet
으로 생성해야만 합니다.
- 데이터 검색
NO 리턴타입 메서드명 설명 11 E first()
- Set 원소 중 가장 작은 원소값을 리턴해줍니다. 12 E last()
- Set 원소 중 가장 큰 원소값을 리턴해줍니다. 13 E lower(E element)
- 매개변수로 입력된 원소보다 작은 원소 중 가장 큰 원소를 리턴해줍니다. 14 E higher(E element)
- 매개변수로 입력된 원소보다 큰 원소 중 가장 작은 원소를 리턴해줍니다. 15 E floor(E element)
- 매개변수로 입력된 원소와 같거나 그 보다 작은 원소 중 가장 큰 원소를 리턴해줍니다. 16 E celling(E element)
- 매개변수로 입력된 원소와 같거나 그 보다 큰 원소 중 가장 작은 원소를 리턴해줍니다. - 데이터 꺼내기
NO 리턴타입 메서드명 설명 17 E pollFirst()
- Set 원소 중 가장 작은 값을 꺼내 리턴 (size 변동 O) 18 E pollLast()
- Set 원소 중 가장 큰 값을 꺼내 리턴 (size 변동 O)
- 데이터 부분 집합 생성
NO 리턴타입 메서드명 설명 19 SortedSet<E>
headSet(E toElement)
- toElement 미만인 모든 원소로 구성된 Set을 리턴합니다. 20 NavigableSet<E>
headSet(E toElement, boolean inclusive)
- toElement 이하(ture)/미만(false)의 모든 원소로 구성된 Set을 리턴합니다. 21 SortedSet<E>
tailSet(E toElement)
- toElement 이상인 모든 원소로 구성된 Set을 리턴합니다. 22 NavigableSet<E>
tailSet(E toElement, boolean inclusive))
- toElement 이상(ture)/초과(false)의 모든 원소로 구성된 Set을 리턴합니다. 23 SortedSet<E>
subSet(E fromElement, E toElement)
- fromElement 이상, toElement 미만인 모든 원소로 구성된 Set을 리턴합니다. 24 NavigableSet<E>
subSet(E fromElement, boolean inclusive, E toElement, boolean inclusive)
- fromElement 이상(ture)/초과(false), toElement 이하(ture)/미만(false)인 모든 원소로 구성된 Set을 리턴합니다. 💡 위 데이터 부분 집합 관련 메서드는 매개변수로
boolean
값을 넣게 될 시true
면 해당 값을 포함하고,false
면 해당 값은 미포함한다고 생각하면 간단합니다!
- 데이터 정렬
NO 리턴타입 메서드명 설명 25 NavigableSet<E>
descendingSet()
- 현재정렬 기준의 반대로 전환합니다.
- 사용예시
// 1. add(E element)
Set<String> hSet = new HashSet<String>();
hSet.add("가");
hSet.add("나");
hSet.add("다");
System.out.println(hSet); // 출력 : [가, 다, 나]
// 2. addAll(Collection<? extends E> c)
Set<String> hSet1 = new HashSet<String>();
Set<String> hSet2 = new HashSet<String>();
hSet1.add("가");
hSet1.add("나");
hSet1.add("다");
hSet2.add("A");
hSet2.add("B");
hSet2.addAll(hSet1);
System.out.println(hSet2); // 출력 : [가, A, B, 다, 나]
// 3. remove(Object o)
Set<String> hSet3 = new HashSet<String>();
hSet3.add("가");
hSet3.add("나");
hSet3.add("다");
System.out.println(hSet3); // 출력 : [가, 다, 나]
hSet3.remove("가");
// remove(Object o) 객체로 매개변수를 입력해줘야한다는 점 명심!
// String 타입의 경우 "" 로 감싸져있는 것 자체가 String 타입임을 명시해주는 것
System.out.println(hSet3); // 출력 : [다, 나]
//4. clear()
Set<String> hSet3 = new HashSet<String>();
hSet4.add("가");
hSet4.add("나");
hSet4.add("다");
System.out.println(hSet4); // 출력 : [가, 다, 나]
hSet4.clear();
System.out.println(hSet4); // 출력 : []
// 5. size()
Set<String> hSet5 = new HashSet<String>();
hSet5.add("가");
hSet5.add("나");
hSet5.add("다");
System.out.println(hSet5); // 출력 : [가, 다, 나]
System.out.println(hSet5.size()); // 출력 : 3
hSet5.remove("가");
System.out.println(hSet5); // 출력 : [다, 나]
System.out.println(hSet5.size()); // 출력 : 2
hSet5.clear();
System.out.println(hSet5); // 출력 : []
System.out.println(hSet5.size()); // 출력 : 0
// 6. isEmpty()
Set<String> hSet6 = new HashSet<String>();
hSet6.add("가");
hSet6.add("나");
hSet6.add("다");
System.out.println(hSet6); // 출력 : [가, 다, 나]
System.out.println(hSet6.isEmpth()); // 출력 : false
hSet6.remove("가");
System.out.println(hSet6); // 출력 : [다, 나]
System.out.println(hSet6.isEmpth()); // 출력 : false
hSet6.clear();
System.out.println(hSet6); // 출력 : []
System.out.println(hSet6.isEmpth()); // 출력 : true
// 7. contains(Object o)
Set<String> hSet7 = new HashSet<String>();
hSet7.add("가");
hSet7.add("나");
hSet7.add("다");
System.out.println(hSet6); // 출력 : [가, 다, 나]
System.out.println(hSet6.contains("나")); // 출력 : tuue
System.out.println(hSet6.contains("A")); // 출력 : false
// contains(Object o) 객체로 매개변수를 입력해줘야한다는 점 명심!
// String 타입의 경우 "" 로 감싸져있는 것 자체가 String 타입임을 명시해주는 것
// 8. iterator()
Set<String> hSet8 = new HashSet<String>();
hSet8.add("가");
hSet8.add("나");
hSet8.add("다");
Iterator<String> iterator = hSet8.iterator();
// Iterator객체 생성 후 hSet8을 Iterator 객체로 변환하여 대입
while(iterator.hasNext()) {//iterator객체 내 꺼낼 값이 있을동안 반복
System.out.print(iterator.next() + " ");
// Iterator 내 next()메서드로 값 하나씩 반복하면서 꺼내서 출력
}
// 출력 : 가 나 다
// 9. toArray()
Set<String> hSet9 = new HashSet<String>();
hSet9.add("가");
hSet9.add("나");
hSet9.add("다");
Object[] objArr = hSet9.toArray();
System.out.println(Arrays.toString(objArr)); // 출력 : [가, 다, 나]
// toArray()메서드를 이용해 변환한 배열의 타입은 Object[]라는 점을 꼭 명심해야합니다.
// 10. toArray(T[] t)
Set<String> hSet10 = new HashSet<String>();
hSet10.add("가");
hSet10.add("나");
hSet10.add("다");
String[] strArr1 = hSet10.toArray(new String[0]);
// 대상 리스트 사이즈보다 작게 입력 시 자동으로 리스트의 사이즈크기의 배열로 변환
String[] strArr2 = hSet10.toArray(new String[5]);
// 대상 리스트 사이즈보다 크게 입력 시 남는 자리에는 null이 입력된 배열로 변환
System.out.println(Arrays.toString(strArr1)); // 출력 : [가, 다, 나]
System.out.println(Arrays.toString(strArr2)); // 출력 : [가, 다, 나, null, null]
// 11. frist()
TreeSet<Integer> tSet = new TreeSet<Integer>();
tSet.add(1);
tSet.add(2);
tSet.add(3);
tSet.add(4);
tSet.add(5);
System.out.println(tSet.first()); // 출력 : 1
// 12.last()
System.out.println(tSet.last()); // 출력 : 5
// 13. lower()
System.out.println(tSet.lower(3)); // 출력 : 2
// 14.higher()
System.out.println(tSet.higher(3)); // 출력 : 4
// 15. floor()
System.out.println(tSet.floor(3)); // 출력 : 3
// 16. ceiling()
System.out.println(tSet.ceiling(3)); // 출력 : 3
// 17. pollFirst()
System.out.println(tSet); // 출력 : [1, 2, 3, 4, 5]
System.out.println(tSet.pollFirst()); // 출력 : 1
System.out.println(tSet); // 출력 : [2, 3, 4, 5]
tSet.add(1);
// 18. pollLast()
System.out.println(tSet); // 출력 : [1, 2, 3, 4, 5]
System.out.println(tSet.pollLast()); // 출력 : 5
System.out.println(tSet); // 출력 : [1, 2, 3, 4]
tSet.add(5);
// 19. headSet(E toElement)
SortedSet<Integer> sSet;
sSet = tSet.headSet(3);
System.out.println(sSet); // 출력 : [1, 2]
// 20. headSet(E toElement, boolean inclusive)
NavigableSet<Integer> nSet1;
nSet = tSet.headSet(3, true);
System.out.println(nSet); // 출력 : [1, 2, 3]
nSet = tSet.headSet(3, false);
System.out.println(nSet); // 출력 : [1, 2]
// 21. tailSet(E toElement)
sSet = tSet.tailSet(3);
System.out.println(sSet); // 출력 : [4, 5]
// 22. tailSet(E toElement, boolean inclusive)
nSet = tSet.tailSet(3, true);
System.out.println(nSet); // 출력 : [3, 4, 5]
nSet = tSet.tailSet(3, false);
System.out.println(nSet); // 출력 : [4, 5]
// 23. subSet(E toElement)
sSet = tSet.subSet(2,4);
System.out.println(sSet); // 출력 : [2, 3]
// 24. subSet(E fromElement, boolean inclusive, E toElement, boolean inclusive)
nSet = tSet.subSet(2,true,4,true);
System.out.println(nSet); // 출력 : [2, 3, 4]
nSet = tSet.subSet(2,true,4,false);
System.out.println(sSet); // 출력 : [2, 3]
nSet = tSet.subSet(2,false,4,true);
System.out.println(nSet); // 출력 : [3, 4]
nSet = tSet.subSet(2,false,4,false);
System.out.println(sSet); // 출력 : [2, 3]
// 25. descendingSet()
nSet = tSet.desendingSet();
System.out.println(nSet); // 출력 : [5, 4, 3, 2, 1]
System.out.println(tSet); // 출력 : [1, 2, 3, 4, 5]
'Study > Java' 카테고리의 다른 글
[Java] 컬렉션 - 1) 컬렉션과 List(ArrayList, LinkedList, Vector) (0) | 2024.09.15 |
---|---|
[Java] JavaBeans (0) | 2024.09.15 |
[Java] 제네릭 (2) | 2024.09.15 |
[Java] 예외 처리 (1) | 2024.09.15 |
[Java] 이너 클래스 (0) | 2024.09.15 |