JAVA 스트림

금송·2024년 10월 2일
0

이론

목록 보기
21/26
post-thumbnail

메소드 참조

종류람다메소드 참조
정적(static) 메소드 참조(x) → ClassName.method(x)ClassName::method
인스턴스 메소드 참조(obj, x) → obj.method(x)ClassName::method

메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내어 람다식에서 불필요한 매개변수를 제거하는 것이 목적

(left, right) -> Math.max(left, rigth);
== Math::max
  • 정적 메서드, 인스턴스 메서드 호출 실습 코드
    package chap12;
    
    import java.util.function.IntBinaryOperator;
    
    public class MethodReferencesExample {
        public static void main(String[] args) {
            IntBinaryOperator operator;
    
            // 정적 메서드 호출
            //operator = ((left, right) -> Calculator.staticMethod(left, right));     // == operator = Calculator::staticMethod;
    
            operator = Calculator::staticMethod;
            System.out.println("정적 메서드 호출 결과 : " + operator.applyAsInt(1, 2));
    
            // 인스턴스 메소드 호출
            Calculator calculator = new Calculator();
            // operator = (left, right) -> calculator.method(left, right);     // == operator = calculator::method;
    
            operator = calculator::method;
            System.out.println("인스턴스 메서드 호출 결과 : " + operator.applyAsInt(3, 5));
        }
    }
    

매개변수의 메서드 참조

람다식에서 제공되는 a 매개변수의 메서드를 호출해서 b 매개변수를 매개값으로 사용하는 경우

(a, b) -> {a.instanceMethod(b);}

// 해당 코드의 메서드를 표현하면 a 클래스 이름 뒤에 ::를 붙이고 메서드 이름 기술
클래스::instanceMethod

생성자 참조

메서드 참조는 생성자 참도도 포함

생성자를 참조한다는 것은 객체를 생성하는 것을 의미

단순히 메소드 호출로 구성된 람다식을 메소드 참조로 대치할 수 있듯이, 단순히 객체를 생성하고 리턴하는 람다식은 생성자 참조로 대치할 수 있다.

(a, b) -> {return new 클래스(a, b);}

클래스::new
  • 실습코드
    package chap12;
    
    public class Member {
        private String name;
        private String id;
    
        public Member(String name) {
            System.out.println("매개변수 하나인 생성자 호출");
            this.name = name;
        }
        public Member(String name, String id) {
            System.out.println("매개변수 두개인 생성자 호출");
            this.name = name;
            this.id = id;
        }
    }
    package chap12;
    
    import java.util.function.BiFunction;
    import java.util.function.Function;
    
    public class ConstructorReferencesExample {
        public static void main(String[] args) {
            // 두개 차이가 없는데 ctrl 누르고 클릭해서 매개변수 확인
            Function<String, Member> function = (x) -> new Member(x);
            // 생성자 참조
            function = Member::new;
            function.apply("string");
    
            BiFunction<String, String, Member> function2 = (x, y) -> new Member(x, y);
            // 생성자 참조
            function2 = Member::new;
            function2.apply("string", "string2");
    
        }
    }
    

스트림

스트림은 데이터의 흐름

자바 8부터 사용할 수 있는 기능이며 배열이나 컬렉션을 가공하여 원하는 결과를 얻을 수 있다.

흐름 생성 > 가공 (조건절로 필터링, 람다식. 메서드 참조) > 결과 수집 및 반환

스트림 소개

// Collection 인터페이스의 메서드로 제공
Stream<E> stream()
  • 짝수만 출력하는 소스코드
    // ex) 스트림을 사용하지 않았을 때
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    
    for (int n: numbers) {
    	if (n % 2 == 0) {
    		System.out.println(n);
    	}
    }
    // ex) 스트림을 사용했을 때
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    
    numbers.stream()
    	.filter(n -> n % 2 == 0)
    	.forEach(System.out::println);
  1. 생성 : 스트림 인스턴스 생성 (배열이나 컬렉션을 스트림 인스턴스로 변환)
  2. 가공 : 원하는 결과를 만들어가는 중간 작업
  3. 결과 : 최종 결과를 만들어내는 작업

각 단계 (생성, 가공, 결과)마다 데이터를 다루기위한 함수가 존재

스트림의 종류

이름설명
BaseStream모든 스트림에서 사용할 수 있는 공통 메소드들이 정의되어 있습니다. 코드에서 직접 사용하지는 않습니다.
Stream객체 요소를 처리하는 스트림입니다.
IntStreamint 형을 처리하는 스트림입니다.
LongStreamlong 형을 처리하는 스트림입니다.
DoubleStreamdouble 형을 처리하는 스트림입니다.

“java.util.stream” 패키지에는 스트림 인터페이스가 정의되어 있고 BaseStream을 부모로해서 자식 인터페이스들이 상속 밭는 구조를 이루고 있다.

