[SPRING] JAVA8

[ Lambda ]

 

[ 람다함수 ]

프로그래밍 언어에서 사용되는 개념으로 익명함수를 지칭하며 함수를 단순하게 표현하는 방법.

 

[ 장점 ]

  1. 코드의 간결성 : 복작한 식을 단순히 표현 가능.
  2. 지연연산 수행 : 지연실행 함으로 불필요한 연산을 최소화한다.
    1. 지연실행이란 프로그램이 실행될 때 메소드가 메모리에 올라가 있는 상태가 아닌 코드에서 필요할때만 실행되는 (Lazy Loading)것을 의미한다
  3. 병렬처리 가능 : 멀티쓰레드를 활용하여 병렬처리 가능. (Stream 이용)

[ 단점 ]

  1. 람다식의 호출이 까다롭다.
  2. 람다 Stream 사용 시 for, while 문보다 비효율적일 수 있다.
    1. for, while 문은 break를 통해 탈출조건을 만들 수 있지만 stream.forEach()의 경우 조건이 만족됬다할지라도 끝까지 검사를 해야되는 경우가 생김
  3. 람다식을 너무 많이 사용하게 되면 가독성을 떨어트릴 수 있다.

 

 

[ 예제 ]

// 기존 자바 문법

new Thread(new Runnable() {
   @Override
   public void run() { 
      System.out.println("Welcome Heejin blog"); 
   }
}).start();

// 람다식 사용

new Thread(()->{
      System.out.println("Welcome Heejin blog");
}).start();

 

[ 함수 ]

람다를 함수형 인터페이스로 사용될 수 있는데, Interface의 메소드를 정의하고 메인 함수에서 구현하는 방식으로 사용할 수 있다.

 

@FunctionalInterface
interface Math {
    public int Calc(int first, int second);
}

public static void main(String[] args){

   Math plusLambda = (first, second) -> first + second;
   System.out.println(plusLambda.Calc(4, 2));
   
 }

 

[ Stream ] 

  1. 데이터를 변경하지 않는다. ( 읽기만 가능 )
  2. 일회성이다. ( 다시 사용하려면 스트림을 다시 생성해야 함 )
  3. 지연 연산을 수행한다. 
  4. 병렬 실행 가능
// 리스트 컬렉션
List<Integer> list = Array.asList(0,1,2);
Stream<Integer> intStream = list.stream();

// 배열로 생성
Stream<String> stringStream = Stream.of(new String[] {a,b,c}); 

// 람다 사용
Stream<Integer> oddStream = Stream.iterate(1, n->n+2);
Stream<Double> randomStream = Stream.generate(Math::random);

 

[ 중간 연산, 최종 연산 ]

연산결과가 Stream으로 반복적으로 적용가능한 중간연산과 연산결과가 Stream이 아닌 다른 타입인 경우 최종적으로 한번만 적용할 수 있어 최종연산이라고 함.

String [] arr = { "a1", "a2", "b1" };
Stream<String> stream = Stream.of(arr); // 문자열 배열이 소스인 스트림
Stream<String> filter = stream.filter(); // 걸러내는 함수로 중간연산에 해당
Stream<String> distinct = stream.distinct(); // 중복을 제거하는 함수로 중간연산에 해당
Stream<String> sorted = stream.sort(); // 정렬함수로 중간연산에 해당
Stream<String> limited = stream.limit(x); // x번째까지 자르는 함수로 중간연산에 해당

int total (다른 변수타입) = stream.count(); // 갯수 세기로 int타입 반환으로 최종연산에 해당

 

 

[ 중간연산 ]

  1. 스트림 자르기 
    1. skip(x) : 앞에서 x번 건너뛰기
    2. limit(x) : x 이후로 요소 잘라내기 
  2. 스트림 요소 걸러내기
    1. filter(조건) : 조건에 맞지 않는 요소 제거
    2. distinct() : stream 내 중복 제거.
  3. 스트림 정렬하기
    1. sorted() : 기본 정렬
    2. sorted(Comparator<>) : 지정된 Comparator로 정렬 역시 가능
  4. 스트림 요소 변환학
    1. map() 
Stream<File> fileStream = Stream.of(new File("aaa.java"), new File("bbb.java"));

// Type 변환
Stream nameStream = fileStream.map(File::getName);

