Stream

YEON·2022년 3월 15일
0

java

목록 보기
1/2

Stream API

함수형 프로그래밍
(java 는 기본적으로 객체지향 언어라 함수형 프로그래밍이 불가능한데, JDK8부터 Stream API 와 람다식 등을 통하여 Java 를 이용해 함수형으로 프로그래밍 할 수 있는 API 를 제공해준다.)

스트림을 생성하는 최초연산, 중간 로직을 구성하는 중간 연산, 결과물을 처리하는 최종 연산으로 구성된다.

  • 함수형 프로그래밍이란?
    • 선언형 프로그램으로 How 보단 What 목적 위주로 기술한다. 데이터 입력이 주어지고 데이터 흐름을 추상적으로 정의하는 방식이다. 객체지향이 동작하는 부분을 캡슐화하여 이해를 돕는다면 함수형은 동작하는 부분을 최소화하여 이해를 돕는다.
    • 프로그래밍을 순수 함수로 나누어 문제를 해결하는 기법. 작은 문제를 해결하기 위한 함수를 작성하므로써 가독성을 높이고 유지보수를 용이하게 함.



stream 을 사용하는 이유

  • 데이터 소스를 추상화하여 코드의 재사용성이 높아졌다
    (데이터 소스의 추상화란? 데이터를 다루는데 자주 사용하는 메서드들을 정의
    데이터 소스가 무엇이든 같은 방식으로 처리 가능 : 배열이나 List 를 둘다 Stream 으로 처리 가능하다)

  • 작업을 내부 반복으로 처리함으로써 작업이 간결해질 수 있다.
    ( java8 이전엔 for 문으로 처리했지만, 지금은 .forEach 를 쓰면 되는 것 처럼.)

  • 컬렉션 요소를 람다식으로 처리하여, 복잡한 구조의 데이터 처리를 간단하게 해주어 코드의 복잡도를 낮출 수 있다.

  • 데이터를 다룰 때 병렬 처리가 쉽다. 멀티 스레드를 활용해서 병렬로 연산을 계산 할 수 있고,부 반복으로 연산을 수행하기 때문에 코드가 매우 간단해진다는 것을 알 수 있음.~



stream 특징

  • 일회용이다. 한번 사용하고 나면 끝.
stream1.sorted().forEach(System.out::println);
int count = stream1.count(); // ERROR 스트림 닫힘
  • 스트림에서 제공하는 연산을 이용하여 복잡한 작업들을 간단히 처리 가능
stream.forEach(System.out::println);
  • 원본의 데이터를 변경하지 않는다. (데이터를 읽기만 한다)
List<String> result = stream1.sorted().collect(Collectors.toList());



생성

  • Collection 에 스트림을 생성하는 메서드 정의 .stream()
  • Stream 에 스트림을 생성하는 static 메서드로 정의 .of()
  • IntStream 에 범위의 정수를 반환하여 스트림을 생성하는 메서드 .ranged()
  • 람다식을 매개변수로, 무한 스트림을 생성하는 메서드 .iterate(), .generate()
    ( iterate 와의 차이점은 매개변수가 없어야하고, 이전 결과를 이용하여 다음 결과를 계산하지 않는다)
  • Files 에 파일 목록을 소스로 하는 스트림 생성 메서드 .list()
List<Integer> list = ArrayList.asList(1,2,3,4);
Stream<Integer> stream = list.stream();

Stream<String> stream = Stream.of("a","b","c");

IntStream stream = IntStream.rangeClosed(1,5);

Stream<Integer> stream = Stream.iterate(0, n->n+2); //0,2,4,6...
Stream<Integer> stream = Stream.generate(()->1);



중간연산

  • 데이터 변환 map , 필터링 filter , 정렬 sort , 중복제거 distinct 등.. 을 제공한다
  • 매개변수로 함수형 인터페이스(Predicate)를 받는다.
    (Type T 인자를 받고 boolean을 리턴하는 함수형 인터페이스)
  • 중간연산의 리턴 값은 스트림으로 계속해서 메서드 체이닝을 이어나갈 수 있다.
  • 최종연산의 리턴값이 수행되지 않는다면 중간 연산 역시 수행되지 않는다.
    (최종 연산이 수행되어야 중간연산을 거쳐 최종 연산이 이뤄진다)
• 필터링 / 조건

.filter()

intStream.filter(i->i%2==0 && i%3!=0)
	.forEach(System.out::print);
• 데이터 변환

.map() , .mapToInt() , .mapToObj() ..
: 매개변수로 T 타입을 R 타입으로 변환해서 반환하는 함수를 지정

