09009

[Java] compareTo(), compare() 본문

Back-End/JAVA
[Java] compareTo(), compare()
09009

객체를 정렬할 때 - Comparable 인터페이스

 

[Java] Arrays 클래스

Arrays 클래스 • 배열 조작 기능을 하는 클래스 • 배열을 다루는데 유용한 메서드가 정의되어 있다. 배열의 비교와 출력 - equals(), toString() • equals() 두 배열에 저장된 모든 요소를 비교하여 같으

haaland09009.tistory.com

위의 게시글에서 확인할 수 있듯이 Arrays.sort()를 호출만 하면 컴퓨터가 알아서 배열을 정렬하는 것처럼 보이지만

사실은 Character 클래스의 Comparable의 구현에 의해 정렬된 것이다.

Comparator와 Comparable은 모두 인터페이스로 컬렉션을 정렬하는 데 필요한 메서드를 정의한다.

 

- Comparator와 Comparable의 실제 소스

    public interface Comparator {
        int compare(Object o1, Object o2);
        boolean equals(Object obj);
    }
    public interface Comparable {
        public int compareTo(Object o);
    }

Comparable : 기존 정렬기준을 구현하는데 사용. Comparable을 구현한 클래스들이 기본적으로 오름차순으로 정렬되어 있다.

Comparator : 내림차순으로 정렬하던지 아니면 다른 기준에 의해 정렬하고 싶을 경우 Comparator를 구현

 

  정렬의 기준을 정하는 인터페이스

 

 Java에서 기본 자료형은 부등호를 통해서 서로 쉽게 비교할 수 있다.

int x = 3, y = 2;
System.out.println(x > y); // true
System.out.println(x == y); // false
System.out.println(x < y); //false

 

사람의 이름과 나이 정보를 갖고 있는 클래스가 있다고 가정해 보자.

이러한 클래스의 경우 부등호를 통해 크고 작음을 비교할 수 있을까? 

이와 같은 클래스는 이름의 대소를 비교할 것인지, 나이의 대소를 비교할 것인지

즉, 비교 기준이 명확하게 정해져 있지 않기 때문에 정확한 비교를 할 수 없다.

 

그래서 우리는 Comparable 또는 Comparator 인터페이스를 이용하여 비교 기준을 직접 정한 후 객체를 비교할 수 있게 하여야 한다.

 


• CompareTo() 메서드

- Comparable 인터페이스에 정의된 메서드 중 하나

- 두 개의 값, 객체를 비교하여 int값으로 반환하는 함수

 

- 숫자 비교: 크다(1), 같다(0), 작다(-1)와 같은 결괏값 반환

- 문자열 비교:같다(0), 그 외 양수/음수값과 같은 결과를 반환

 

•  a.compareTo(b) 메서드

- a와 b가 같은 순서면 0

- a > b 이면 0보다 큰 값 반환

- a < b 이면 0보다 작은 값 반환

 

위 메서드를 수행하고 결과 값이 0이면 같은 순서. 0보다 크면 a > b, 0보다 작으면 b > a로 순서가 정해진다.
새 클래스를 만들고 그 클래스의 객체들 사이에 순서를 정해주고 싶다면 
Comparable 인터페이스를 구현하고 compareTo 메서드를 위 규칙대로 정의하여 순서를 정해줄 수 있다.

 

!!! Comparable은 인터페이스이므로  Comparable을 이용하여 객체를 정렬하려면 비교하고자 하는 객체에서

아래에 작성된 소스코드와 같이 Comparable 인터페이스를 구현해야 한다.!!!

 

✍ Member1.java

package ch11;
// 객체를 정렬하기 위해서는 comparable 을 구현해야 한다.
public class Member1 implements Comparable<Member1>  {
	String name;
	public Member1(String name) {
		this.name = name;
	}

// 정렬할 기준을 재정의한다.
	@Override
	public int compareTo(Member1 o) {
		return name.compareTo(o.name);
	}

	@Override
	public String toString() {
		return name;
	}
}

✍ Arrays4.java

