[Java] Stream 기본

Jerry·2021년 9월 8일
0

language & framework study

목록 보기
21/28

Java 8에서 도입된 Stream 인터페이스를 이용하면 시퀀셜한 객체들의 연산을 간편히 할 수 있습니다.

Java 8부터 인터페이스도 default 라는 키워드로 구현 매소드를 정의할 수 있게 되었습니다. Collection 인터페이스의 default 매소드인 stream() 매소드를 이용하면 Stream 형태로 객체들을 다룰 수 있습니다.

Stream 인터페이스에 정의되어있는 연산 매소드에 실제 적용할 함수를 전달함으로써 java에서 간단하게 함수형 프로그래밍을 사용할 수 있도록 지원합니다.

Stream 인터페이스에 정의되어있는 다양한 함수를 예제코드로 확인해봅시다.

다음 객체들은 아래 예제코드에서 활용됩니다.

ArrayList<String> names=new ArrayList<>(Arrays.asList("kim","lee","park","choi","jung","ku","yoon"));

ArrayList<Integer> nums=new ArrayList<>(Arrays.asList(1,2,9,11,4,3,55,1,2));

ArrayList<ArrayList<String>> twoDimensionNames=new ArrayList<>(
		Arrays.asList(new ArrayList<>(Arrays.asList("gang","do","jerry","lucy","tom"))
              ,new ArrayList<> Arrays.asList("kim","lee","park","choi","jung","ku","yoon"))));

ArrayList<Car> cars=new ArrayList<>(Arrays.asList(new Car(1000,"아반떼")
          ,new Car(2000,"쏘나타")
          ,new Car(3000,"벤츠")
          ,new Car(4000,"아우디")));

// Car Class
public class Car {
private int price;
private String carName;

..
@Override
public String toString() {
  return "Car{" +
          "price=" + price +
          ", carName='" + carName + '\'' +
          '}';
}
}

중개 연산(intermediate operation) : 연산 처리 후 Stream을 반환

filter

Stream 데이터를 조건으로 거름

// 예시1
List<String> results=names.stream().filter(name-> name.equals("kim")).collect(Collectors.toList());
System.out.println(results);

// 예시2
List<Integer> results2= nums.stream().filter(num->num>10).collect(Collectors.toList());
System.out.println(results2);

[output]

[kim][11, 55]

Map

데이터를 특정 데이터로 변환

// 예시1
List<String> results=names.stream().map(name-> name+"_added").collect(Collectors.toList());
System.out.println(results);

// 예시2
List<Integer> results2= nums.stream().map(num-> num+100).collect(Collectors.toList());
System.out.println(results2);

// 예시3
List<Car> results3= cars.stream().map(car-> {
  car.setCarName("changed car"+car.getCarName());
  return car;
}).collect(Collectors.toList());
System.out.println(results3);

[output]

[kim_added, lee_added, park_added, choi_added, jung_added, ku_added, yoon_added][101, 102, 109, 111, 104, 103, 155, 101, 102]
[Car{price=1000, carName='changed car아반떼'}, Car{price=2000, carName='changed car쏘나타'}, Car{price=3000, carName='changed car벤츠'}, Car{price=4000, carName='changed car아우디'}]

flatMap

스트림의 각 요소를 또다시 스트림으로 변환하여 처리할때 유용

// map 예시 : List 안에 List가 있는 객체의 경우, map을 사용하면 내부 List는 Stream으로 직렬화 하지않고 객체 그 자체로 사용함
List<String> results=twoDimensionNames.stream()
  .map(name-> name+"_added")
  .collect(Collectors.toList());
System.out.println(results);

// flatMap 예시 : flatMap을 이용하면 내부 List도 Stream으로 직렬화
List<String> results2=twoDimensionNames.stream()
  .flatMap(names-> names.stream())
  .map(name->name+"_+")
  .collect(Collectors.toList());
System.out.println(results2);

[output]

