자료구조 #15 - 컬렉션 프레임워크 : TreeSet With Comparable & Comparator

HongInSung·2022년 12월 13일
0
post-thumbnail

이 포스트는 FastCampus에 이 강의를 보고 포스팅되었습니다.
문제가 될 시 삭제될 예정입니다.

TreeSet은 뭐하는 놈인데?

TreeSet은 Set중에서도 정렬을 위해서 사용되는 놈입니다.
이 TreeSet은 내부적으로 BST(이진검색트리) 로 구현이 되어 있습니다.
BST를 구성하는 방법과 정렬하는 방법은 여기서 확인하시면 됩니다.
여기서 중요한 부분은 요소가 추가될때는 기존에 있던 요소와 비교를 해야 한다.
"그래서 이 비교하는 것을 어떻게 구현할까?"를 정해줘야 합니다.
그래서 전 시간인 HashSet은 Equals()와 hashCode()로 논리적 비교를 했다면
이번 시간에 쓸 TreeSet은 기존에 있던 애하고 비교를 해서 어디로 들어가야 하는지를 구현해야 합니다.
그래서 구현하는 것이 Comparable과 Comparator입니다.

String, Integer등 JDK의 많은 클래스들은 이미 Comparable를 구현해놓았답니다

TreeSet 함 써보자

테스트 코드를 한번 적어보겠습니다.

import java.util.TreeSet;

public class TreeSetTest {

    public static void exportStr(TreeSet<String> T) {
        for(String str : T) {
            System.out.println(str); // 출력
        }
    }
    public static void main(String[] args) {
        TreeSet<String> treeSet = new TreeSet<>(); // TreeSet 생성

        treeSet.add(new String("홍길동"));
        treeSet.add(new String("간감상"));
        treeSet.add(new String("다부진"));
        treeSet.add(new String("키리토"));
        treeSet.add(new String("미도리야"));
        treeSet.add(new String("토리코")); // String 데이터 집어넣기

        exportStr(treeSet); // 출력 함수 실행
    }
}

결과는 아래와 같이 나왔습니다.

간감상
다부진
미도리야
키리토
토리코
홍길동

멤버십 리팩토링

전에 만들던 멤버십을 TreeSet을 이용해서 리팩토링 해보겠습니다.

MemberTreeSet 생성

import java.util.TreeSet;
import java.util.Iterator;

public class MemberTreeSet {
    private TreeSet<Member> memberTreeSet;

    public MemberTreeSet() {
        memberTreeSet = new TreeSet<>();
    }

    public void addMember(Member member) {
        memberTreeSet.add(member);
    }

    public boolean removeMember(int memberId) {
        // Iterator 선언
        Iterator<Member> ir = memberTreeSet.iterator();

        // ir.hasNext()가 false가 될 때까지 돈다.
        while(ir.hasNext()) {
            Member member = ir.next(); // 다음 요소를 member에 저장시키고

            if (member.getMemberId() == memberId) { // memberId가 맞는지 확인
                memberTreeSet.remove(member); // 삭제하고
                return true; // true 반환
            }
        }
        System.out.println("해당 멤버id를 가지고 있는 유저가 없습니다.");
        return false;
    }

    public void showAllMember() {
        for (Member member : memberTreeSet) {
            System.out.println(member);
        }
    }
}

테스트 코드 작성

public class MemberTreeSetTest {
    public static void main(String[] args) {
        Member member1 = new Member();
        Member member2 = new Member();
        Member member3 = new Member();
        Member member4 = new Member();
        Member member5 = new Member();

        member1.setMemberId(1); 
        member1.setMemberName("아스나");
        
        member2.setMemberId(2);
        member2.setMemberName("키리토");
        
        member3.setMemberId(3);
        member3.setMemberName("홍인성");
        
        member4.setMemberId(4);
        member4.setMemberName("유지오");
        
        member5.setMemberId(1);
        member5.setMemberName("홍길동");

        MemberTreeSet hashList = new MemberTreeSet();

        hashList.addMember(member1);
        hashList.addMember(member2);
        hashList.addMember(member3);
        hashList.addMember(member4);
        hashList.addMember(member5);

        System.out.println();
        hashList.showAllMember();
    }
}

이렇게 돌리시면 바로 오류가 발생합니다.
java.lang.ClassCastException: ... java.lang.Comparable

Comparable이 구현되어 있지 않다는 뜻입니다.
아까 말했듯 String, int는 Comparable이 구현되어 있지만 지금 사용하고 있는 Member 클래스에 경우, Comparable이 구현되어 있지 않다는 것이죠.

그래서 현재 가지고 있는 Member 클래스에 Compare를 구현해보겠습니다.