스트림을 만드는 방법

  1. 컬렉션으로부터 스트림 생성

컬렉션 타입(Collection, List, Set)의 경우 디폴트 메소드 stream을 이용하여 스트림을 만들 수 있다.

  1. 배열로부터 스트림 생성

배열은 Arrays.stream 메서드를 사용하여 스트림을 생성한다.

  1. 숫자 범위로부터 스트림 생성

range, rangeClosed 메서드를 이용하여 특정 범위의 숫자를 가지는 스트림을 생성할 수 있다.

range, rangeClosed의 차이는 두번째 인자인 범위의 끝 값이 포함 되냐 안되냐 차이다. (후자가 포함)

  • 또한 Random 클래스로 난수 스트림을 생성할 수 있다.
    DoubleStream doubleStream = new Random().doubles(5); // 난수 5개 생성
  1. 파일로부터 스트림 생성

Files 클래스의 정적 메서드인 lines와 BufferedReader의 lines 메서드로 해당 파일의 각 라인을 스트링 타입의 스트림으로 만들 수 있다.

  1. 스트림 연결

Stream.concat 메서드를 이용하여 두 개의 스트림을 연결할 수 있다.

  • 실습 코드
    package chap13;
    
    import java.io.IOException;
    import java.nio.charset.Charset;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.DoubleStream;
    import java.util.stream.IntStream;
    import java.util.stream.LongStream;
    import java.util.stream.Stream;
    
    import static java.lang.Class.forName;
    
    public class StreamExample {
        public static void main(String[] args) throws IOException {
            // 1. 컬렉션으로 스트림 생성
            List<String> list = Arrays.asList("1", "2", "3", "4");
            Stream<String> stringStream = list.stream();
    
            // 2. 배열로 스트림 생성
            String[] array = {"1", "2", "3", "4"};
            Stream<String> stringStream2 = Arrays.stream(array);
    
            // 3. 숫자 범위 스트림 생성
            IntStream intStream = IntStream.range(1, 5); // 범위 지정 [1,2,3,4] 마지막 값 제외
            LongStream longStream = LongStream.rangeClosed(1, 5); // 범위 지정 [1,2,3,4,5] 마지막 값 포함
            DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0); // 값 직접 기입 [1.0, 2.0, 3.0]
    
            // 4. 파일을 통한 스트림 생성
            Stream<String> fileStream = Files.lines(Paths.get("file1.txt"), Charset.forName("UTF-8"));
            fileStream.forEach(System.out::println);
    
            // 5. 스트림 연결해서 하나의 스트림 생성
            Stream<Integer> intStream1 = Stream.of(1, 2, 3);
            Stream<Integer> intStream2 = Stream.of(4, 5, 7);
            Stream<Integer> concated = Stream.concat(intStream2, intStream1);
            concated.forEach(System.out::println);          // == concated.forEach(x -> System.out.println(x));
        }
    }
    
    // 결과
    first line
    new jeans
    4
    5
    7
    1
    2
    3

Optional

하나의 래퍼 클래스를 말함.

해당 클래스 내에는 객체를 가지고 있는데 그 객체는 실제 주소값을 가진 값일수도 null 값일수도 있다. 이때 optional을 감싸서 사용하는 이유 → NullPointException을 방지하기 위해서.

NullPointerException

// student 객체가 null일 경우 NullPointerException 발생
Student student = null;
// null 값에 getName이라는 메소드는 존재하지 않기 때문
student.getName();

null 값을 가진 객체를 참조하려고 했을 때 NPE가 발생하는데, NPE는 컴파일 시점에는 발견할 수 없기 때문에 프로그래머는 항상 객체가 null값이 될 수 있음을 인지하고 소스코드를 작성해야한다.

스트림과 Optional

컬렉션의 경우 동적으로 생성되는 경우가 많다.

List<Integer> list = null;

list.stream().forEach(System.out::println);   // NPE, null값은 stream이라는 메서드를 가지고 있지 않기 때문.

// NPE 방지
List<Integer> list = null;

Optional.ofNullable(list)
	.orElseGet(Collections::emptyList)
	.forEach(System.out::println);

Optional 객체 생성 메서드

Optional 클래스는 of와 ofNullable 메서드를 지원한다.

of() 메소드나 ofNullable() 메소드를 사용하여 Optional 객체를 생성할 수 있는데 둘의 차이점은 of는 null값을 허용하지 않고 ofNullable은 null값을 허용한다는 것이다.

메소드설명
static Optional empty()아무런 값도 가지지 않는 비어있는 Optional 객체를 반환함.
static Optional of(T value)null이 아닌 명시된 값을 가지는 Optional 객체를 반환함.
static Optional ofNullable(T value)명시된 값이 null이 아니면 명시된 값을 가지는 Optional 객체를 반환하며, 명시된 값이 null이면 비어있는 Optional 객체를 반환함.

