Java에서 정렬과 lambda

roon2020·2021년 3월 31일
1

Java

목록 보기
1/2
post-thumbnail

자바에서 정렬하는 여러가지 방법을 다룹니다.
더불어 람다에 대해서도 알아봅니다.

자바7까지는..

자바7 이하에서는 리스트를 정렬하려면 Collections.sort()를,
Primitive type(int,double 같은 소문자로 시작하는 타입,즉, 참조 자료형이 아닌 타입을 말합니다.)의 배열을 정렬하려면 Arrays.sort()를 이용해서 정렬할 수 있습니다.

Collection 타입

List<Integer> numbers = Arrays.asList(2,6,1,-1,2,3,4);
Collections.sort(numbers);

역순으로 정렬하려면 reverseOrder()를 이용하면 됩니다.

Collections.sort(numbers,Collections.reverseOrder());

Primitive 타입

int[] numbers2 = new int[] {2,6,1,-1,2,3,4};
Arrays.sort(numbers2);

역순으로 정렬하려면 primitive타입으론 안됩니다.
Integer이라는 wrapper타입이어야 역순 정렬을 할 수 있습니다.
Collection을 이용할 것이기 때문입니다.

Integer[] numbers2 = new Integer[] {2,6,1,-1,2,3,4};
Arrays.sort(numbers2,Collections.reverseOrder());

객체의 정렬

두 가지 방법이 있습니다.
1. 객체의 클래스가 Comparable을 구현해서 comparTo메서드를 구현하여 비교 기준을 정해주기
2. 익명함수를 이용하여 Comparator로 비교 기준 전달하기

예를 들어 Student 객체가 있다고 해보겠습니다.

  static class Student{
      private int mathLevel;
      private int codingLevel;

      public Student(int mathLevel,int codingLevel){
          this.mathLevel=mathLevel;
          this.codingLevel=codingLevel;
      }

      public int getCodingLevel() {
          return codingLevel;
      }

      public int getMathLevel() {
          return mathLevel;
      }

      @Override
      public String toString() {
          return "Student{" +
                  "mathLevel=" + mathLevel +
                  ", codingLevel=" + codingLevel +
                  '}';
      }
  }

Comparator를 이용하는 codingLevel을 기준으로 하는 정렬은 아래처럼 이루어질 수 있습니다.

Collections.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getCodingLevel()-o2.getCodingLevel();
            }
        });

자바8부터는..

자바 8부터 더 간단한 람다라는 문법을 지원합니다. 람다는 익명함수의 발전된 형태라고 볼 수 있습니다.
더 나아가서 메서드참조라는 것도 지원합니다.
그리고 자바8에서 default method라는 것이 새로 생겼습니다.
자바 8 이전에 인터페이스는 메서드를 선언만 하고 구현은 하면 안되었지만 인터페이스에서 default 키워드를 붙인 메서드는 몸통을 구현해도 됩니다.

자바 8의 List 인터페이스에 다음처럼 한번 감싸주는 default method가 추가되었습니다. 따라서 Collections.sort(리스트) 형식으로 호출할 필요가 없이 리스트에서 바로 메서드를 호출할 수 있게 됐습니다.

default void sort(Comparator<? super E> c){
	Collections.sort(this,c);
}

람다

람다는 2가지 스타일의 문법이 있습니다.

  1. (parameters) -> expression
  2. (parameters) -> {statements;}

컴파일러같은 과목을 경험했다면 expression,statements 등의 단어가 익숙하실 겁니다.

먼저 예시부터 들면 람다를 사용하여 정수 리스트를 정렬하는 방식은 아래처럼 합니다.

numbers.sort((Integer number1,Integer number2)->
{return number1-number2;});

람다는 함수형 인터페이스(Functional Interface)에만 쓰일 수 있습니다.
함수형 인터페이스란 오직 하나의 추상 메서드만 가지는 인터페이스를 말합니다. 그래서 람다는 꽤 똑똑하게 타입 추론을 할 수 있습니다.
즉, 인터페이스의 유일한 추상 메서드를 보고 타입을 추론할 수 있기때문에 람다표현식에 타입을 명시해 주지 않아도 됩니다.
그래서 위 코드는 아래처럼 쓸 수 있습니다.

numbers.sort((num1,num2)->num1-num2);
또는
numbers.sort((num1,num2)->Integer.compare(num1,num2));

가독성이 더 높아졌네요.
다른 예시로 Student 리스트를 정렬한다고 하면 아래처럼 할 수 있습니다.

students.sort((s1,s2)->s1.getCodingLevel()-s2.getCodingLevel());

람다식이 타입을 추론하는 방법은 다음과 같습니다.

  1. 람다가 사용되는 context 확인 (함수형 인터페이스의 추상 메서드의 리턴타입,파라미터들을 확인)
  2. 그 메서드의 시그니처를 통해 함수 디스크립터를 얻습니다.

메서드 참조

어찌됐든 함수형 인터페이스의 유일한 추상메서드에 충분한 정보만 주면 될 것입니다. 메서드 참조는 이러한 메서드의 정의를 제공해서 람다와 동일한 기능을 수행합니다.
문법은 메서드 이름 앞에 :: 라는 구분자를 붙여서 메서드 참조를 할 수 있습니다.

import static java.util.Comparator.comparing;

students.sort(comparing(Student::getCodingLevel));

Integer 등 Wrapper객체의 리스트인 경우 이렇게 정렬 할 수도 있습니다.

numbers.sort(Integer::compare);

마치며

메서드 참조,람다에 대한 더 자세한 내용은 책이나 docs.oracle 을 참조하세요.
특히 캡처링,메서드 참조의 3가지 유형,제약 사항,람다의 다양한 코드를 찾아보시면 이 글의 모자란 부분을 채울 수 있습니다.

자바8이 2014년도에 나왔지만 람다나 스트림같은 기능을 사용하지 않는 분들이 매우 많은 것 같습니다. 사실 멀티 코어,병렬 처리를 위한 기능들이 대다수라 자바 8 이전 방법을 사용해도 간단한 코딩을 하는데는 무리가 없다고는 생각합니다. 하지만 더 간결한 코드를 작성하고 큰 시스템을 다루는 일을 하게 될거라면 기본적으로 알아야 하는 것이 됐습니다.
저도 2021년이 되서야 처음 알았지만.. 써보니 대단한 것이란 생각이 드네요.

profile
keep in positive mindset. I've got this.

0개의 댓글