PostIT

[Java/Comparator/Comparable] 자바 객체 비교를 위한 인터페이스 분석 본문

Java

[Java/Comparator/Comparable] 자바 객체 비교를 위한 인터페이스 분석

shun10114 2017. 6. 3. 00:14

# [Java/Comparator/Comparable] 자바 객체 비교를 위한 인터페이스 분석


작성일 : 2017년 6월 3일

최종 수정일 :  2017년 7월 23일


## 1. 배경

  1. Java로 알고리즘을 풀면서 Java 또한 Sort가 Quick Sort로 구현되었다는 글을 보았습니다. 이를 테스트 하기 위해 Quick Sort를 구현한 클레스와 자바에서 제공하는 API Sort 클래스를 사용하여 어떤 것이 더 빠른지 비교하고 싶었습니다.

  2. 웹 개발을 하면 DB의 정렬을 통해 Sort를 직접 쓸일은 생각보다 많지 않다고 생각합니다. 하지만, 최근 API를 이용해서 객체에 API 값을 매핑하는 서비스를 만들었는데, Sort를 서버 내에서 정렬하는 것이 필요하다는 생각을 갖게 만들었습니다. 이를 위해서 알아본 결과 Comparator & Comparable 인터페이스를 제공해주어 쉽게 해결할 수 있을 것으로 판단했습니다.

## 2. 내용

  1. Java에서 제공되는 인터페이스를 잘 사용하면, 알고리즘을 직접 구현할 필요없이 알고리즘을 쉽게 이용할 수 있습니다. 특히, Sort관련 알고리즘이 그러한데, 그 중 Comparator와 Comparable을 이용하면 객체를 비교하여 정렬 할 수 있습니다.
    일반적인 자연율(일반적인 정렬 순서)이 아니라 사용자가 새롭게 정렬 순서를 정의하고 싶을 때 사용할 수 있습니다.

  2. Object 안에 객체의 값의 기준을 재정의하여 원하는 값을 기준으로 정렬 하는데 사용할 수 있습니다.


이제 소스 코드를 보겠습니다.


1. Arrays Util 사용하여 JAVA API 에서 Comparable interface 확인하기.

import java.util.Arrays;


public class ComparatorMain1 {

    public static void main(String[] agrs){

        String[] students = new String[]{"Ave","Steve","Jane","David","Shaw","Rooney","Ronaldo","Trial",};


        Arrays.sort(students);

        for (String student: students) {

            System.out.print(student+" ");

        }

        //Ave David Jane Ronaldo Rooney Shaw Steve Trial 

    }

}


자바에서 제공하는 Arrats util에서 Sort 메소드들을 다양하게 제공해줍니다. 여기서 String 배열은 Object로 판단하여 Object[] sort 메소드로 정렬을 수행합니다. 이 코드를 자세히보면 Comparable 인터페이스가 구현된 것을 확인 하실 수 있습니다. 그리고, Object를 통해 자바에서 사용하는 소트는 머지소트를 주로 사용하는 것으로 확인됩니다.

public static void sort(Object[] a) {

if (LegacyMergeSort.userRequested)

legacyMergeSort(a);

else

ComparableTimSort.sort(a, 0, a.length, null, 0, 0);

}


2. Comparable을 사용하여 Object 안의 객체 값 비교/정렬하기

public class Students {

    private int no;

    private String name;


    //getter

    //setter

    //toString


    public Students(){  }

    

    public Students(int no, String name){

        this.no = no;

        this.name = name;

    }

}

------------------------------------------------------------------------

import java.util.Arrays;


public class ComparatorMain2 {

    public static void main(String[] agrs){

        String[] students = new String[]{"Ave","Steve","Jane","David","Shaw","Rooney","Ronaldo","Trial",};

        Student[] studentsList = new Student[students.length];

        for (int i = 0; i <students.length; i++) {

            studentsList[i] = new Student((int)(Math.random()*100), students[i]);

        }


        Arrays.sort(studentsList);

        System.out.println("----- No 기준 ----");

        for (Student tempStudent: studentsList) {

            System.out.println(tempStudent.toString()+" ");

        }

    }

}


위의 코드를 실행하면 이러한 에러를 볼 수 있습니다. 어떠한 값을 기준으로 비교할지 선정할 수 없기 때문입니다. 

Exception in thread "main" java.lang.ClassCastException: comparator.Student cannot be cast to java.lang.Comparable

    at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)

    at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)

    at java.util.Arrays.sort(Arrays.java:1246)

    at comparator.ComparatorMain2.main(ComparatorMain2.java:15)


Student 클레스의 Comarable Interface를 구현하겠습니다. Generic을 사용하므로 Student 클레스를 넣어줍니다.

public class Students implements Comparable<Student> {

    private int no;

    private String name;

