You will be fine

<Java> 그룹으로 묶어서 정렬하기

by BFine
반응형

 

가.  개요

 a.  생각보다 잘안됨..

  -  하다보면 어플리케이션 내에서 그룹정렬해야하는 경우가 있다. 간단할것 같지만 은근 잘되지않아서 포스팅하려고 한다.! 

 

 b.  조건

  1. 예약자명으로 그룹화 되어있어야한다.

  2. 예약날짜가 빠른 순으로 정렬되어야한다.

  3. 예약날짜가 같은 경우 예약자명 순으로 정렬된다.

  4. 그룹내에서는 금액 인원수로 정렬되어야한다. (날짜가 같은 경우)

 

 C.  Reservation 클래스

import java.time.LocalDate;

public record Reservation(LocalDate reserveDate,
                          String reserveName,
                          long price,
                          int people) implements Comparable<Reservation>  {

    public String concatDateAndName() {
        return reserveDate.toString() + reserveName;
    }

    @Override
    public int compareTo(Reservation o) {
        if (this.reserveDate != o.reserveDate) {
           return reserveDate.compareTo(o.reserveDate);
        }

        if (this.price != o.price) {
            return Long.compare(this.price, o.price);
        }

        return Integer.compare(this.people, o.people);
    }
}

/*
@Getter
@RequiredArgsConstructor
public class Reservation {
    private final LocalDate reserveDate;
    private final String reserveName;
    private final long price;
    private final int night;
}*/

  -  인텔리제이에서 워닝표시가 보이길래 봤더니 jdk14 부터 record란 키워드가 추가 되어서 kotlin의 data class 처럼 사용할 수 있어서 한번 써봤다! 

 

나.  구현하기

 a.  테스트 먼저 만들기

public class GroupingReservationSortTest {

    private static final LocalDate _23_02_01 = LocalDate.of(2023, 2, 1);
    private static final LocalDate _23_02_02 = LocalDate.of(2023, 2, 2);
    private static final LocalDate _23_02_03 = LocalDate.of(2023, 2, 3);
    private static final LocalDate _23_02_04 = LocalDate.of(2023, 2, 4);
    private static final LocalDate _23_02_05 = LocalDate.of(2023, 2, 5);
    private static final LocalDate _23_02_06 = LocalDate.of(2023, 2, 6);
    private static final LocalDate _23_02_07 = LocalDate.of(2023, 2, 7);
    private static final LocalDate _23_02_08 = LocalDate.of(2023, 2, 8);
    private static final LocalDate _23_02_09 = LocalDate.of(2023, 2, 9);


    @Test
    void groupingTest() {

        //given
        List<Reservation> reservations = getReservations();

        //when
        
        
        //then
        assertThat(reservations)
            .extracting("reserveDate", "reserveName", "price", "people")
            .containsExactly(
                tuple(_23_02_01, "kim", 30000L, 3),
                tuple(_23_02_05, "kim", 20000L, 2),
                tuple(_23_02_01, "lee", 15000L, 1),
                tuple(_23_02_07, "lee", 10000L, 1),
                tuple(_23_02_01, "park", 10000L, 1),
                tuple(_23_02_02, "park", 20000L, 2)
            );
    }

    private static List<Reservation> getReservations() {
        return new ArrayList<>(
            List.of(
                new Reservation(_23_02_05, "kim", 20000, 2),
                new Reservation(_23_02_01, "kim", 30000, 3),

                new Reservation(_23_02_01, "park", 10000, 1),
                new Reservation(_23_02_02, "park", 20000, 2),

                new Reservation(_23_02_07, "lee", 10000, 1),
                new Reservation(_23_02_01, "lee", 15000, 1)
            )
        );
    }
}

 

 b.  Collectors.mapping 을 활용해서 그룹정렬하기

@DisplayName("stream 정렬")
@Test
void groupingTest() {

    //given
    List<Reservation> reservations = getReservations();

    //when
    reservations.sort(Comparator.comparing(Reservation::concatDateAndName));

    Map<String, List<Reservation>> collects =  reservations.stream()
        .collect(Collectors.groupingBy(Reservation::reserveName, LinkedHashMap::new,
                    Collectors.mapping(i -> i,
                                        Collectors.collectingAndThen(Collectors.toList(),
                                            reservations -> reservations.stream()
                                                .sorted()
                                                .collect(Collectors.toList())
                                        ))
            )
        );

    reservations = collects.values()
        .stream().flatMap(Collection::stream)
        .toList();

    //then
    assertThat(reservations)
        .extracting("reserveDate", "reserveName", "price", "people")
        .containsExactly(
            tuple(_23_02_01, "kim", 30000L, 3),
            tuple(_23_02_05, "kim", 20000L, 2),
            tuple(_23_02_01, "lee", 15000L, 1),
            tuple(_23_02_07, "lee", 10000L, 1),
            tuple(_23_02_01, "park", 10000L, 1),
            tuple(_23_02_02, "park", 20000L, 2)
        );
}

  -  더 좋은 방법이 있을 것 같지만 떠오르지 않는다.. (마음에 안든다)

  1. 이 로직은 처음 정렬을 통해서 최상단에 위치해야하는 내역을 먼저 찾는다.

     => 날짜가 같으면 이름순으로 정렬되어야하니 예약일자+예약자명을 만들어서 정렬했다

  2. 그리고 정렬한 순서대로 그룹핑을 하고 그룹내부에서 다시 정렬을 해주었다.

     => 여기서 LinkedHashMap 이 1번에서 정렬한 Key(예약자명) 순서를 유지해 준다.

반응형

'공부 > Java' 카테고리의 다른 글

<Java> CompletableFuture  (0) 2022.08.02
<Java> Future  (0) 2022.05.06
<Java> Thread 와 비동기  (0) 2022.01.20
ForkJoinPool의 Thread Size 고정이 안되는 경우  (3) 2021.02.26
<모던자바인액션> Stream API, JMH, Parallel Stream  (0) 2021.02.21

블로그의 정보

57개월 BackEnd

BFine

활동하기