먼저 Java 는 객체지향 언어이므로 기본적으로 함수형 프로그래밍이 불가능했었습니다.
하지만 Java8부터 Stream API, lambda, 함수형 인터페이스 등을 지원하면서 Java에서도 함수형 프로그래밍을 할 수 있게 되었습니다. 그 중 Stream API에 대해 알아보려고 합니다.
함수형 인터페이스를 적용하여 컬렉션, 배열과 같은 저장요소를 반복적으로 순환하며 가공,처리를 해주는 기능입니다. 이러한 Stream API를 사용한다면 불필요한 for문과 그 안에서 이루어지는 if문 등의 분기처리를 쓰지않고 깔끔하고 직관적인 코드로 변형할 수 있습니다.
먼저 생성하기에 대한 예시로 Collection은 내부에 stream() 메소드를 통하여 stream을 생성 할 수 있습니다. 배열은 Arrays.stream()을 사용하여 만들 수 있고 Stream.of() 정적 팩토리 메소드를 통해서도 생성 할 수 있다.
또 기타 생성하는 코드는 아래 코드에서 확인 가능합니다.
//Collection -> Stream
List<Integer> list = new ArrayList<>();
list.stream(); // Stream<Integer>
//Array -> Stream
int[] array = new int[2];
Arrays.stream(array); // IntStream
Stream.of(1, 2, 3, 4, 5);s // Stream<Integer>
//특정 범위의 정수를 요소로 갖는 Stream
IntStream.range(1, 5); // 1,2,3,4
IntStream.rangeClosed(1, 5) // 1,2,3,4,5
//난수를 요소로 갖는 Stream
new Random().ints(); //무한 스트림
new Random().ints(5); // 크기가 5인 난수 스트림 (유한)
여기서 Stream과 IntStream의 차이를 알아두어야 합니다.
IntStream은 기본형 Stream으로 LongStream DoubleStream이 있고 오토 박싱/언박싱의 비효율이 제거되어있고 숫자와 관련된 유용한 메소드를 Stream보다 많이 제공합니다.
가공하는 중개연산에는 filter, map, sorted, distinct, mapToInt, mapToDouble 등이 있습니다.
// filter
// list = [-2, -1, 0, 1, 2]
list.stream()
.filter(item -> item > 0); // [1, 2]
//map
//list = ["a", "b", "c", "d"]
list.stream()
.map(item -> item.toUpperCase()); // ["A", "B", "C", "D"]
//sorted
//list = [3, 5, 1, 2, 4]
list.stream()
.sorted(); // [1, 2, 3, 4, 5]
list.stream()
.sorted(Comparator.reverseOrder());// [5, 4, 3, 2, 1]
//distinct
//list = [1, 1, 2, 2, 3]
list.stream()
.distinct(); // [1, 2, 3]
//mapToObj
IntStream.range(1,4)
.mapToObj(item -> "a" + item); // ["a1", "a2", "a3"]
//mapToInt
//list = [1, 2, 3];
list.stream().mapToInt(i -> i) // IntStream
외에도 다른 가공연산들도 찾아보면 좋을것같습니다.
두번째 중간연산에서는 Stream을 반환하여 체이닝되어 연산을 계속 이어나갈수 있지만 최종연산에서는 반환되는 값이 Stream이 아닙니다.
최댓값/최솟값/총합/평균/갯수 등 과 같은 연산과 연산된 Stream을 컬렉션이나 다른 종류의 결과로 수집할 수 있으며 join을 수행하여 String으로 반환할 수 있습니다.
//min, max는 Stream이 비어있을 경우를 값을 특정할 수 없어 Optional로 반환됩니다.
OptionalInt max = IntStream.of(1, 2, 3).max(); // 3
OptionalInt min = IntStream.of(1, 2, 3).min(); // 1
OptionalDouble avarge = IntStream.of(1, 2, 3).average(); // 2.0
//Stream이 비어있는 경우 0을 리턴한다.
long count = IntStream.of(1, 2, 3).count(); // 3
long sum = IntStream.of(1, 2, 3).sum(); // 6
//collect(stream to list)
//Product(id, name)
List<String> nameList = productList.stream()
.map(Product::getName)
.collect(Collectors.toList());
//collect(stream to map)
Map<Long, String> map = productList.stream()
.collect(Collectors.toMap(
item1 -> item1.getId(),
item2 -> item2.getName())
);
Map<Long, String> map = productList.stream()
.collect(Collectors.toMap(
Product::getId,
Product::getName
);
//collect(stream to set)
Set<Integer> set = IntStream.of(1, 2, 3, 4, 5)
.collect(Collectors.toSet());
//joining
//productList =
String nameString = Stream.of("hello","java","world")
.collect(Collectors.joining()); //hellojavaworld
String nameString = Stream.of("hello", "java", "world")
.collect(Collectors.joining(",")); //hello,java,world
String nameString = Stream.of("hello", "java", "world")
.collect(Collectors.joining(",","<",">"));//<hello,java,world>
//forEach
Stream.of("hello","java","world")
.forEach(System.out::println);
//hello
//java
//world
Stream API에 대해 간단하게 알아보았는데 위에서 다룬 메소드들은 극히 일부고 여러 메소드들도 사용해보면서 공부하면 좋을 것 같습니다.