[Java] 자바 #37, Set 계열 컬렉션(HashSet, TreeSet)

Set 계열 컬렉션

- 순서가 없는 배열

- 첨자가 업슴

- 중복값을 가질 수 없다.

HashSet


- Set인터페이스 구현한 컬렉션

- 순서는 중요하지 않고 의미를 가지지 않는다.

- 같은 set 인터페이스 구현컬렉션 내에 중복값을 허용하지 않는다.


HashSet 예제 1

Set<String> set = new HashSet<String>();


요소 추가.

set.add("하나");

set.add("둘");

set.add("셋");

set.add("넷");

set.add("다섯");


System.out.println(set.add("하나")); // 무시 혹은 덮어쓰기...

System.out.println(set.size()); //set 사이즈


모든 요소 출력 -> Set은 순서가 중요하지 않은 집합(***)

System.out.println(set.toString());  //[넷, 둘, 하나, 셋, 다섯] -> 순서가 중요하지 않다고 했음.


Set계열 컬렉션을 사용하면 편한경우 1.

- 중복값을 없애는 코드가 필요한 경우

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
class Example{
    public static void main(String[] args){
        // 로또번호 추출        // 1. ArrayList
        // 2. HashSet
        Random ran = new Random();
        ArrayList<Integer> list = new ArrayList<Integer>();
        // 중복 없애는 코드
        for (int i = 0; i < 6; i++) {
            int num = ran.nextInt(45+ 1//1부터 45까지 랜덤숫자 반환...
            boolean flag = false;
            for (int j = 0; j < i; j++) {
                if (num == list.get(j)) { //이전까지 나왔던 숫자들과 비교해서 중복숫자 나오면 빼줘야했음.
                    flag = true;
                }
            } // for j
            if (flag)
                i--;
            else
                list.add(num);
        }
        System.out.println(list.toString()); // ex) [26, 9, 43, 17, 38, 30]
        
        Set<Integer> set2 = new HashSet<Integer>();
        while (set2.size() < 6) { //셋에서는 이렇게 6자리의 숫자가 정해질때까지 추출하면 됨.
            int num2 = ran.nextInt(45+ 1;
            set2.add(num2);
        }
        System.out.println(set2); // ex) [33, 36, 21, 5, 38, 31]    
    }    
}
cs


HashSet 예제 2

- 복합값 집합(클래스 (객체) 집합)

ex) 

Set<String> set = new HashSet<String>();

set.add("홍길동");

set.add("홍길동"); 

>>> 이 경우에는 "홍길동"이라는 문자열이 두개 겹치므로 당연히 뒤에 나오는 값은 set에 더해지지 않는다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person{
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name=name;
        this.age=age;
    }
    
    @Override
    public String toString() {
        return String.format("%s(%s)",this.name,this.age);
        
    }
}
cs


다음과 같은 Person 클래스가 있다고 할때! 같은 name값을 가지고 같은 age 값을 가지는 객체는 같게 인식할까???

nob!!


Set<Person> set2 = new HashSet<Person>();

set2.add(new Person("홍길동",20));

set2.add(new Person("홍길동",20));


System.out.println(set2.toString());  >>> [홍길동(20), 홍길동(20)] 아니이런?

>> 서로 다른 객체이다보니(new 키워드로 각각 생성한) 같은 값을 가졌는지는 판단하지 않고 다른 객체로만 판단함.

>> Person 클래스에 hashCode() 메소드와 equals() 메소드를 오버라이딩 하여준다.


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
class Person{
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name=name;
        this.age=age;
    }
    
    @Override
    public String toString() {
        return String.format("%s(%s)",this.name,this.age);
        
    }
    @Override
    public int hashCode() {
        return (this.name+this.age).hashCode();
    }
    
    @Override
    public boolean equals(Object obj) {
        //p1.equals(p2)
        if(obj instanceof Person) {
            Person p = (Person)obj;
            return this.hashCode()==p.hashCode(); 
            
        }
        return false;
    }
}
cs


Person 클래스에 다음과 같이 hashCode 메소드와 equals 메소드를 재정의 해준다.

이유 >>

1. 참조변수를 비교할때 주소값을 비교하게 되는데 이를 문자열 형태로 변환한다. 즉, "주소값"으로 변환하고 여기에 hashCode 메소드를 통해 해쉬코드값으로 변환시켜 준다.

2. 이 원리를 통해 주소값을 비교하도록 만들지 말고, 클래스 내의 멤버변수 값을 비교하도록 name과 age의 데이터를 문자열 결합(+)을 통해 "홍길동20" 으로 만든 후 "홍길동20".hashCode() 메소드를 통해 "홍길동20"의 해쉬코드값을 비교하게 만들도록 구조를 바꾸었다.


- 이러한 작업을 클래스내에 작성하여주고 다시한번 컴파일을 하면 같은 객체로써 뒤의 객체는 set에 담기지 않는다.


set2.add(new Person("홍길동",20));

set2.add(new Person("홍길동",20));


System.out.println(set2.toString()); [홍길동(20)]


HashSet 예제 3

Set<String> set = new HashSet<String>();

set.add("사과");

set.add("바나나");

set.add("복숭아");

set.add("사과"); // 얘는 당연히 안담기겠죠?? 아셔야합니다. 이젠ㅎㅎ


과일을 이름순서대로 정렬해주세요. -> Set은 순서개념이 없음. -> 정렬도 못함. -> 되게하려면 Set을 List로 변화시켜야함.(역도 됨)


Set을 List로 바꾸는 방법

List<String> list = new ArrayList<String>(set);

System.out.println("list : "+list.toString()); 정렬 전 list : [복숭아, 사과, 바나나]


