java
에서는 collection
을 다루는 것이 정말 중요하다. 많이 중요해 정말.
실무에서 특히 stream
을 요즘 많이 사용하고 있는데, 약간 복잡한 요구사항을 stream
으로 간단하게 풀어낸 코드가 있어서 기록하고자 한다.
현재 최종적으로 return
되는 ReturnDto
는 Map<String, CategoryModifiedDto>
형식이다.
사실, 이 프로젝트에서는 DTO
가 매우 복잡한데, 오늘 주제가 DTO
가 아니므로 간단히 DTO
설계에 대해 설명하자면, Entity
에서 set
인 데이터를 최종 return
되는 데이터에서는 Map
으로 담아야 하고, 무한루프를 돌지 않도록 또다른 Dto
를 만들어서 뱉어내는 데이터를 잘라냈다.
이런 설계 상황에서, 데이터가 넘어갈 때, 정렬이 되지 않은 채로 넘어가게 되었는데, 이것을 데이터가 DB
에 저장된 순서대로 넘겨달라는 요구사항이 들어왔다.
DB
에 저장된 데이터를 확인해보니 모든 데이터가 일관된 Date
로 들어와 있었다.
- 초까지 똑같이 들어와있다 🤔
우리 회사에서는 데이터를 정제하여 넘겨주는 크롤링 팀이 따로 있는데, 그곳에서 아마 한번에 데이터를 수집하여 저장시키기 때문에 데이터를 저장하는 초마저 정확하게 똑같이 들어갔다.
고민 끝에 우선 크롤러 팀에게 데이터 수집이 해당 사이트의 순서대로 수집이 되어 들어간 것인지를 확인했고, 데이터 정렬 기준을 id
값으로 하기로 정했다. 데이터가 들어간 시간은 같지만, id
는 고유하고 어쨌든 데이터는 ordered
되어서 들어가는 것이므로 id
로 정렬하게 되었다.
public static LinkedHashMap<String, CategoryModifiedDto> sortMapById(Map<String, CategoryModifiedDto> map) { List<Map.Entry<String, CategoryModifiedDto>> entries = new LinkedList<>(map.entrySet()); Collections.sort(entries, (o1, o2) -> o1.getValue().getCategory_id() .compareTo(o2.getValue().getCategory_id())); LinkedHashMap<String, CategoryModifiedDto> result = new LinkedHashMap<>(); for (Map.Entry<String, CategoryModifiedDto> entry : entries) { result.put(entry.getKey(), entry.getValue()); } return result; }
- 일단 머릿속에 있는 로직의 흐름대로 코딩을 했다.
sortMapById
라는 메소드를 하나 생성하여 파라미터로 기존의 생성된Map<String, CategoryModifiedDto>
타입의 객체를 하나 받는다.- 그 후에
List<Map.Entry>>
타입의LinkedList
를 생성하고, 그곳에서getCategory_id()
를 기준으로 정렬을 해준다.- 최종적으로
return
할LinkedHashMap
객체를 생성해주고,for
루프를 통해result
인스턴스에 담아주어 메소드를 리턴한다.- 초큼...복잡해 보이지만, 머리속에 있는 로직의 흐름대로 좌르륵 만들어보았다.
- 그런데 코드를 보고있자니 너무 복잡하기도하고 리스트로 담았다가 다시 맵에 담았다가..낭비되는 부분이 많아 보인다.
stream
에서 하지 못하나..? 라는 생각이 들었고, 극한의 효율을 뽑아내기위해CategoryDto
에서stream
을 건들여보았다.
public CategoryDto(Category entity) { super(entity); children = new HashMap<>(); this.children = entity.getChildrenCategory().stream() .sorted(Comparator.comparing(Category::getId)) .collect(Collectors .toMap(Category::getCategoryName, CategoryModifiedDto::new, (x, y) -> x, LinkedHashMap::new));
this.children
은Map<String, CategoryModifiedDto>
타입이다.getChildrenCategory()
는Set<Category>
타입이다.
딱 보기만 해도 코드가 너무나 깔끔해졌다.
우선, set
을 stream()
을 통해 판을 깔고, sorted
로 정렬 순서를 정해줬다. 극한의 코드 효율을 위해 lambda
를 사용했다. (람다 간지)
그 후에 다시 이 set
을 map
으로 변환시켜줬는데, key
값으로 CategoryName
을 가져왔고, value
값으로 CategoryModifiedDto
를 생성자에 set
의 들어가 있는 Category
인스턴스들을 넣어 생성자로 초기화시켜주었다.
그 다음은 그냥 Map
으로 변환하는것이아니라 LinkedHashMap
으로 변환을 시켜주어야 하기 때문에 넣은 코드다. Map
으로 가게되면 정렬한 것이 다시 흐트러져 들어가게 된다. 그렇기 때문에, LinkedHashMap
으로 넣어주고 this.children
을 완성해주면 된다.
결론 : stream
은 짱이다. 갓갓 너무 편하다.
그럼 이만~ ! ✋