Optional 객체 반환

기본적으로 해당 클래스는 get() 메서드를 사용하여 Optional 객체에 저장된 값을 반환하지만, 만약 해당 객체가 null일 경우에는 예외가 발생한다.

안전하게 객체를 꺼내오기 위해서는 null일 경우 사용할 default 값을 정할 수 있다.

이때 사용하는 메서드가 orElse(), orElseGet()메서드이고 orElseGet() 메서드는 인수로 람다식을 사용할 수 있다.

Optional.ofNullable(stringList)
         .get()      // stringList가 null일 경우 NoSuchElementException 예외 발생!!
List<String> newList = Optional.ofNullable(list)
	.orElse(Collections.emptyList());
List<String> newList = Optional.ofNullable(list)
	.orElseGet(Collections::emptyList);  // 람다 표현식을 넣는 경우 
메소드설명
T get()Optional 객체에 저장된 값을 반환함.
T orElse(T other)저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 값을 반환함.
T orElseGet(Supplier<? extends T> other)저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 람다 표현식의 결과값을 반환.
T orElseThrow(Supplier<? extends X>  exceptionSupplier)저장된 값이 존재하면 그 값을 반환하고, 값이 존재하지 않으면 인수로 전달된 예외를 발생시킴.
boolean isPresent()저장된 값이 존재하면 true를 반환하고, 값이 존재하지 않으면 false를 반환함.

Optional을 이용한 스트림에서의 null 처리

  1. isPresent()로 체크하고 값을 꺼내서 출력
  2. 값을 꺼내올 때 orElse 사용하여 defalut값을 기입해 값이 있으면 값 출력, 값이 없으면 defalut값 출력
  3. ifPresent()을 사용하여 값이 실제로 존재할 경우에만 동작하여 값이 있으면 출력 없으면 공백.
  • 실습 코드
    package chap13.optional;
    
    import java.util.*;
    
    public class OptionalExample {
        public static void main(String[] args) {
            List<String> list = null;
    
            // Optional
            Optional<List<String>> optional = Optional.ofNullable(list);
            optional.orElseGet(Collections::emptyList)     // == optional.orElseGet(() -> Collections.emptyList());
                    .forEach(System.out::println);   // == .forEach(x -> System.out.println(x));
    
            Optional.ofNullable(list)
                    .orElseGet(Collections::emptyList)
                    .forEach(System.out::println);
    
            // Optional 객체 생성 (empty(), of(), ofNullable())
            Optional<Integer> empty = Optional.empty();
            OptionalInt empty2 = OptionalInt.empty();
            OptionalDouble empty3 = OptionalDouble.empty();
    
            Optional<Integer> optional2 = Optional.of(456);     // value로 null이 들어가면 오류가 발생한다.
    
            Optional<Integer> optional3 = Optional.ofNullable(3281);
    
            // Optional 객체 꺼내기 .get(), orElse(), orElseGet(), orElseThrow()
            Integer value = empty.orElse(123);
            System.out.println(value);      // 123
            // empty2.orElseGet(() -> 12);
    
            optional3.orElseThrow();
            Integer i = optional2.orElseThrow(NoSuchElementException::new);         // == Integer i = empty.orElseThrow(() -> new NoSuchElementException() );
            System.out.println(i);
    
            // isPresent() 값을 체크하는 목적으로 많이 사용
            if(optional3.isPresent()){
                Integer integer = optional3.get();
                System.out.println(integer);
            }
    
        }
    }
    package chap13.optional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.OptionalDouble;
    
    public class OptionalExample2 {
        public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
    
            // NoSuchElementException 예외 발생
    //        double avg = list.stream()
    //                .mapToInt(Integer::intValue)
    //                .average()
    //                .getAsDouble();
    //
    //        System.out.println(avg);
    
            // NoSuchElementException 예외 발생 안함
            // optional 객체 안전하게 꺼내는 방법 1
            OptionalDouble optionalDouble = list.stream()
                                                .mapToInt(Integer::intValue)
                                                .average();
            if(optionalDouble.isPresent()) {
                double avg = optionalDouble.getAsDouble();
                System.out.println(avg);
            }
    
            // optional 객체 안전하게 꺼내는 방법 2
            double result = optionalDouble.orElse(0.0);     // 0.0 이라는 값 defalut로 설정
            System.out.println(result);     // 0.0
    
            //
            List<Integer> list2 = new ArrayList<>();
            OptionalDouble optionalDouble2 = list2.stream()
                                                .mapToInt(Integer::intValue)
                                                .average();
            optionalDouble.ifPresent(System.out::println);       //== optionalDouble.ifPresent(avg -> System.out.println(avg));
        }
    }
profile
goldsong

0개의 댓글

관련 채용 정보