package ch11;
import java.util.Arrays;
public class Arrays4 {
	public static void main(String[] args) {
		Member1 m1 = new Member1("음바페");
		Member1 m2 = new Member1("홀란드");
		Member1 m3 = new Member1("케인");
		Member1 m4 = new Member1("비니시우스");
		
		Member1[] members = {m1, m2, m3, m4};
		Arrays.sort(members);
		System.out.println(Arrays.toString(members));
		int index = Arrays.binarySearch(members, m2);
		System.out.println("찾은 인덱스 : "  + index);
	}
}

 

💻  출력

[비니시우스, 음바페, 케인, 홀란드]
찾은 인덱스 : 3

예제

 

• 나이를 기준으로 정렬하고 싶을 때

 

✍ Member2.java

package ch11;
public class Member2 implements Comparable<Member2>{
	String name;
	int age;
	public Member2(String name, int age) {
		this.name = name; this.age =age;
	}
	// 비교하는 기준
	@Override
	public int compareTo(Member2 o) {
		// 비교 대상은 문자열이어야 한다.
		String age1 = age + ""; // 숫자 + 문자 = 문자 
		String age2 = o.age + "";
		return age1.compareTo(age2);
	}
//    @Override
//    public int compareTo(Member2 o) {
//        if (age > o.age) return 1;
//        else if (age < o.age) return -1;
//        else return 0;
//    }
	@Override
	public String toString() {
		return name + "(" + age + ")";
	}
}

✍ ArraysMember2.java

package ch11;

import java.util.Arrays;
import java.util.Collections;

public class ArraysMember2 {
	public static void main(String[] args) {
		Member2 m1 = new Member2("손흥민", 32);
		Member2 m2 = new Member2("이강인", 23);
		Member2 m3 = new Member2("백승호", 27);
		Member2 m4 = new Member2("김민재", 28);
		Member2 m5 = new Member2("김영권", 34);
		
		Member2[] members = {m1, m2, m3, m4, m5};
		System.out.println(Arrays.toString(members)); // [손흥민(32), 이강인(23), 백승호(27), 김민재(28), 김영권(34)]
		
		// 나이를 기준으로 오름차순 정렬
		Arrays.sort(members);
		System.out.println(Arrays.toString(members)); // [이강인(23), 백승호(27), 김민재(28), 손흥민(32), 김영권(34)]
		
		// 나이를 기준으로 내림차순 정렬
		Arrays.sort(members, Collections.reverseOrder());
		System.out.println(Arrays.toString(members)); // [김영권(34), 손흥민(32), 김민재(28), 백승호(27), 이강인(23)]
	
	}
}

Member2.java에 있는 compareTo() 메서드에서 나이를 기준으로 비교하도록 설정하였으므로  ArraysMember2.java에 있는 main 메서드 실행 결과 나이를 기준으로 정렬된 것을 확인할 수 있다. 

 

그렇다면 이름으로 비교하면 어떤 결과가 나올까?


• 이름을 기준으로 정렬하고 싶을 때

 

✍ Member2.java

package ch11;

public class Member2 implements Comparable<Member2>{
	String name;
	int age;
	public Member2(String name, int age) {
		this.name = name; this.age =age;
	}
	// 비교하는 기준
	@Override
	public int compareTo(Member2 o) {
		return name.compareTo(o.name);
	}
	@Override
	public String toString() {
		return name + "(" + age + ")";
	}
}

✍ ArraysMember2.java

package ch11;

import java.util.Arrays;
import java.util.Collections;

public class ArraysMember2 {
	public static void main(String[] args) {
		Member2 m1 = new Member2("손흥민", 32);
		Member2 m2 = new Member2("이강인", 23);
		Member2 m3 = new Member2("백승호", 27);
		Member2 m4 = new Member2("김민재", 28);
		Member2 m5 = new Member2("김영권", 34);
		
		Member2[] members = {m1, m2, m3, m4, m5};
		System.out.println(Arrays.toString(members)); // [손흥민(32), 이강인(23), 백승호(27), 김민재(28), 김영권(34)]
		
		// 이름을 기준으로 오름차순 정렬
		Arrays.sort(members);
		System.out.println(Arrays.toString(members)); //[김민재(28), 김영권(34), 백승호(27), 손흥민(32), 이강인(23)]
		// 이름을 기준으로 내림차순 정렬
		Arrays.sort(members, Collections.reverseOrder());
		System.out.println(Arrays.toString(members)); //[이강인(23), 손흥민(32), 백승호(27), 김영권(34), 김민재(28)]
	
	}
}