// example
fileStream.map(File.getName) // Stream<File> -> Stream<String>
		 .filter(s->s.indexof('.') != -1) // 확장자 없는 것 제외 Stream<String> -> Stream<String>
         .map(s->s.substring(s.indexOf('.')+1) // 확장자 받기 Stream<String> -> Stream<String>
         .map(String::toUpperCase) // 대문자 변환 Stream<String> -> Stream<String>
         .distinct() // 중복제거 Stream<String> -> Stream<String>
         .forEach(System.out::print); // 최종 연산

 

[ 최종 연산 ]

  1. 모든 요소에 지정된 작업 수행
    1. forEach() : 병렬스트림의 경우 순서 보장 안됨.
    2. forEachOredered() : 병렬스트림의 경우에도 순서가 보장.
  2. 스트림을 배열로 저장
    1. toArray() :
      1. Class [] className = classStream.toArray(); ==> 오류 발생
      2. Class [] className = classStream.toArray(Class[]::new);
      3. Object [] className = classStream.toArray();
  3. 조건 검사 
    1. allMatch() : 모든 요소가 조건을 만족시킨다면 true 리턴
    2. anyMatch() : 어떤 요소라도 조건을 만족시킨다면 true 리턴
    3. noneMatch() : 모든 요소가 조건을 만족시키지 못한다면 true리턴
    4. findFirst() : 조건을 만족하는 첫 번째 요소를 반환, sequential Stream
    5. findAny() : 조건을 만족하는 요소 중 하나 반환, parallelStream
  4. 통계 정보
    1. getCount() : Long Type 
    2. getSum() : Integer Type
    3. getAverage() : OptionalDouble Type
    4. getMax() : OptinalInteger Type
    5. getMin() : OptinalInteger Type
  5. Collector 이용
    1. collect() : Collector를 매개변수로 하는 스트림의 최종 연산, Object collect (Collector collector) 
    2. Collector : collect에 필요한 메소드를 정의해놓은 인터페이스, public interface Collector< 생략 >
    3. Collectors : 다양한 기능의 Collector를 구현한 클래스를 제공한다.   
      1. 스트림을 컬렉션으로 변환
        1. toList() : Stream<T>를 List<T>으로 변환한다.
        2. toMap() : Stream<T>를 MAP<T, C>로 변환한다.
        3. toCollection() : Stream<T>를 ArrayList<T> 로 변환한다.

 

[ Optional ]

 

개발 중 가장 많이 발생하는 에러 중 하나로 NPE (NullPointException) 이 존재한다. 

변수가 많으면 null 검사하는 코드의 복잡도가 올라간다.

JAVA8 이상으로 Optional<T> 클래스를 사용할 수 있게 되었고 NPE을 방지할 수 있도록 도우며 null이 올 수 있는 값을 감싸는 Wrapper Class로 참조하더라도 에러가 발생하지 않으며 클래스의 메소드를 사용할 수 있다.

 

public final class Optional<T> {

    private static final Optional<?> EMPTY = new Optional<>();
    private final T value;
    
    private Optional() {
        this.value = null;
    }
    
     public T orElse(T other) {
        return value != null ? value : other;
    }

    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }
}
Optional<String> optional = Optional.empty();

System.out.println(optional); // Optional.empty
System.out.println(optional.isPresent()); // false

// Optional의 value는 절대 null이 아니다.
Optional<String> optional = Optional.of("MyName");

// Optional의 value는 값이 있을 수도 있고 null 일 수도 있다.
Optional<String> optional = Optional.ofNullable(getName());
String name = optional.orElse("값이 없습니다."); // 값이 없다면 "값이 없습니다" 를 리턴

List<String> nameList = Optional.ofNullable(getNames())
    .orElseGet(() -> new ArrayList<>());

  

[ orElse, orElseGet ]

 

Optional 클래스의 메소드에 정의되어 있듯이 orElse는 파라미터로 값을 전달받아 null 이라면 전달받은 값을 리턴하며 null 이 아니라면 orElse() 내용을 출력한다 하지만 orElseGet은 null 이 아니라면 내용을 출력하지 않는다.

public void findUserEmailOrElse() {
    String userEmail = "Empty";
    String result = Optional.ofNullable(userEmail)
    	.orElse(getUserEmail());
        
    System.out.println(result);
    // getUserEmail() Called
    // Empty
}

public void findUserEmailOrElseGet() {
    String userEmail = "Empty";
    String result = Optional.ofNullable(userEmail)
    	.orElseGet(this::getUserEmail);
        
    System.out.println(result);
    // Empty
}

public void findUserEmailOrElseGet() {
    String result = Optional.ofNullable(null)
    	.orElseGet(this::getUserEmail);
        
    System.out.println(result);
    // getUserEmail() Called
    // mangkyu@tistory.com    
}

private String getUserEmail() {
    System.out.println("getUserEmail() Called");
    return "mangkyu@tistory.com";
}

 

 

[ 참고자료 ]

 

[JAVA]람다와 스트림(Lambda & Stream)

1. 람다식(Lambda Expression) 1.1. 람다식이란? - 함수(메서드)를 간단한 식으로 표현하는 방법    - 익명 함수(이름이 없는 함수) - 함수와 메서드의 차이 - 근본적으로 동일. 함수는 일반적 용어. 메서

bombichun.tistory.com

 

[Java] Optional이란? Optional 개념 및 사용법 - (1/2)

이번에는 Java8부터 지원하는 Optional 클래스에 대해 알아보도록 하겠습니다. 1. Optional이란? Optional 개념 및 사용법 [ NPE(NullPointerException) 이란? ] 개발을 할 때 가장 많이 발생하는 예외 중 하나..

mangkyu.tistory.com

 

[JAVA] 람다식(Lambda)의 개념 및 사용법

람다함수란? 람다 함수는 프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous functions)를 지칭하는 용어입니다. 현재 사용되고 있는 람다의 근간은 수학과 기초 컴퓨터과학 분야에서의 

khj93.tistory.com

 

'🍃 스프링' 카테고리의 다른 글

[SPRING] 남의 코드 이해하기  (1) 2023.05.07
[SPRING] Filter, Interceptor  (0) 2022.10.06
[SPRING] Dispatcher Servlet  (0) 2022.10.02
[SPRING] JPA, ORM  (0) 2022.09.27
[SPRING] Maven, Gradle  (0) 2022.09.24