자바는 객체지향 언어이기 때문에 함수형 프로그래밍이 불가능하다. 그래서 배열이나 데이터를 정렬된 상태로 출력하고자 할 때, 원본의 데이터가 직접 정렬이 되어야만했다.
따라서 아래의 코드와 같이 처리해야만 했다.
String[] nameArr = {"A", "C", "B", "F"}
List<String> nameList = Arrays.asList(nameArr);
// 원본의 데이터가 직접 정렬됨
Arrays.sort(nameArr);
Collections.sort(nameList);
for (String str: nameArr) {
System.out.println(str);
}
for (String str : nameList) {
System.out.println(str);
}
하지만 Stream API와 람다식, 함수형 인터페이스를 사용하면 자바를 이용한 함수형 프로그래밍이 가능하다. 그 중 Stream API는 데이터를 추상화/처리하는 데에 자주 사용하는 함수를 정의한다. (*여기서 추상화
란 데이터의 종류에 상관없이 같은 방식으로 데이터를 처리할 수 있도록 하는 것. 재사용성 증대 등의 이점이 존재함)
따라서 아래의 코드로 가독성을 높여서 처리할 수 있다.
String[] nameArr = {"A", "C", "B", "F"}
List<String> nameList = Arrays.asList(nameArr);
// 원본의 데이터가 아닌 별도의 Stream을 생성함
Stream<String> nameStream = nameList.stream();
Stream<String> arrayStream = Arrays.stream(nameArr);
// 복사된 데이터를 정렬하여 출력함
nameStream.sorted().forEach(System.out::println);
arrayStream.sorted().forEach(System.out::println);
둘 다 연속된 요소 형식의 값을 저장하는 자료구조의 인터페이스를 제공하며 순서에 따라 순서대로 요소에 접근한다.
🤔 내부반복 VS 외부반복 ?
- 내부반복 : 작업을 병렬처리하면서 최적화된 순서로 처리하는 방법.
- 외부반복 : 반복문으로 요소를 직접 탐색하는 방법. 병렬처리를 위해서는
synchronized
를 통해 관리가 가능함.
List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList
.stream() // 생성하기
.filter(s -> s.startsWith("c")) // 가공하기
.map(String::toUpperCase) // 가공하기
.sorted() // 가공하기
.count();
특정 조건을 만족한느 데이터만 거르기. true를 반환하며 stream형태로 반환.
List<String> startsWithN = names.stream()
.filter(name -> name.startsWith("S"))
.collect(Collectors.toList());
데이터를 변형하는데 사용함.
List<String> names = Arrays.asList("Sehoon", "Songwoo", "Chan", "Youngsuk", "Dajung");
names.stream()
.map(name -> name.toUpperCase())
.forEach(name -> System.out.println(name));
데이터가 순서대로 정렬된 stream 리턴, 데이터의 종류에 따라 Comparator가 필요할 수 있다.
// 오름차순
var numbers = List.of(5, 2, 3, 9, 4);
numbers.stream()
.sorted()
.collect(Collectors.toList());
//내림차순
var numbers = List.of(5, 2, 3, 9, 4);
numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
// 오름차순 - Comparator.comparing 사용
var StudentList = List.of(
new Student(4, "tae", 100, 100, 100),
new Student(5, "lob", 75, 75, 100),
new Student(1, "hong", 75, 90, 80),
new Student(2, "sujin", 50, 90, 100),
new Student(3, "kate", 90, 75, 75));
StudentList.stream()
.sorted(Comparator.comparing(Student::getNo))
.collect(Collectors.toList());
// 내림차순 - Comparator.comparing 사용
StudentList.stream()
.sorted(Comparator.comparing(Student::getNo).reversed())
.collect(Collectors.toList());
Stream 요소들을 하나의 데이터로 만드는 작업을 수행함.
// 결과값에 대한 초기값이 없는 경우
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> sum = numbers.reduce((x, y) -> x + y);
sum.ifPresent(s -> System.out.println("sum: " + s));
// 초기값이 있는 경우
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = numbers.reduce(10, (total, n) -> total + n);
System.out.println("sum: " + sum);
map과 달리 평면화된 단일 스트림 반환, Array나 Object로 감싸져 있는 모든 원소를 단일 원소 스트림으로 반환.
// flatMap 사용하지 않았을 때
String[][] sample = new String[][]{
{"a", "b"}, {"c", "d"}, {"e", "a"}, {"a", "h"}, {"i", "j"}
};
Stream<String> stream = sample.stream()
.filter(alpha -> "a".equals(alpha[0].toString() || "a".equals(alpha[1].toString())))
stream.forEach(alpha -> System.out.println("{"+alpha[0]+", "+alpha[1]+"}"));
> output
{a, b}
{e, a}
{a, h}
// flatMap 사용했을 때
String[][] sample = new String[][]{
{"a", "b"}, {"c", "d"}, {"e", "a"}, {"a", "h"}, {"i", "j"}
};
Stream<String> stream = sample.stream()
.flatMap(array -> Arrays.stream(array))
.filter(x-> "a".equals(x));
stream.forEach(System.out::println);
> output
a
a
a
*참고 : https://mangkyu.tistory.com/112, https://gyoogle.dev/blog/computer-language/Java/Stream.html