Member2.java에 있는 compareTo() 메서드에서 이름을 기준으로 비교하도록 설정하였으므로  ArraysMember2.java에 있는 main 메서드 실행 결과 이름을 기준으로 제대로 정렬된 것을 확인할 수 있다. 

 

우리는 이전에 사람의 이름과 나이 정보를 갖고 있는 클래스의 대소비교를 어떻게 할 것인지에 대한 의문이 있었다. 위의 소스코드에서 볼 수 있듯이 comparable 인터페이스를 이용하면 비교 기준을 직접 정하여 우리가 원하는 대로 비교할 수 있음을 확인하였다.


예제 

나이를  기준으로 정렬하기

✍ 입력

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

class Person implements Comparable<Person> {
    String name; int age;
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public void showData() {
        System.out.printf("%s %d\n", name, age);
    }
    @Override
    public int compareTo(Person p) { // a.compareTo(b)
        if (age > p.age) return 1;
        else if (age < p.age) return -1;
        else return 0;
    }
}
public class ComparablePerson {
    public static void main(String[] args) {
        TreeSet<Person> sTree = new TreeSet<Person>();
        sTree.add(new Person("Choi", 24));
        sTree.add(new Person("Hong", 29));
        sTree.add(new Person("Lee", 21));

        Iterator<Person> itr = sTree.iterator();
        while(itr.hasNext())
            itr.next().showData();

    }
}

💻 출력

 

Lee 21
Choi 24
Hong 29

 


나이가 같다면 이름을 기준으로 정렬하기

✍ 입력

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

class Person implements Comparable<Person> {
    String name; int age;
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public void showData() {
        System.out.printf("%s %d\n", name, age);
    }
    @Override
    public int compareTo(Person p) {
        // a.compareTo(b) 메서드 수행의 결과 값이 0이면 같은 순서. 0보다 크면 a > b, 0보다 작으면 b > a로 순서가 정해짐
        // 새 클래스를 만들고 그 클래스의 객체들 사이에 순서를 정해주고 싶다면
        // Comparable 인터페이스를 구현하고 compareTo 메서드를 위 규칙대로 정의하여 순서를 정해줄 수 있다.

        if (age > p.age) return 1; // 나이가 크면 뒷 순서
        else if (age < p.age) return -1; // 나이가 작으면 앞 순서
        else {
           return name.compareTo(p.name); // 나이가 같다면 이름의 사전순대로 순서를 정함.
            // String 클래스는 사전 순대로 크기가 정해지도록 이미 compareTo 메서드를 오버라이딩 해놨다.
        }
    }
}
public class ComparablePerson {
    public static void main(String[] args) {
        TreeSet<Person> sTree = new TreeSet<Person>();
        sTree.add(new Person("Lee", 21));
        sTree.add(new Person("Hong", 21));
        sTree.add(new Person("Choi", 21));
        sTree.add(new Person("Lee", 22));
        sTree.add(new Person("Hong", 22));
        sTree.add(new Person("Choi", 22));

        Iterator<Person> itr = sTree.iterator();
        while(itr.hasNext())
            itr.next().showData();
    }
}

💻 출력

Choi 21
Hong 21
Lee 21
Choi 22
Hong 22
Lee 22

객체를 정렬할 때 - Comparator 인터페이스

String 클래스에서 순서를 임의로 정하고 싶을 때 - Comparator 

 

사전 순이 아닌 문자열의 길이를 기준으로 정렬할 때

✍ 입력

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

class StrLenComparator implements Comparator<String> {
    public int compare(String str1, String str2) {
        if (str1.length() > str2.length()) return 1;
        else if (str1.length() < str2.length())
            return -1;
        else return 0;
    }
}

public class IntroComparator {
    public static void main(String[] args) {
        TreeSet<String> tSet = new TreeSet<String>(new StrLenComparator());
        tSet.add("Orange"); tSet.add("Apple");
        tSet.add("Dog"); tSet.add("Individual");

        Iterator<String> itr = tSet.iterator();
        while(itr.hasNext())
            System.out.println(itr.next());
    }
}

💻 출력

Dog
Apple
Orange
Individual

Comparator 인터페이스를 구현하여 compare 메서드를 문자열의 길이를 기준으로 정렬하는 의미로 재정의하였기 때문에 위와 같은 결과가 출력된다.