안녕하세요. 이번 시간에는 java stream의 filter에 대해 알아봅시다.

 


 먼저, filter는 stream을 리턴하는데요. predicate에 match가 되는 원소들로 이루어진 무언가를 리턴합니다. 조건에 매치되는 필터를 적용한 무언가를 리턴한다. 정도로 생각하시면 편할 듯 싶어요. 예제를 하나씩 보겠습니다.

 

 

 7번째 줄부터 11번째 줄까지 봅시다. 보시면, mapToInt로 들어오는 stream으로부터, valueOf를 적용하는 Intstream을 리턴해요. 다음에, 필터를 태우는데요. k -> k%2 != 0인 걸로 보아서는 홀수만 뽑아오겠다는 필터임을 알 수 있어요. 그리고 boxed는 박싱을 하는 stream을 리턴해요. 최종 연산으로 toList를 호출하는데요. 결과를 List로 뽑아옵니다.

 

 

 결과는 위와 같습니다.

 

 다음 예제를 봅시다. mapToInt로, valueOf를 적용하는 스트림을 리턴합니다. 그 다음에 필터를 2개 적용하는데요. 하나는 2로 나눴을 때 나머지가 0이 아닌 것을 뽑아옵니다. 그 다음에 남은 것 중 3으로 나누었을 때 나머지가 0이 아닌 것을 뽑아와요. 즉, 2로 나누어 떨어지지 않으면서, 3으로도 떨어지지 않는 것을 뽑아와요. 이걸 다시 boxing을 하고, List로 결과를 내보냅니다.

 

 

 필터가 순차적으로 적용되었으므로, and 효과를 낸 셈입니다. 2도 결과에 포함되지 않았고 3도 결과에 포함되지 않았습니다. 복잡한 조건을 적용해야 하는 경우에는 어떨까요? 조건에 따라 동적으로 적용해야 하는 경우도 적지 않을 겁니다. 예를 들자면, and, or의 복합 조건문을 써야 한다던지 등등. 이럴 때 어떻게 하면 좋을까요?

 


 java.util.function의 Predicate를 이용해 봅시다.

 

 Predicate는 각각의 조건들을 명시합니다. k -> k%2 != 0은 k가 2로 나누어 떨어지지 않는다는 것을 의미합니다. 이와 비슷하게 해석하면, k -> k%3 != 0은 k가 3으로 떨어지지 않는다는 것이겠네요. clause_3은 k가 0보다 크다는 조건이고요. clause_4는 k가 4보다 작다는 조건입니다.

 

 수가 2와 3으로 나누어 떨어지지 않거나, 0보다 크고 4보다 작은 경우에 결과를 취하도록 하고 싶어요. 어떻게 해야 할까요? 요약하면, (조건 1 and 조건 2) or (조건 3 and 조건 4)인 셈인데요.

 

 Predicate끼리 and나 or로 연결하거나 자기 자신에게 negate를 할 수 있는 것을 이용하면 됩니다. 14번째 줄은 조건 1 and 조건 2라는 의미이고요. 15번째 줄은 조건 3 and 조건 4를 의미합니다.

 

 

 20번째 줄은 무엇과 같나요? clause_1에 clause_1.and(clause_2)를 대입했으므로, 조건 1이면서 조건 2다. 라는 것을 의미합니다. clause_3에는 clause_3.and(clause_4)를 대입했으니, 조건 3이면서 조건 4임을 의미합니다. 어찌 되었던 Predicate 끼리는 연결을 계속 할 수 있습니다.

 

 즉 20번째 줄은 결국, 2와 3으로 떨어지지 않거나, 0보다 크고 4보다 작은 경우만 취하겠다는 의미입니다.

 

 정말 그런 것들만 취해졌나요? 그렇네요. 2와 3은 2와 3으로 떨어지지 않는다는 조건을 만족하지 않지만, 0보다 크고 4보다 작은 조건을 만족하므로, 결과에 나오게 됩니다. 그리고, 5와 13은 2와 3으로 떨어지지 않으므로, 결과에 나타나게 됩니다.

 

 

 그러면 이건 왜 안 될까요? mapToInt는 어떠한 연산을 적용한 결과들을 applying한 IntStream을 리턴합니다. 필터에 걸려있는 절들은 int가 아니라 Integer에 대한 것입니다. int와 Integer는 엄연히 다른 형태이기 때문에 오류를 뱉습니다. 따라서, boxed()를 통해, boxing을 적용한 스트림에 필터를 걸어줘야 합니다.