    private char grade;


    //getter

    //setter

    //toString

    //constructor


    @Override

    public int compareTo(Student compareStudent) {

        int compareNo = compareStudent.getNo();


        return this.no - compareNo;

    }

}


위의 코드를 다시 수행하면 이제는 정렬된 값을 받을 수 있습니다. No 값이 Random이기 때문에 순서는 다를 수 있습니다. 하지만, no를 기준으로 정렬됨을 알 수 있습니다.

----- No 기준 ----

Student{no=26, name='Shaw'} 

Student{no=46, name='Rooney'} 

Student{no=54, name='Ronaldo'} 

Student{no=70, name='Steve'} 

Student{no=83, name='Trial'} 

Student{no=89, name='Ave'} 

Student{no=89, name='David'} 

Student{no=97, name='Jane'} 


3. Comparator을 사용하여 비교자 재정의 하기

- Arrays와 Collections의 차이 비교.


Arrays

import java.util.Arrays;

import java.util.Comparator;


public class ComparatorMain2 {

    public static void main(String[] agrs){

        String[] students = new String[]{"Ave","Steve","Jane","David","Shaw","Rooney","Ronaldo","Trial",};

        Student[] studentsList = new Student[students.length];

        for (int i = 0; i <students.length; i++) {

            studentsList[i] = new Student((int)(Math.random()*100), students[i]);

        }


        Arrays.sort(studentsList);

        System.out.println("----- No 기준 ----");

        for (Student tempStudent: studentsList) {

            System.out.println(tempStudent.toString()+" ");

        }



        Arrays.sort(studentsList, new Comparator<Student>() {

            @Override

            public int compare(Student o1, Student o2) {

                return o1.getName().compareTo(o2.getName());

            }

        });

        System.out.println("----- Name 기준 ----");

        for (Student tempStudent: studentsList) {

            System.out.println(tempStudent.toString()+" ");

        }

    }


위의 클래스와의 차이점은 Comparator Interface를 필요한 부분만 재정의 해준 것입니다.

문자는 자연율을 가지고 있습니다. 이를 비교한 것으로 비교대상자가 더 크면 = -1, 같다면 = 0, 작으면 = 1을 반환시켜줍니다. 이를 비교하여 정렬을 할 수 있습니다.



- Collections

import com.example.comparator.Student;

import java.util.*;


public class ComparatorMain2 {

    public static void main(String[] agrs){

        String[] students = new String[]{"Ave","Steve","Jane","David","Shaw","Rooney","Ronaldo","Trial",};

        List<Student> studentsList = new ArrayList<>();


        for (int i = 0; i <students.length; i++) {

            studentsList.add(new Student((int)(Math.random()*100), students[i]));

        }


        Collections.sort(studentsList);

        System.out.println("----- No 기준 ----");

        for (Student tempStudent: studentsList) {

            System.out.println(tempStudent.toString());

        }



        Collections.sort(studentsList, new Comparator<Student>() {

            @Override

            public int compare(Student o1, Student o2) {

                return o1.getName().compareTo(o2.getName());

            }

        });

        System.out.println("----- Name 기준 ----");

        for (Student tempStudent: studentsList) {

            System.out.println(tempStudent.toString());

        }

    }

}


  • Effective Java의 저자나 많은 사용자, 그리고 저도 배열을 사용하라고는 권하지 않습니다. 배열은 위에 예제에서 보듯이, 프로그램이 알수있게 길이를 주어줘야 합니다. 이 말은 추가/수정/삭제가 어려워지는 확장성을 포기하게 됩니다. 또한, 제네릭타입을 이용하지 않아 해당 코드들이 타입의 의존하게되어 확장성을 또 포기하게됩니다. 그러므로, 해당 객체(Object)를 정렬할 때는 꼭 Collections를 사용해주세요



혹은 아래와 같이 객체 안에 Interface를 구현해주어 사용할 수 있습니다.


public class Student implements Comparable<Student>, Comparator<Student> {

    private int no;

    private String name;


    @Override

    public int compare(Student o1, Student o2) {

        return o1.getName().compareTo(o2.getName());

    }


    //reverse ....etc

}



## 3. 결론

  1. DB를 사용하지 않아도 되는 분야나 혹은 Java를 통해 알고리즘을 이해하는데는 Comparator, Comparable Interface를 이해하는게 큰 도움이 될 것이라고 생각합니다.

  2. Generic도 같이 이해하여 Casting되지 않게 구현되어있지만, Generic을 더 응용하여 다른 객체와 비교하게 구현할 수 있다면, 다른 객체와도 정렬이 가능하지 않을까하고 생각해봅니다. 이는 차후에 시도해보는 걸로 하겠습니다.



## 4. 참조

https://brunch.co.kr/@kd4/7#comments

Comments