본문 바로가기
끄적/노트

[JAVA] Stream 이해하기( filter , collect , 람다식)

by 밀키스 2022. 3. 19.

코딩테스트 문제를 좀 풀다가 이중 for문에 대해 조금 찾아보다 Stream이란걸 공부하게 됐다.

 

어렴풋이는 알고 있었는데, 오늘 조금이나마 쓸줄은 알 정도로만 이해하고 조금씩 더 공부할 생각이다.

 

 

1. 코드 

만일 내가 두개의 리스트를 비교해서 서로 똑같이 있는 값만을 걸러내 출력하거나 담고 싶다면

 

List<String> result2 = new ArrayList<String>();
	for(int i=0; i<test_list.size(); i++) {
		for(int j=0; j<test_list.size();j++) {
			if(test_list.get(i).equals(test2_list.get(j))) {
				result2.add(test_list.get(i));
			}
		}
	}

 

난 당연히 위의 코드처럼 이중 for문을 생각하겠지만 이에 대해 Stream을 이용해 간결하게 표현할 수 있다.

 

public class Main {
	public static void main(String[] args) {
List<String> test_list = Arrays.asList("a","b","c","d");
List<String> test2_list = Arrays.asList("a","2","c","4");

List<String> result = test_list.stream()
        .filter(str -> test2_list.stream().anyMatch(Predicate.isEqual(str)))
        .collect(Collectors.toList());

result.stream().forEach(System.out::println);
}}

대충 설명하자면 

  1. test_list와 test2_list 라는 두개의 리스트를 만든다.
  2. result라는 결과 값을 담을 리스트를 만든다.
  3. 결과 리스트에 스트림을 받아 filter로 걸러내고, anyMatch로 일치하는것을 찾고, 끝에는 이를 list로 반환한다.
  4. 마지막으로 이를 출력한다.

 


 

@ asList 

  --> asList는 굳이 사용할 필요는 없는 구문이지만 처음 봐서 그대로 가져왔다. asList의 특성으로는

  1. 수정은 되나 삭제, 추가의 작업은 이뤄지지 않는다. -> size(크기)를 변환할 수 없다.
  2. 위의 코드에서는 각 값을 직접 넣어주었으나, 배열을 매개로 리스트를 생성할 수도 있다.
  3. 2번과 같이 생성한 경우, 새로운 별개의 리스트를 생성한것이 아닌, 해당 배열의 주소값을 가져온다.

 

이런 3가지의 특성을 가지고 있다. 특히 3번의 경우 주소값 복사라는 부분은 다른 부분에서도 많이 보았던 부분이라 익숙하다. (Map이라던가 맵이라던가....)

 

때문에 주소값이 아닌 별개의 값을 갖는 리스트를 만들고 싶다면, 아예 새로 만들어주면 된다.

 

결론적으로는 asList란 녀석은 배열을 리스트로 바꿔 작업할 필요성이 있는 경우 도움이 되는 구문이라 한다.

 

 


 

2. Stream 

스트림이란 것에 대해 아직 제데로 이해하지 못했지만 간단히 정의하면 "데이터의 흐름" 이라고 한다.

 

예로 운영체제에 의해 입력과 출력이 이뤄질 때, 그 사이에서 매개 역할을 하는 중간자 라고 한다.그리고 스트림은

"트림 생성 -> 가공(Mapping, Filtering ...) -> 결과(Collector, Reduction)" 의 과정으로 생성하여 사용하게 된다.

 

이에 대한 이론은 조금씩 더 공부하고... 코드를 보면

List<String> result = test_list.stream()
	        .filter(str -> test2_list.stream().anyMatch(Predicate.isEqual(str)))
	        .collect(Collectors.toList());

result.stream().forEach(System.out::println);
  1. 우선 나는 2개의 리스트를 비교할것이기 때문에 한개의 리스트를 stream( ) 메소드로 스트림 객체를 얻어준다.
  2. 그리고 filter 메소드로 내가 만들 결과물에 대한 "가공"을 진행한다.
  3. 마지막으로 collect 메소드를 통해 "결과물"을 만들어준다.

 


@ Filter 

  ⇒ Filter 함수의 설명은 위와 같다. 조금 이해하기 어렵지만, 다른 정보들을 찾으며 이해한 바로는

  1. 스트림 내 값들을 하나씩 특정 수행을 통해 필터링한다.
  2. Predicate 라는 녀석을 Bollean 형태로 받는데, 이는 함수형태 이며 true인것에 대해 리턴한다.

이다.

 

이때 나는 람다식을 사용하였는데 (자바스크립트의 화살표함수 같은것),

반복변수인 "str" 에는 test_list의 값이 , test2_list를 스트림으로 얻어 비교한다.

anyMatch(단 하나라도 일치하는게 있다면 값을 반환한다) 안의 isEqual을 통해 값이 있다면 반환한다.

 

 

@ Collect 

Collect 구문은 Collector 타입의 값을 받아 처리하는 스트림의 결과 작업 함수 중 하나이다.

여기서 말하는 Collector 타입에는 List와 Set이라는 형태가 있다고 하는데 이에 대한 부분은 후에 좀 더 봐야할듯 하다..

 

Collectors.toList( ) -> 내가 사용한 구문으로 Stream 작업을 통해 얻은 최종적인 결과물을 List로 반환한다.

Collectors.joining( ) -> toList와는 다르게 하나의 String으로 이어 붙혀서 값을 반환한다.

 

이 외에도 Collector에는 많은 함수들이 있는데, 내가 알고리즘 문제를 푸는데 있어선 저 2개 말고는 사용해봐야 감이 올것 같다...

 

 

 

 

 

아직은 테스트 케이스를 내가 자체적으로 만들기는 좀 제한적이라... 이중 for문을 썼을 때보다 스트림을 사용한것이 더 효율이 좋은지는 아직은 잘모르겠다.

나중에.. 알고리즘 문제를 풀다 비교 가능한 상황이라면 진행해볼 생각이다.

반응형

댓글