지금도 어렵지만, 1단계, 2단계, 3단계... 이런 식으로 설명해 보겠다.
1단계 Intro - 자바조상님은 사용자가 만든 객체에 대해서는 모르신다.
Arrays.sort() 메서드부터 이야기해보겠다. 아래의 코드를 보자
import java.util.Arrays;
public class Exercise11_7 {
public static void main(String[] args){
int[] arr1 = { 30, 50, 10, 40, 20};
Arrays.sort(arr1); //int, float 등 기본형 타입의 배열을 자바가 내부적으로 알아서 오름차순으로 정렬해준다.
System.out.println("arr1= "+Arrays.toString(arr1));
Integer[] arr2 = { new Integer(30),
new Integer(50),
new Integer(10),
new Integer(40),
new Integer(20)
};
Arrays.sort(arr2);//Integer, Chracter 등과 같은 객체자료형도 자바가 내부적으로 알아서 오름차순으로 정렬해준다.
System.out.println("arr2= "+Arrays.toString(arr2));
String[] arr3 = { "cat", "dog", "line", "tiger"};
Arrays.sort(arr3);
System.out.println("arr3= "+Arrays.toString(arr3)); //String 타입의 참조형도 자바가 내부적으로 알아서 오름차순으로 정렬해준다.
String[] arr4 = { "김가을", "이가을", "송가을", "남가을"};
Arrays.sort(arr4);
System.out.println("arr4= "+Arrays.toString(arr4)); //String 타입의 참조형인데, 한글에 대해서느 내부적으로 알아서 오름차순으로 정렬해준다.
Character[] arr5 = { '김', '이', '송', '남'};
Arrays.sort(arr5);
System.out.println("arr5= " + Arrays.toString(arr5));
}
}
정렬결과
위 코드의 결과를 요약해보자면
int, float 등과 같은 기본형,
Integer, Chracter 등과 같은 객체자료형,
String 등과 같은 참조형(객체)에 대해서는 자바조상님께서 알아서 잘 정렬을 해주신다.
그런데 자바 조상님께서 내가 만든 참조자료형(ex-Person객체,Book객체, Student객체 등)에 대해서는 모르신다.
그렇지만 나는 자바조상님에게 내가 만든 객체에 대해서 정렬을 요청 드리고 싶다.
이때 필요한 것이 "정렬기준"이며
내가 만든 객체에 대한 정렬 기준을 만들 때 필요한 수단이 Comparable 인터페이스와 Comparator인터페이스이다.
2단계 정렬 전 - 대소관계 비교부터 하자
우리가 하고 싶은 것은 정렬이다. 그런데 정렬을 하기 위해서는 대소관계 비교가 우선 되어야 한다.
초등학교 때로 돌아가보자
- 큰 - 작 >0 큰 수에서 작은 수를 뺀 경우 양수가 나오며, 왼쪽값이 오른쪽 값보다 크다 왼쪽>오른쪽
- 5 - 5 =0 동일한 크기의 수를 빼는 경우 0이 나오며, 두 값의 크기는 동일하다. 왼쪽=오른쪽
- 작 - 큰 <0 작은 수에서 큰 수를 빼는 경우 음수가 나오며, 왼쪽값은 오른쪽 값보다 작다 왼쪽 <오른쪽
오름차순을 기준으로
- 큰 - 작 >0 왼쪽이 큰 수 이므로 작은수를 큰 수의 앞쪽으로 자리바꿈한다.
- 5 - 5 =0 자리바꿈을 하지 않는다.
- 작 - 큰 <0 왼쪽이 작은 수 이므로 자리바꿈을 하지 않는다.
이러한 메커니즘은 Comparable 또는 Comparator를 구현할 때 이용된다.
3단계 Comparable의 구성과 전제조건
일단 Comparable의 인터페이스가 어떻게 구성되어 있는지 살펴보자
public interface Comparable {
int compareTo(Object obj); //객체 자신(this)와 obj를 비교
}
더불어 아래의 사실을 인지하자
- compareTo : 추상메서드이므로 구현해야 함
- CompareTo : 객체자신(this)와 매개변수로 넘어온 객체(obj)를 비교한다는 점
- CompareTo : 리턴타입이 int형이라는 점
- 사용자 정의 자료형(ex-Student객체)에 대한 정렬기준을 만들기 위해서는 해당 Student객체가 Comparable인터페이스를 implements해야 한다는 점
4단계 Comparable - 무엇을 기준으로 비교할 것인가?
class Student {
String name; //이름
int age; //나이
int classNumber; //학급
public Student(String name, int age, int classNumber) {
this.name = name;
this.age = age;
this.classNumber = classNumber;
}
}
public class Test {
public static void main(String[] args) {
Student a = new Student("김가을", 17, 2); // 17살 2반
Student b = new Student("이가을", 18, 1); // 18살 1반
/*
어떻게 비교..?
if(a > b) ..?
*/
}
}
위 코드에 대한 출처를 밝힙니다.
조상님께서는 사용자정의 자료형 Student 객체에 대해 모르시기 때문에 "정렬기준"을 주어야한다.
그런데 우리 선택할 수 있는 정렬 기준은 "name", "age", "classNumber"가 있다.
"반 번호"를 비교기준으로 선택하고 비교를 해보겠다.
class Student implements Comparable<Student> {
String name;
int age;
int classNumber;
public Student(){};
public Student(String name, int age, int classNumber) {
this.name = name;
this.age = age;
this.classNumber = classNumber;
}
public int compareTo(Student s){
return this.classNumber -s.classNumber;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", classNumber=" + classNumber +
'}';
}
}
위 코드에 대한 출처를 밝힙니다.
this.classNumber - s.classNumber를 분석해 보면
자기 자신(this)인 Student객체와 compareTo의 매개변수로 넘어온 Student객체를 비교할 것이므로
compareTo의 매개변수를 Student타입으로 한다.
Student객체와 Student객체를 비교한다. 비교대상의 타입이 Student이므로 <>에 Student를 대입한다.(나도 잘 이해 안감)
public class Test {
public static void main(String[] args) {
Student a = new Student("김가을", 17, 2); // 17살 2반
Student b = new Student("이가을", 18, 1); // 18살 1반
int isBigBan = a.compareTo(b);
if (isBigBan > 0) {
System.out.println("학생a는 학생b보다 반이 높습니다.");
} else if (isBigBan == 0) {
System.out.println("학생a와 학생b는 반이 같습니다.");
}
if (isBigBan < 0) {
System.out.println("학생a는 학생b보다 반이 낮습니다.");
}
}
}
위 코드에 대한 출처를 밝힙니다.
출력결과
지금까지 한 일은
1. Comparable인터페이스를 구현을 통한 "비교기준(classNumber)" 정하기
2. "비교기준"으로 비교해 보기(this.classNumber - s.classNumber)
말 그대로 비교만 했다.
이제 다시 정렬로 돌아가 보자
5단계 Comparable - 정렬
글이 길어지다 보니 궁극적인 목적을 잃어 버릴 수 있어서 다시 한번 언급한다.
- 자바 조상님께서 내가 만든 참조자료형(ex-Person객체,Book객체, Student객체 등)에 대해서 모르신다.
- 그렇지만 나는 자바조상님에게 사용자가 만든 객체(Student)에 대해서 정렬을 요청 드리고 싶다.
추가적으로 더 알아야 할 지식을 이야기 하면,
- 객체자료형 - Integer, Float, Chracter, Boolean (primitive type을 reference type으로 바꾼 자료형)
- 참조형 - String
- Integer, Float, Chracter과 같은 객체자료형 배열을 정렬할 때는 Arrays클래스를 사용하며,
- Person객체, Student객체 등과 같은 사용자자료형 배열"리스트"을 정렬할 때는 Collections클래스를 사용한다.
사용자 정의 자료형인 Student객체를 비교하므로 Collections클래스를 이용하겠다.
정렬기준은 "반 번호"를 여전히 사용하겠다.
여기서 뭔가 점프한 느낌이 든다.
앞서서 compareTo() 메서드를 이용한 비교기준을 정하고,
classNumber로 비교를 했다.
그런데 갑자기 Collections클래스가 나왔다.
요약해 보자면
compareTo()메서드 구현 this.classNumber - s.classNumber
Student implements Comparable<Student>
위 2가지는 Collections.sort() 메서드에 "정렬기준"을 제공하기 위함이었다.
아래의 코드를 실행해보자
import java.util.*;
public class Test {
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>(); // 17살 2반
list.add(new Student("김가을",17, 2));
list.add(new Student("이가을",18, 1));
list.add(new Student("송가을",16, 6));
list.add(new Student("남가을",19, 5));
System.out.println("list = " + list);
Collections.sort(list);
System.out.println("list =" +list);
}
}
class Student implements Comparable<Student> {
String name;
int age;
int classNumber;
public Student(){};
public Student(String name, int age, int classNumber) {
this.name = name;
this.age = age;
this.classNumber = classNumber;
}
public int compareTo(Student s){
return this.classNumber -s.classNumber;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", classNumber=" + classNumber +
'}';
}
}
출력결과
정렬기준을 제공하지 않은 경우
아래의 사항을 삭제해 보았다.
- compareTo()메서드 구현 this.classNumber - s.classNumber
- Student implements Comparable<Student>
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>(); // 17살 2반
list.add(new Student("김가을", 17, 2));
list.add(new Student("이가을", 18, 1));
list.add(new Student("송가을", 16, 6));
list.add(new Student("남가을", 19,5));
System.out.println("list = " + list);
Collections.sort(list); //정렬기준을 제공하지 않는 경우
System.out.println("list =" +list);
}
}
class Student {
int age;
int classNumber;
public Student(){};
public Student(String name, int age, int classNumber) {
String name = name;
this.age = age;
this.classNumber = classNumber;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", classNumber=" + classNumber +
'}';
}
}
"정렬기준"이 없으므로 Collections.sort()메서드는 사용자정의 객체(Student객체)에 대해 "무엇을 기준으로 정렬할 지를 몰라서" 에러가 발생한다.
궁금한 점
사실 우리는 this.classNumber - s.classNumber만 해주었다. 즉 빼기 연산만 했다. 눈을 비비고 찾아봐도
Collections.sort()메서드가 compareTo()메서드를 호출한다거나 위치를 바뀌주는 swap 함수가 나타나지 않는다. 놀랍다. 말 그대로 빼기만 했는데, 알아서 정렬을 다 해준다.
이 부분은 자바조상님께서 알아서 해주신다고 생각하자 글쓴이도 내부 소스를 찾아 보았으나 머리가 더 아프다.
※이것만 기억하자
- compareTo 메소드 구현을 통한 정렬기준제시★
- 크기비교를 위한 뺄셈연산
- 해당객체의 Comparable인터페이스 implements
6단계 Comparable - 내림차순, 이름기준 정렬(심화)
내림차순
앞서 "반 번호"를 기준으로 오름차순으로 정렬하였는데, 내림차순으로 정렬하려면 어떻게 하면 될까??
정답은
(this.classNumber - s.classNumber) * -1 하면 된다.
public int compareTo(Student s){
return (this.classNumber -s.classNumber)*-1;
}
큰 - 작>0 , A - A =0, 작 - 큰 <0 이므로 -1을 곱하면, 앞서 말한 메커니즘이 반대(reverse)로 한다.
출력결과
이름기준 정렬
사용자 자료형인 Student객체의 이름순으로 정렬을 할려면 어떻게 하면 될까??
- 일단의 Student객체에서 name 값을 얻기 위해 getName 이름의 Getter를 만든다.
- 그리고 this.getName().compareTo(s1.getName())으로 compareTo() 메서드를 구현한다.
public class Test {
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>(); // 17살 2반
list.add(new Student("김가을",17, 2));
list.add(new Student("이가을",18, 1));
list.add(new Student("송가을",16, 6));
list.add(new Student("남가을",19,5));
Collections.sort(list);
System.out.println("list = " + list);
}
}
class Student implements Comparable<Student> {
String name;
int age;
int classNumber;
public Student(){};
public Student(String name, int age, int classNumber) {
this.name = name;
this.age = age;
this.classNumber = classNumber;
}
public String getName() {
return name;
}
public int compareTo(Student s){
return this.getName().compareTo(s.getName());
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", classNumber=" + classNumber +
'}';
}
}
출력결과
궁금해 할수도 있는데
this.getName().compareTo(s1.getName())
여기에서 compareTo의 리턴타입이 int형인데,
getName() 리턴값은 String 타입이다.
그러면 name - name 이라고 생각할 수 있는데
String 타입의 리턴값이 String 클래스에서 오버라이딩된 compareTo()메서드를 호출한다. 그러면 나머지는 조상님이 알아서 해주신다.
※비교기준의 자료형이 age, classNumber 등과 int형이 아니더라도
String 타입의 비교기준(name)으로도 compareTo()메서드를 호출해서 이름정렬이 가능해진다.
-1을 곱해서 이름을 내림차순으로 정렬할 수도 있다.
public int compareTo(Student s){
int result = this.getName().compareTo(s.getName());
return result * -1;
}
출력결과
기억하자
"비교기준"을 정해서 빼주기만 하면 나머지는 조상님이 정렬해 주신다.
'자바 > Java (중요하고, 이해 안 되고, 어려운)' 카테고리의 다른 글
메서드 참조 (0) | 2022.10.30 |
---|---|
람다 Lambda (0) | 2022.10.30 |
생성자 (0) | 2022.10.26 |
객체변수와 객체 (0) | 2022.10.26 |
자바 - 익명클래스★★ (0) | 2022.10.26 |