Student[] student = { new Student("홍길동",20,160) };
Stream<Student> studentStream = Stream.of(student);

//모두 대문자로 변환
studentStream.map(Student::getName)
			 .map(s->s.toUpperCase());

//기본형 스트림의 계산 메서드 제공
int avg = studentStream.mapToInt(Student::getTotalScore)
					   .average();

//정수를 문자열로 변환 (원시 Stream으로 변환)
Stream<String> lottoStream = new Random().ints(1,46)
										 .distinct().limit(6).sorted()
                                         .mapToObj(i->i+",");
                                         
//문자열을 정수로 사용
int sum = "12345".chars()
				 .map(ch->ch-'0').sum();  //sum=15
• 정렬

sorted()

List<String> list = Arrays.asList{"c","b","a"};
list.stream().sorted().forEach(System.out::print); // [a,b,c]

//Comparator의 static 메서드) Comparator<T> 반환
studentStream.sorted(Comparator.comparing(Student::getAge)) // 우선 나이 순 정렬
			 			.thenComparing(Student::getScore))  // 이후 성적 순 정렬
                        .forEach(System.out::print);
• Optional 반환 연산들 / 통계

.findAny(), .findFirst(), .max(), min(), .reduce()

  • Optional< T> 이란?
    • T 타입의 객체를 감싸는 래퍼 클래스
    • 최종 연산의 결과를 Optional 객체에 담아서 반환함으로써, 반환된 결과가 null 일 때 NullPointerException 을 방지 할 수 있다.
    • 값을 가져올 때는 .get() 을 사용한다.



최종연산

  • 데이터 수집 collect, 조건 검사 match, 통계count 등.. 을 제공한다.
  • 스트림의 요소를 소모하여 결과를 만들어낸다.
• 출력

.forEach()
: stream의 요소들을 대상으로 특정한 연산을 수행 할 때
: 반환 타입은 void

• 데이터 수집

.collect()
: stream 의 요소들을 컬렉션이나 배열등으로 수집하는 최종 연산
: 매개변수로 Collector(인터페이스) 필요
: Collectors(클래스)에 static 메서드로 미리 작성된 Collector를 제공한다.

  • 스트림을 컬렉션과 배열로 변환
List<Student> students = Arrays.asList(new Student(1, "홍길동", 100),
                            		new Student(2, "고길동", 80));

// List 반환
List<String> name = students.stream()
							.map(Student::getName)
                            .collect(Collectors.toList());
                            
// Map 반환 | <키,값> 지정
Map<Integer, Student> store = students.stream()
									  .collect(Collectors.toMap(s->s.getId(), s->s));

//Array 반환 | 해당 타입의 생성자 참조를 매개변수로 지정
Student[] student = students.stream().toArray(Student[]::new);
  • ..
• 조건 검사

.match() : anyMatch, allMatch, noneMatch
: Stream 요소들이 특정한 조건을 충족하는 검사

// 1개의 요소라도 해당 조건을 만족하는가
boolean anyMatch = names.stream().anyMatch(name -> name.contains("a")); 

// 모든 요소가 해당 조건을 만족하는가
boolean allMatch = names.stream().allMatch(name -> name.length() > 10); 

// 모든 요소가 해당 조건을 만족하지 않는가
boolean noneMatch = names.stream().noneMatch(name -> name.length() < 5);




stream 에 정의된 연산 정리

: 중간연산 & 최종연산
: 매개변수로 함수형 인터페이스를 받는다.

  • distinct() : 중복 제거

  • count() : 개수 반환

  • max,min()

  • limit() : 스트림의 일부만

  • findAny,findFirst() : 스트림 요소 하나만 반환

  • allMatch, anyMatch, noneMatch() : 주어진 조건을 모든 요소가 만족시키는지

  • filter() : 스트림의 조건

  • sorted() : 스트림의 요소를 정렬

  • map() : 스트림의 요소를 변환

  • forEach() : 각 요소에 지정된 작업 수행

  • toArray() : 배열로 반환
  • collect() : 스트림의 요소 수집. 컬렉션으로 담아 반환




[개념 참조]
자바의 정석
https://mangkyu.tistory.com/112
https://ryan-han.com/post/dev/java-stream/

[기타 참조]
https://velog.io/@hygoogi/%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91-%EC%A4%80%EB%B9%84%ED%95%98%EA%B8%B0
https://dev-kani.tistory.com/32

profile
- 👩🏻‍💻

0개의 댓글