정렬

Collections.sort(list); -> Collections 클래스의 라이브러리로 배열계열을 () 안에 넣으면 원형 배열(list 컬렉션 포함)이 정렬됨.

System.out.println("list : "+list.toString());  정렬 후 list : [바나나, 복숭아, 사과]




TreeSet


- Set : 중복값 X, 순서 X(정렬 X)

- 자료구조 특성 때문 -> 자동정렬(데이터를 관리하는 형태 자체가 이미 정렬이 완성된 형태로 관리가 된다.)

- 트리 구조(이진 검색 트리 구조)

- 검색, 범위검색 용이(******) : TreeSet을 선택하는 이유(목적, 용도)


TreeSet 예제1


TreeSet<Integer> set = new TreeSet<Integer>();


set.add(10);

set.add(20);

set.add(30);

set.add(40);

set.add(50);

System.out.println(set); [10, 20, 30, 40, 50] 해쉬셋은 엉망으로 들어있는데 TreeSet은 정렬이 되서 들어있다.


TreeSet 예제2


Random ran = new Random();

TreeSet<Integer> set = new TreeSet<Integer>();


while (set.size() < 30) {

set.add(ran.nextInt(30) + 1);

}

System.out.println( set ); [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] 

>>> 순서대로 1부터 30까지 담긴 set


System.out.println( set.subSet(5, 10) ); [5, 6, 7, 8, 9] 

>>> subSet(a,b) 에서 b는 포함하지 않는(미만이란뜻) set을 반환


System.out.println( set.headSet(10) ); [1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> 헤드셋(a) 는 맨 첫값부터 a미만까지의 값까지의 set을 반환


System.out.println( set.tailSet(10) ); [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]

>>> tailSet(a)는 첫값부터 a미만번째(9)까지의 set을 버리고 나머지 set을 반환.


이런 것도 당연히 가능

TreeSet<String> items = new TreeSet<String>();

items.add("필통");

items.add("스마트폰");

items.add("텀블러");

items.add("티슈");

items.add("우산");

items.add("마우스");             //아이템을 담고(물론 중복없고 한글순서대로 저장되겠죠??)

items.add("키보드");

items.add("커피");

items.add("가위");

items.add("테이프");

items.add("연습장");

//한글의 순서를 숫자라 치고 하는것도 가능!

System.out.println(items.subSet("마", "차"));   [마우스, 스마트폰, 연습장, 우산]

System.out.println(items.headSet("자"));        [가위, 마우스, 스마트폰, 연습장, 우산]

System.out.println(items.tailSet("자"));           [커피, 키보드, 텀블러, 테이프, 티슈, 필통]



TreeSet 예제3


- 객체(참조형) TreeSet


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
31
32
33
class User {
    private String name;
    private int point;
 
    public User(String name, int point) {
        this.name = name;
        this.point = point;
 
    }
 
    @Override
    public String toString() {
 
        return String.format("%s(%d)"this.name, this.point);
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getPoint() {
        return point;
    }
 
    public void setPoint(int point) {
        this.point = point;
    }
 
}
cs


이러한 User 클래스가 있을 때


TreeSet 생성자 안에 compare 메소드를 오버라이딩한 Comparator<> 익명객체를 삽입하는 방법


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Example{
    public static void main(String[] args) {
        TreeSet<User> set = new TreeSet<User>(new Comparator<User>() {
            @Override
            public int compare(User a, User b) {
                //포인트 순으로 정렬할 경우
                return a.getPoint()-b.getPoint();
                
                // 이름순으로 정렬할 경우
                //return a.getName().compareTo(b.getName());
            }
        });
        set.add(new User("홍길동"100));
        set.add(new User("아무개"10));
        set.add(new User("하하하"55));
        set.add(new User("호호호"90));
        set.add(new User("테스트"42));
 
        System.out.println(set);
    }
}
cs


출력 결과 : [아무개(10), 테스트(42), 하하하(55), 호호호(90), 홍길동(100)] (포인트순 오름차순 - 저거 더하기빼기 b.getPoint - a.getPoint 일캐 해주면 내림차순됩니다. )


- TreeSet 생성자의 ( ) 안에 TreeSet의 데이터형으로 제네릭형 Comparator 익명객체를 생성하여 넣어준다. 그리고 그안의 compare 메소드를 오버라이딩 해주면 이런식으로 TreeSet에 있는 참조형 객체데이터에 대한 정렬도 가능하다.


Comparable 인터페이스를 구현하여 정렬하는 방법


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
31
class Unit implements Comparable<Unit>{
    private String name;
    private int point;
    
    public Unit(String name, int point) {
        this.name = name;
        this.point = point;
    }
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getPoint() {
        return point;
    }
    public void setPoint(int point) {
        this.point = point;
    }
    @Override
    public int compareTo(Unit o) {
        return this.point - o.point;
    }
    @Override
    public String toString() {
        return "["+this.name+this.point+"]";
    }
}
cs


Comparable 인터페이스를 구현하는 Unit 클래스가 있다고 할때, 이 Unit 클래스는 compareTo 메소드를 오버라이딩 해줍니다. 그렇게 되면 TreeSet에 값이 삽입될때 오버라이딩된 compareTo 메소드를 이용하여 정렬을 진행하게 됩니다.


TreeSet<Unit> uset = new TreeSet<Unit>();

uset.add(new Unit("A",100));

uset.add(new Unit("B",120));

uset.add(new Unit("C",130));

uset.add(new Unit("D",140));

uset.add(new Unit("E",110));

System.out.println("uset"+uset); 


출력 결과 : uset[[A100], [E110], [B120], [C130], [D140]]


댓글

Designed by JB FACTORY