Member 클래스에 Comparable 구현

public class Member implements Comparable<Member>{

	// prev code
    
    @Override
    public int compareTo(Member member) {
        if (this.memberId > member.memberId) {
            return 1;
        } else if ( this.memberId < member.memberId) {
            return -1;
        } else {
            return 0;
        }
    }
}

Member 클래스에 Comparable 인터페리스를 상속받아서 구현하면 됩니다.
현재는 id로 정렬할 거니까, id쪽만 구현해둡시다.
그리고 다시 테스트 코드를 실행시키면...!

짜잔! 이렇게 중복도 없애고, 정렬도 됩니다.

어케믿음 ㅋ 순서대로 집어넣었잖어 ㅋㅋ

뭐, 순서를 섞어서 테스트코드를 적는 방법도 있겠지만, 더 확실한 방법으로 인증하겠습니다.
그렇다면 저 순서를 뒤집어 보겠습니다.

순서 역순출력

그러기 위해선 다시 Member 클래스로 가셔서 compareTo를 수정하셔야 합니다.

@Override
public int compareTo(Member member) {
	if (this.memberId > member.memberId) {
		return -1;
	} else if ( this.memberId < member.memberId) {
		return 1;
	} else {
		return 0;
	}
}

return에 있는 1과 -1의 위치를 서로 바꿔주기만 하면 됩니다.
이렇게 놓고 다시 테스트 코드를 돌려보겠습니다.

자 역순으로 출력됬죠?
이렇게 정렬 기능도 된다는 것을 입증해냈습니다.

너무 코드가 길다..

코드를 한번 줄여볼게요.

@Override
public int compareTo(Member member) {
	if (this.memberId > member.memberId) {
		return 1;
	} else if ( this.memberId < member.memberId) {
		return -1;
	} else {
		return 0;
	}
}

이 코드를 한줄로 만들어보겠습니다.

@Override
public int compareTo(Member member) {
	return this.memberId - member.memberId;
}

이렇게 한줄로 끝낼 수 있습니다.
역순도 진짜 간단합니다.

@Override
public int compareTo(Member member) {
	return (this.memberId - member.memberId) * (-1);
}

뒤에 -1을 곱해주시면 됩니다.

자 이제 Comparable은 아셨고, 다음으로 Comparator로 구현해봅시다.

Comparable VS Comparator

Comparable과 Comparator의 다른점은 매개변수에 차이입니다.
Comparable은 매개변수를 한개를 받죠? 하지만 Comparator는 2개를 받습니다.
즉, 비교대상이 다르다는 거죠.
Comparable는 비교대상이 인거고, Comparator은 나와 비교대상이라는 거죠.
한번 코드를 짜볼까요?

public class Member implements Comparator<Member> {

    public Member(){} // 기본 생성자 생성

    @Override
    public int compare(Member m1, Member m2) { // Compare 구현
        return (m1.memberId - m2.memberId);
    }
}

그리고 한가지 작업을 더 해주셔야 합니다.
아까 만들었단 MemberTreeMap에 생성자 부분에 자신이 어떤 타입으로 Comparator를 구현해놨는지, 그 대상을 적어주셔야 합니다.

public MemberTreeSet() {
    memberTreeSet = new TreeSet<Member>(new Member());
}

Comparator 얜 어따 씀?

사실 제일 많이 사용하는 것은 Comparable입니다.
Comparator는 이미 Comparable로 구현된 것을 다시 재구현 시킬 수 있죠.
말보다는 코드를 보시는게 이해가 되실겁니다.

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

class MyCompare implements Comparator<String> {

    @Override
    public int compare(String s1, String s2) {
        return (s1.compareTo(s2)) * -1;
    }
}

public class ComparableToComparatorTest {
    public static void main(String[] args) {
        Set<String> set = new TreeSet<>(new MyCompare()); // 위에서 만든 Comparator 적용
        set.add(new String("aaa")); // 전에 말했듯, String은 이미 Comparable이 구현되어 있음.
        set.add(new String("aaa"));
        set.add(new String("ccc"));
        set.add(new String("bbb"));

        System.out.println(set); // 출력하면 [ccc, bbb, aaa]가 출력됨.
    }
}

마치며

다음시간엔 Map 인터페이스를 배워보도록 하겠습니다.
아마 다음시간에 마무리가 될거같네요.
한 2주동안 여러가지 일이 닥쳐오면서 포스팅도 제대로 못올렸네요 ㅎ;
앞으로는 제대로 올려보도록 하겠습니다. 감사합니다.

profile
안녕하세요! 풀스택 노려보고 있는 홍인성입니다!

0개의 댓글