뭉
노재능 록리형 개발자
뭉
전체 방문자
오늘
어제
  • 분류 전체보기 (27)
    • Java (18)
      • Grammer (14)
      • Problem Solving (4)
    • JavaScript (0)
      • Grammer (0)
      • jQuery (0)
    • Spring (0)
    • DB (9)
      • SQL (6)
      • JPA (3)
    • Storage (0)
    • ETC (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
뭉

노재능 록리형 개발자

객체의 중복/정렬 그리고 Collection
Java/Grammer

객체의 중복/정렬 그리고 Collection

2022. 4. 24. 03:33

Collection Framwork에 속하는 자료구조에 기본형이 아닌 객체를 넣는다면 어떻게 될까요?

넣으면 넣는거지 무엇이 문제인가 생각하시는 분들도 계실겁니다.

예를 들어 TreeSet에 기본형이 아닌 우리가 만든 객체를 넣는다 가정해봅시다.

객체의 중복이 제거되고 정렬이 되겠죠?

(해당 사실이 이해가 가질 않는다면 [Java/Grammer] - Simple Data Structure (2) - List/Set/Map 참조)

하지만 객체가 같다는 것을 어떻게 알고, 무엇을 기준으로 정렬을 할지 TreeSet이 어떻게 알수 있을까요?

 

객체의 중복 기준 설정

두 객체의 중복 기준을 세우려면 hashCode와 equals 메서드를 오버라이딩해야 합니다.

왜냐하면 Collection Framework는 중복을 hashCode값이 같은지 비교하고 그 다음 equals값이 true인지 확인하여 판단하기 때문입니다. 

예를 들어 Person이라는 객체를 만들고 멤버변수로 age와 name을 가진다고 가정해 봅시다.

public class Person{
    private String name;
    private int age;

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

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

    public static void main(String[] args) {
        Person person1 = new Person("jax", 20);
        Person person2 = new Person("jax", 20);
        Person person3 = new Person("lulu", 19);
        Set<Person> datas = new HashSet<>();

        datas.add(person1);
        datas.add(person2);
        datas.add(person3);

        System.out.println(datas);   //[jax, lulu, jax]
    }
}

같은 데이터를 집어넣어도 중복이 제거되지 않습니다. 정확히는 중복 기준이 정해지지 않았다는게 맞겠지요.

 

자 그럼 중복 hashCode와 equals 메서드를 오버라이딩해 봅시다.

public class Person{
    ...

    @Override
    public int hashCode() {
        return (name + age).hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        Person tmp = (Person)obj;
        return (name.equals(tmp.name)) && (age == tmp.age);
    }

    public static void main(String[] args) {
        Person person1 = new Person("jax", 20);
        Person person2 = new Person("jax", 20);
        Person person3 = new Person("lulu", 19);
        Set<Person> datas = new HashSet<>();

        datas.add(person1);
        datas.add(person2);
        datas.add(person3);

        System.out.println(datas);    //[jax, lulu]
    }
}

마지막 결과값을 보시면 중복이 제거된 것을 아실수 있습니다.

이는 Set뿐만 아니라 Collection Framework 모두에도 적용된다는 사실을 주의하셔야 합니다.

 

객체 자연순서 정렬기준 정의

원시타입이나 문자열의 경우 크거나 작은 기준이 이미 정해져 있으며 문자열의 경우도 알파벳 순서와 같이 정렬 기준이 정해져 있습니다. 하지만 우리가 만든 객체는 그 기준을 우리가 정해줘야 정렬도 할수 있습니다.

그 기준은 Comparable Interface를 구현하여 정할 수 있습니다.

 

위에서 사용한 Person 클래스를 재활용하여 Comparable Interface를 구현해보겠습니다.

public class Person implements Comparable<Person>{
    ...

    @Override
    public int compareTo(Person o) {
        return age - o.age;
    }

    public static void main(String[] args) {
        Person person1 = new Person("jax", 1000);
        Person person2 = new Person("shen", 500);
        Person person3 = new Person("lulu", 20);
        SortedSet<Person> datas = new TreeSet<>();        

        datas.add(person1);
        datas.add(person2);
        datas.add(person3);

        System.out.println(datas);    //[lulu, shen, jax]
    }
}

Comparable Interface를 상속받으면 compareTo 메서드를 구현하면 됩니다.

이때 값이 양수면 왼쪽 값이 더 크다는 의미로 오름차순에서 나중을 차지하게 됩니다. 이와 마찬가지로 동등일 때는 0이고 음수일 때는 이와 반대입니다.

아 그리고 TreeSet에 데이터를 넣으면 자동으로 정렬과 중복제거가 되는 부분은 아시죠?

혹시 모르겠다면 Simple Data Structure (2) - List/Set/Map를 참고하시길 바랍니다.

 

우리가 정렬에 사용하는 메서드인 Arrays.sort나 Collections.sort 모두 compareTo를 사용하여 정렬하는 것입니다.

이에 따라 Integer과 String 클래스에는 이미 compareTo 메서드가 구현되어 있으며 이들모두 기본적인 정렬을 자연순서인 오름차순으로 정렬하게끔 구현되어 있습니다.

따라서 우리가 정의한 사용자 객체들도 모두 오름차순과 같은 자연순서로 정렬기준을 정하는 것이 좋습니다.

 

사용자 정렬기준 만들기

Comparator는 comparator과 compare메서드를 구현해야하며 이는 인자값을 두개 받아 두 인자를 비교하는 메서드입니다.

이 둘의 차이점은 객체 본인과 비교하지 않고 두 인자를 비교하므로 새로운 클래스에서 사용자 정렬기준을 만들수 있다는 점입니다. 또한 Arrays.sort나 Collections.sort 둘 모두 두번째 파라미터로 Comparator를 구현한 클래스를 정렬기준을 줄수 있습니다.

 

예를 들어 아까 만든 Person 클래스의 나이을 내림차순으로 정렬하는 기준을 만들고 싶다 가정합니다.

class PersonAgeDesc implements Comparator<Person>{
    @Override
    public int compare(Person o1, Person o2) {
        return (o1.getAge() - o2.getAge()) * -1;
    }
}

public static void main(String[] args) {
    Person person1 = new Person("jax", 1000);
    Person person2 = new Person("shen", 500);
    Person person3 = new Person("lulu", 20);
    Person[] datas = {person1, person2, person3};

    Arrays.sort(datas, new PersonAgeDesc());
    System.out.println(Arrays.toString(datas));   //[jax, shen, lulu]

}

다음과 같이 내림차순으로 정렬하는 새로운 정렬기준을 만들어 정렬시킬 수 있습니다.

이는 객체 본인을 비교하는 것이 아니기 때문에 외부에서 만들고 이에 따라 익명 클래스로 만들수도 있습니다.

더 나아가 람다식으로 좀더 쉽게 compare 메서드를 구현하는 방법이 있습니다만 이는 해당 주제가 목적이 아니니 패스하겠습니다.

 

참고로 기본형에 대한 내림차순에 대한 Comparator은 이미 Collections에 내부에 구현되어 있습니다.

Integer[] me = {1, 2, 3};
Arrays.sort(me, Collections.reverseOrder());
System.out.println(Arrays.toString(me));    //[3, 2, 1]

List<Integer> ma = new ArrayList<>(Arrays.asList(1, 2, 3));
Collections.sort(ma, Collections.reverseOrder());
System.out.println(ma);       //[3, 2, 1]

이때 Collections.reverseOrder메서드 사용 시 Integer만 사용가능하고 int는 불가능합니다. 또한 Integer 뿐 아니라 String도 해당 메서드를 사용하면 내림차순으로 정렬할 수 있습니다.

'Java > Grammer' 카테고리의 다른 글

입출력 스트림 (1) - System.out.println와 I/O  (0) 2022.05.22
ShallowCopy와 DeepCopy 완전 정복  (0) 2022.05.07
Simple Data Structure (2) - List/Set/Map  (0) 2022.04.23
Simple Data Structure (1) - 공통 메서드  (0) 2022.04.23
String Class (3) - 정규표현식  (0) 2022.04.02
    'Java/Grammer' 카테고리의 다른 글
    • 입출력 스트림 (1) - System.out.println와 I/O
    • ShallowCopy와 DeepCopy 완전 정복
    • Simple Data Structure (2) - List/Set/Map
    • Simple Data Structure (1) - 공통 메서드
    뭉
    뭉
    노재능 록리형 개발자

    티스토리툴바