Java 8에서 도입된 Stream 인터페이스를 이용하면 시퀀셜한 객체들의 연산을 간편히 할 수 있습니다.
Java 8부터 인터페이스도 default 라는 키워드로 구현 매소드를 정의할 수 있게 되었습니다. Collection 인터페이스의 default 매소드인 stream() 매소드를 이용하면 Stream 형태로 객체들을 다룰 수 있습니다.
Stream 인터페이스에 정의되어있는 연산 매소드에 실제 적용할 함수를 전달함으로써 java에서 간단하게 함수형 프로그래밍을 사용할 수 있도록 지원합니다.
다음 객체들은 아래 예제코드에서 활용됩니다.
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 + '\'' + '}'; } }
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]
데이터를 특정 데이터로 변환
// 예시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아우디'}]
스트림의 각 요소를 또다시 스트림으로 변환하여 처리할때 유용
// 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_+]
중복된 값이나 객체를 제거
List<Integer> results= nums.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(results);
[output]
[1, 2, 9, 11, 4, 3, 55]
중개 연산 단계에서 요소들을 순회하며 처리할 것이 있는 경우 사용
=> 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 매소드의 파라미터로 전달되는 long 변수 크기만큼, 첫번째 요소부터 제외후 나머지 요소로만 이루어진 스트림 반환
요소들을 정렬
객체 요소의 경우 Comparator 인터페이스를 구현하여 전달해야 사용 가능
스트림의 요소들을 순회하며 함수를 수행
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
데이터를 원하는 형태로 반환합니다.
// 예시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='벤츠'}]
스트림을 순회하며 특정 연산의 결과를 누적하여 결과값을 반환
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
스트림을 배열로 변환하여 반환
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
요소의 갯수를 반환
요소의 최대값을 반환
값 비교를 위한 Comparator 인터페이스 구현 필요
요소의 최소값을 반환
값 비교를 위한 Comparator 인터페이스 구현 필요
둘다 Stream의 첫번째 요소를 Optional 객체로 반환
차이점은
스트림의 요소들 중 주어진 조건에 만족하는 요소가 있는지를 논리연산하여 boolean 으로 리턴
스트림의 모든 요소들이 주어진 조건에 만족하지 않는지를 논리연산하여 boolean 으로 리턴