Language/Java
Stream, Optional & Lambda
do-oni
2021. 8. 17. 20:25
Stream
Java SE 8 부터 추가 된 함수형 프로그래밍으로
컬렉션, 배열등의 저장 요소를 하나씩 참조하여 람다식을 적용해 반복적으로 처리할 수 있게 해줌
데이터를 추상화 하여 다룸
< Stream >
• 데이터의 흐름
• 데이터를 변경하지 않고 읽기만 함
• 한번 사용하면 재사용 불가, 일회성
• 작업을 내부 반복으로 처리
• 다양한 자료구조 인스턴스를 여러개 결합하여 결과 도출 가능
• lambda와 병행 사용하여 코드를 간결화
• 다중 thread를 이용한 병렬 처리 지원 (Parallel processing)
Java Platform SE 8
docs.oracle.com
• 스트림 연산 단계
- 스트림 생성 : 스트림 인스턴스 생성
› stream(), Stream.builder(), Stream.generate(), Stream.iterate()
- 중간 연산 (스트림 변환, 가공) : 필터링(filtering), 맵핑(mapping) 등 원하는 결과를 만들어 가는 중간 작업
› map(), filter(), sorted(), limit(), peek(), distinct()
- 최종 연산 (스트림 사용, 결과) : 최종 결과를 만드는 작업
› min(), max(), sum(), count(), average(), reduce(), collect()
import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; public class Step01StreamAPI { @Test public void m1() { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); /* asList() : 가변인자 즉 parameter수 제한없이 호출해서 데이터 구성 가능, List 객체로 반환 */ List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); list.stream().forEach(System.out::println); list.forEach(data -> System.out.println(data + "\t")); //1 2 3 4 5 list.forEach(System.out::print); //12345 //? 3이상인 데이터만 출력(조건식 = 필터링) //filter()는 stream()의 반환값 객체에서 호출 가능 list.stream().filter(v -> v >= 3).forEach(System.out::print); //345 System.out.println("\n---1---"); list.stream().filter(v -> v > 3).forEach(System.out::println); System.out.println("---2---"); list.stream().filter(v -> v > 2).forEach(System.out::println); //matToInt() : 문자(객체타입)를 숫자로 변환해주는 메소드 System.out.println("---3---"); int r1 = list.stream().filter(v -> v > 2).mapToInt(i -> i).sum(); System.out.println(r1); System.out.println("---4---"); r1 = list.stream().filter(v -> v > 2).sum(); //문법 오류 System.out.println(r1);
< Optional >
• null이나 null이 아닌 값을 담을 수 있는 래퍼 클래스 (Wrapper class)
• NullPointerException(NPE) 을 예방 할 수 있음
Java Platform SE 8
docs.oracle.com
/* * Optional 객체 생성 방법 + * - ofNullable() : null 또는 객체로 생성 * - isPresent() : Optional 내부에 객체가 존재할 경우 true/null 즉 Optional.empty인 경우 false */ @Test public void m2() { String s1 = null; Optional<String> opt = Optional.ofNullable(s1); //null 또는 객체 다 parameter로 적용 가능 System.out.println(opt); //Optional[playdata] System.out.println("===="); s1 = "playdata"; Optional<String> opt2 = Optional.ofNullable(s1); System.out.println(opt2); System.out.println(opt2.isPresent()); //true 데이터가 존재할 경우 true/ null인 경우는 false System.out.println(opt.isPresent()); //true 데이터가 존재할 경우 true/ null인 경우는 false if(opt2.isPresent()) { System.out.println("null이 아니에요"); }else { System.out.println("null 입니다"); } } @Test public void m3() { String s1 = "playdata"; Optional<String> opt = Optional.ofNullable(s1); //null 또는 객체로 생성 String s2 = null; Optional<String> opt2 = Optional.ofNullable(s2); //playdata라는 문자열 객체를 보유한 Optional 객체로 문자열 같이 출력 opt.ifPresent(data -> { System.out.println(data.length()); //8 }); opt.ifPresent(data -> System.out.println(data.length())); //8 //null로 만들어진 Optional 객체 따라서 ifPresent() 로직에서 필터링 즉 실행 자체가 안되기 때문에 람다식 무시 opt2.ifPresent(data -> { System.out.println(data.length()); }); }
Lambda(람다)식
메소드를 하나의 식으로 표현한 것
메소드의 이름이 필요하지 않기 때문에 익명 함수 (Anonymous function) 라고 함
객체 지향 언어인 Java를 함수 지향 프로그래밍 가능하게 해줌
• 람다식 내에서 사용되는 지역변수는 final이 붙지 않아도 상수로 간주
• 람다식으로 선언된 변수명은 다른 변수명과 중복 불가
• 장점
- 코드의 간결화
- 가독성이 좋음
- 함수를 만드는 과정이 없어 생산성이 좋음
- 병렬 프로그래밍 용이
• 단점
- 재사용 불가
- 디버깅이 어려움
- 비슷한 함수가 중복 생성되어 코드가 지저분해질 수 있음
- 재귀에 부적합
public class Step02Lambda { @Test public void m1() { AddFunction addR = (no1,no2) -> no1 + no2; System.out.println(addR.sum(1, 2)); addR = (no1,no2) -> no1 - no2; System.out.println(addR.sum(1, 2)); MessageFunction msg = () -> "A"; System.out.println(msg.message()); MessageFunction2 mess = () -> System.out.println("A"); mess.message(); } @Test public void m2() { Map<String, String> map = new HashMap<>(); map.put("1", "A"); map.put("2", "B"); map.put("3", "C"); map.put("4", "D"); map.put("5", "E"); map.put("6", "F"); System.out.println("--- 1. class"); for(Map.Entry<String, String> entry :map.entrySet() ) { System.out.println(entry.getKey() + " : " + entry.getValue()); } System.out.println(" -- 2. java 8, forEach & lambda --"); map.forEach((k,v) -> System.out.println( k+ " : " +v )); System.out.println("--- 3. class"); for(Map.Entry<String, String> entry :map.entrySet() ) { if(entry.getValue().equals("A")) { System.out.println(entry.getKey() + " : " + entry.getValue()); } } System.out.println(" -- 4. java 8, forEach & lambda --"); map.forEach((k,v) -> System.out.println( k+ " : " +v )); map.entrySet().stream().filter(entry -> entry.getValue().equals("A")).forEach(data -> System.out.println(data.getKey() + " : " + data.getValue())); System.out.println(" -- 5. 이해를 위한 코드 --"); List<Integer> l1 = Arrays.asList(1,2,3,4,5); l1.stream().filter(v -> v == 2 ).forEach(System.out::println); l1.stream().filter(v -> v == 2 ).forEach(x -> System.out.println(x)); } }