백엔드 개발자 2024. 7. 16. 23:32

 

  • 둘 모두 인터페이스
  • Comparable (비교의 대상) : 자신다른 객체를 비교
    • 숫자 클래스들, 불리언, 문자열 (왜 자기자신과 비교하냐면 숫자, 문자열들은 비교 가능한 기준이 있으므로)
    • 이후 배울 Date, BigDecimal, BigInteger 등
  • Comparator (비교의 주체) : 주어진 두 객체를 비교
    • 컬렉션에서는 정렬의 기준으로 사용
    • Arrays의 정렬 메소드, TreeSet이나 TreeMap등의 생성자에 활용

 

 

 

Comparator는 양수를 반환하면 자리를 바꾸는 식으로 동작한다.

커스터마이징해서 비교기준을 마음대로 바꿀 수 있다.

 

Comparable도 내부의 compareTo 메서드를 오버라이딩해서 기준 변경 가능.

 

 

 

 

 

본문 제목


 

				Integer int1 = Integer.valueOf(1);
        Integer int2 = Integer.valueOf(2);
        Integer int3 = Integer.valueOf(3);

        //  대상보다 작을 때: -1, 같을 때 0, 클 때: 1
        int _1_comp_3 = int1.compareTo(3);
        int _2_comp_1 =  int2.compareTo(1);
        int _3_comp_3 =  int3.compareTo(3);
        int _t_comp_f = Boolean.valueOf(true).compareTo(Boolean.valueOf(false));
        int _abc_comp_def = "ABC".compareTo("DEF");
        int _def_comp_abc = "efgh".compareTo("abcd");

 

  • compareTo는 Comparable을 상속받는 클래스의 메서드라서 자기자신과 비교하는 메서드이다.
  • 3은 1보다 2만큼 크므로 대상보다 얼마나 큰지, 작은지를 반환한다.

 

		Integer[] nums = {3, 8, 1, 7, 4, 9, 2, 6, 5};
        String[] strs = {
                "Fox", "Banana", "Elephant", "Car", "Apple", "Game", "Dice"
        };

				//  ⭐️ Arrays 클래스의 sort 메소드
        //  - 기본적으로 compareTo에 의거하여 정렬
        //  - 인자가 없는 생성자로 생성된 TreeSet, TreeMap도 마찬가지
        Arrays.sort(nums);
        Arrays.sort(strs);

 

  • sort도 compareTo에 의거해서 정렬한다고 한다. 하나씩 비교해가며 줄을 세운다.

 

 

comparator

public class IntDescComp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
}

 

  • 음수가 나오면 o1이 더 크고, 양수가 나오면 o2가 더 크기 때문에  Arrays.sort를 하면 양수면 o2가 더 크다고 판단해서 두 원소의 자리를 바꾼다. 그래서 내림차순으로 된다.

 

 

 

 

public class CloseToInt implements Comparator<Integer> {
    int closeTo;
    public CloseToInt(int closeTo) {
        this.closeTo = closeTo;
    }

    @Override
    public int compare(Integer o1, Integer o2) {
        return (Math.abs(o1 - closeTo) - Math.abs(o2 - closeTo));
    }
}
Arrays.sort(nums, new CloseToInt(5));

 

  • closeTo라는 변수를 받고, 그 변수와의 거리차리를 각각 구한다. 그다음 o1 -o2 거리로 비교한다. 
  • 음수면 o1이 더 가까운 것이고, 양수면 o2가 더 가까운 것이다. 그래서 양수면 o2가 더 가까우니 자리를 바꾼다.

 

Comparator(a,b)가 양수면  a,b 자리를 바꾸고 a,b가 음수거나 0이면 그대로 위치를 두는 것으로 보인다.

Comparator를 재정의 하는 것으로 비교하는 기준을 내맘대로 할 수 있다.

 

 

 

익명 클래스를 활용하는 방법

				//  ⭐️ 익명 클래스, 이후 배울 람다로 더 간편하게
        Arrays.sort(strs, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });
  • 서로의 길이를 비교하는데, 양수면 o2의 길이가 더 작은 상황. 이때 순서를 바꾸는 거니까 길이 오름 차순으로 정렬될 것이다.

 

 

 

				//  💡 ArrayList도 sort 메소드 사용 가능
        ArrayList<Integer> numsAry = new ArrayList<>(Arrays.asList(nums));
        numsAry.sort(new IntDescComp());

 

				numsAry.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return (o1 % 2) - (o2 % 2); // 짝수가 앞으로 오도록
            }
        });
  • 이 경우,  o1이 홀수고 o2가 짝수면 자리를 바꾸므로 짝수가 계속 앞으로 오게 될 것이다\

 

 

 

 

 

 

 

 

