https://school.programmers.co.kr/learn/courses/30/lessons/42578
우선 HashMap 을 이용해 의상의 종류를 key로 해당 종류의 의상의 개수를 value 로 만든다. 주어진 2차원 String 배열을 iterate 하며 HashMap 을 업데이트 한다. 그 다음에, 입을 수 있는 의상의 조합을 계산하는데, 이 때 벗은 경우의 수도 염두에 둔다.
import java.util.ArrayList;
import java.util.HashMap;
/*
https://school.programmers.co.kr/learn/courses/30/lessons/42578
*/
class Solution {
public int solution(String[][] clothes)
{
HashMap<String, Integer> clothesMap = new HashMap<>();
// 맵에 값 추가
for (String[] cloth : clothes)
{
clothesMap.merge(cloth[1],1,Integer::sum);
}
int answer = 1;
for (String key : clothesMap.keySet())
{
// 벗은 경우도 추가
answer *= ( clothesMap.get(key) + 1);
}
// 다 벗은 경우 제외
answer -= 1;
return answer;
}
}
코드 통과는 했지만, 더 간략하거나, 더 효과적이게 코드를 짠 결과들을 보고 어떤 것을 배우면 좋을지 분석해봤다.
Arrays.stream(clothes)
.collect(groupingBy(p -> p[1], mapping(p -> p[0], counting())))
.values()
.stream()
.collect(reducing(1L, (x, y) -> x * (y + 1))).intValue() - 1;
위 코드는 내 코드보다 훨씬 간결하다. 사실 근데 아직 stream에 익숙치 않아 line by line 어떤 역할을 하는지 한 번 분석해 보려고 한다.
groupingBy(classifier, downStream)
첫번째 파라미터는 classifer - 분류 기준이다. 여기서는 p -> p[1] 즉 의상의 종류가 된다. 두번째 파라미터는 downStream - 집계 방식인데 분류 기준에 의해 분류된 결과값들에 다른 기준의 집계방식을 더한다고 생각하면 된다. classifier 기준으로 Key를 분류하여, Map<Key,Value> 를 리턴한다. counting()
이 파라미터로 들어가게 되면 그룹 안에 오브젝트가 몇 개 있는지를 준다. 따라서, 위의 코드에서는 의상 종류 (classifier - key) 당 몇 개의 값이 있는지를 저장하게 된다.
reducing()
최종연산 으로, 스트림의 원소를 줄여나가면서 처음 두 원소를 가지고 연산한 결과로 그 다음 원소와 연산한다.
collect()
최종연산 으로 연산된 결과값을 원하는 자료형으로 수집하여 반환한다. 동일한 데이터를 List 나 Set 자료형으로 변환하는데에도 쓰인다.
요약하자면, 우선 의상 종류를 key 로 하고, 해당 종류의 대한 count 값을 value 로 하는 map 을 만든다. 그리고 해당 map 의 value 를 다시 스트림으로 만들어, reduce 연산을 통해 조합 연산을 한다. 그리고 최종 값에 - 1을 하여 전부 벗은 상태 (예외 상태)를 처리한다.
merge(key,1,Integer::sum)
: 이중배열을 iterate 하면서 각 의상 종류가 몇 개 있는 업데이트할 때, contains 여부 확인 후 없다면 put, 있다면 replace를 썼는데, 그것보다 위 함수가 더 좋은 선택이었을 것 같다. 위 함수는 key 가 있는지를 검사하고, 없다면 1을 value로 세팅, 있다면 value +1을 한다고 한다.