[[gang, do, jerry, lucy, tom]_added, [kim, lee, park, choi, jung, ku, yoon]_added][gang_+, do_+, jerry_+, lucy_+, tom_+, kim_+, lee_+, park_+, choi_+, jung_+, ku_+, yoon_+]

distinct

중복된 값이나 객체를 제거

List<Integer> results= nums.stream()
                .distinct()
                .collect(Collectors.toList());
System.out.println(results);

[output]

[1, 2, 9, 11, 4, 3, 55]

peek

중개 연산 단계에서 요소들을 순회하며 처리할 것이 있는 경우 사용

=> foreach와 유사하지만 종단 연산이 아닌 차이점이 있음

long count = nums.stream()
                .peek(num-> System.out.print(num+", "))
                .collect(Collectors.counting());
System.out.println();
System.out.println("after streem : "+count);

[output]

1, 2, 9, 11, 4, 3, 55, 1, 2,
after streem : 9

skip

skip 매소드의 파라미터로 전달되는 long 변수 크기만큼, 첫번째 요소부터 제외후 나머지 요소로만 이루어진 스트림 반환

sorted

요소들을 정렬

객체 요소의 경우 Comparator 인터페이스를 구현하여 전달해야 사용 가능

종단 연산(terminal operation) : Stream이 아닌 다른 형태의 데이터로 반환

foreach

스트림의 요소들을 순회하며 함수를 수행

nums.stream()
    .forEach(num-> {
      num=num+100;
      System.out.println("plus 100 : "+num);
    });

[output]

plus 100 : 101
plus 100 : 102
plus 100 : 109
plus 100 : 111
plus 100 : 104
plus 100 : 103
plus 100 : 155
plus 100 : 101
plus 100 : 102

collect

데이터를 원하는 형태로 반환합니다.

// 예시1 : List 형태로 반환
List<String> results=names.stream().collect(Collectors.toList());
System.out.println(results);

// 예시2 : Charsequence나 String을 joining 하여 하나의 String으로 반환
String str=names.stream().collect(Collectors.joining());
System.out.println(str);

// 예시 3 : Set 형태로 반환
Set<Car> results2=cars.stream().collect(Collectors.toSet());
System.out.println(results2);

[output]

[kim, lee, park, choi, jung, ku, yoon]
kimleeparkchoijungkuyoon
[Car{price=1000, carName='아반떼'}, Car{price=4000, carName='아우디'}, Car{price=2000, carName='쏘나타'}, Car{price=3000, carName='벤츠'}]

reduce

스트림을 순회하며 특정 연산의 결과를 누적하여 결과값을 반환

OptionalInt resultVal=nums.stream()
                .mapToInt(Integer::intValue)
                .reduce((result,num)->result*num+10);
							// result는 축적되는 값. num은 스트림을 순회하며 얻는 요소 
if(resultVal.isPresent()){
  System.out.println(resultVal.getAsInt());
  return;
}
System.out.println("error");

[output]

1731010

toArray

스트림을 배열로 변환하여 반환

int[] numsArr=nums.stream()
                  .mapToInt(Integer::intValue)
                  .toArray();
for(int num : numsArr){
  System.out.println(num);
}

[output]

1
2
9
11
4
3
55
1
2

count

요소의 갯수를 반환

max

요소의 최대값을 반환

값 비교를 위한 Comparator 인터페이스 구현 필요

min

요소의 최소값을 반환

값 비교를 위한 Comparator 인터페이스 구현 필요

findAny, findFirst

둘다 Stream의 첫번째 요소를 Optional 객체로 반환

차이점은

  • findFirst는 병렬 처리인 경우에도 항상 첫번째 요소를 반환하지만
  • findAny는 병렬 처리인 경우 각 스레드에서 stream을 처리할 때 가장 먼저 찾은 요소를 리턴

anyMatch

스트림의 요소들 중 주어진 조건에 만족하는 요소가 있는지를 논리연산하여 boolean 으로 리턴

noneMatch

스트림의 모든 요소들이 주어진 조건에 만족하지 않는지를 논리연산하여 boolean 으로 리턴

profile
제리하이웨이

0개의 댓글