public class UnitSorter implements Comparator<Unit> {
    @Override
    public int compare(Unit o1, Unit o2) {
        int result = getClassPoint(o2) - getClassPoint(o1);

        //  ⚠️ 제거하고 실행해 볼 것 - 내용이 같은 인스턴스들이 있을 시
        if (result == 0) result = o1.hashCode() - o2.hashCode();
        
        return result;
    }

    public int getClassPoint (Unit u) {
        int result = u.getSide() == Side.RED ? 10 : 0;

        if (u instanceof Swordman) result += 1;
        if (u instanceof Knight) result += 2;
        if (u instanceof MagicKnight) result += 3;

        return result;
    }
}

 

  • getClassPoint 는 게임 클래스에서 진영이나 등급에 따라 점수를 매기는 메서드이다.
  • 그러므로 compare시에는 o2가 높으면 양수이니 점수가 높을수록 앞으로 가게 된다.

 

내용이 같은 인스턴스가 있을때 처리를 주석으로 하면, 서열이 같은 애들끼리는 비교가 안된다. 그래서 TreeSet의 경우 비교가 안되면 그값을 집어넣지 않기 때문에 Knight 2개, Swordman2개가 줄어서 6개가 된다.

 

 

 

 

 

Ex02 예제


 

public class Person implements Comparable<Person> {
    private static int lastNo = 0;
    private int no;
    private String name;
    private int age;
    private double height;

    public Person(String name, int age, double height) {
        this.no = ++lastNo;
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public int getNo() { return no; }
    public String getName() { return name; }
    public int getAge() { return age; }
    public double getHeight() { return height; }

    @Override
    public int compareTo(Person p) {
        return this.getName().compareTo(p.getName());
    }

    @Override
    public String toString() {
        return "Person{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}

 

  • comparable을 구현 하므로, 자기자신과 비교가 가능하고 compareTo로 이름에 따라 비교가 가능한 클래스.

 

public class PersonComp implements Comparator<Person> {
    public enum SortBy { NO, NAME, AGE, HEIGHT }
    public enum SortDir { ASC, DESC }

    private SortBy sortBy;
    private SortDir sortDir;

    public PersonComp(SortBy sortBy, SortDir sortDir) {
        this.sortBy = sortBy;
        this.sortDir = sortDir;
    }

    @Override
    public int compare(Person o1, Person o2) {
        int result = 0;
        switch (sortBy) {
            case NO: result = o1.getNo() > o2.getNo() ? 1 : -1; break;
            case NAME: result = o1.getName().compareTo(o2.getName()); break;
            case AGE: result = o1.getAge() > o2.getAge() ? 1 : -1; break;
            case HEIGHT: result = o1.getHeight() > o2.getHeight() ? 1 : -1; break;
        }
        return result * (sortDir == SortDir.ASC ? 1 : -1);
    }
}
  • Person 클래스에 정렬 기준을 추가해서 생성자로부터 받은 필드에 맞게 정렬하는 클래스

 

 

				TreeSet[] treeSets = {
                new TreeSet<>(),
                new TreeSet<>(new PersonComp(PersonComp.SortBy.NO, PersonComp.SortDir.DESC)),
                new TreeSet<>(new PersonComp(PersonComp.SortBy.AGE, PersonComp.SortDir.ASC)),
                new TreeSet<>(new PersonComp(PersonComp.SortBy.HEIGHT, PersonComp.SortDir.DESC))
        };

        for (Person p : new Person[] {
                new Person("홍길동", 20, 174.5),
                new Person("전우치", 28, 170.2),
                new Person("임꺽정", 24, 183.7),
                new Person("황대장", 32, 168.8),
                new Person("붉은매", 18, 174.1),
        }) {
            for (TreeSet ts: treeSets) {
                ts.add(p);
            }
        }

        for (TreeSet ts: treeSets) {
            for (Object p : ts) {
                System.out.println(p);
            }
            System.out.println("\n- - - - -\n");
        }
  • 맨처음은 아무것도 Comparator가 없어서 가나다순
  • 2번째는 NO를 내림차순으로 정렬
  • 3번째는 나이를 오름차순
  • 4번째는 키로 내림차순

 

 

 

 

 

 

 

 

 

 

출처 

 

제대로 파는 자바 (Java) - by 얄코 강의 | 얄팍한 코딩사전 - 인프런

얄팍한 코딩사전 | 적당히 배워서는 살아남을 수 없는 시대. 자바, 한 번에 제대로 파서 마스터하세요!, 자바(Java), 생기초부터 활용까지 강의 하나로 한번에 끝!  🎓 대학교 이메일 계정이 있다

www.inflearn.com