The A :

728x90
반응형

Today I Lean

애너테이션, 람다, 스트림

 

 

 

 

 

학습목표 및 개념정리

# 애너테이션 (Annotation).reduce((a,b) -> a + b)

1+2 = 3+3 = 6+4 = 10+5 = 15

// 계산이 끝나면 위로 올라가서

.map(element -> element * 2)

15*2 = 30

// 초기값이 없는 reduce() : 30

- 애너테이션의 개념을 알 수 있다.

- 표준 애너테이션과, 메타 애너테이션을 이해하고 사용할 수 있다.

- 사용자 정의 애너테이션을 정의하는 기초적인 문법을 이해한다.

 

# 람다 (Lambda)

- 람다식을 알고, 사용법을 이해한다.

- 함수형 인터페이스를 통해 람다를 다루는 방법을 알 수 있다.

- 람다식을 메서드 참조 방식으로 변환할 수 있다.

 

# 스트림 (Stream)

- 스트림의 핵심적인 개념과 특징을 이해할 수 있다.

- 스트림의 생성, 중간, 최종 연산의 세 단계로 구성된 스트림 파이프라인을 이해하고 활용할 수 있다.

- 스트림의 주요 메서드를 활용해 원하는 데이터 처리를 할 수 있다.

 

 

 

배운 것

# 애너테이션 (Annotation)

1. 애너테이션 이란?

 : 소스가 컴파일 되거나 실행될 때 컴파일러 및 다른 프로그램에게 필요한 정보를 전달해 주는 문법 요소

 → @로 시작하며 클래스, 인터페이스, 필드, 메서드 등에 붙여서 사용 가능

 

 

 

2. 애너테이션의 종류

- 표준 애너테이션
 : @Override

 → 메서드 앞에만 붙일 수 있는 애너테이션 

 → 선언한 메서드가 상위 클래스의 메서드를 오버라이딩 하거나 추상 메서드를 구현한다는 것을 컴파일러에게 알려줌

 

 : @Deprcated

 → 기존 사용하던 기술이 다른 기술로 대체 되어 기존의 기술을 더이상 사용하지 않도록 유도할 때 사용

 → 기존의 코드가 다른 코드와의 호환성 때문에 삭제하기는 곤란하지만, 사용하는 것을 권장하지는 않을 때 

 

 : @SuppressWarnings

 → 컴파일 경고 메시지가 나타나지 않도록 함

 → 뒤에 ()를 붙이고 그 안에 억제하고자 하는 경고 메시지를 지정할 수 있음

 

 : @Functionallnterface

 → 인터페이스를 선언 할 때, 컴파일러가 함수형 인터페이스의 선언이 바르게 선언되었는지 확인하도록 함

 

 

- 메타 애너테이션 (mete - annotation)

 → 애너테이션을 정의하는 데에 사용되는 애너테이션


 : @Target

 → 애너테이션을 적용할 대상을 지정하는데에 사용

대상 타입 적용 범위
ANNOTATION_TYPE 애너테이션
CONSTRUCTOR 생성자
FIELD 필드(멤버변수, 열거형 상수)
LOCAL_VARIABLE 지역변수
METHOD 메서드
PACKAGE 패키지
PARAMETER 매개변수
TYPE 타입(클래스, 인터페이스, 열거형)
TYPE_PARAMETER 타입 매개변수
TYPE_USE 타입이 사용되는 모든 대상

 : @Documented

 → 애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 하는 애너테이션 설정

 

 : @Retention

 → 특정 애너테이션의 지속 시간을 결정하는데에 사용

유지 정책 설명
SOURCE 소스 파일에 존재, 클래스파일에는 존재하지 않음
CLASS 클래스 파일에 존재, 실행 시에 사용 불가, 기본값
RUNTIME 클래스 파일에 존재, 실행 시에 사용가능

 : @Repeatable

 → 애너테이션을 여러 번 붙일 수 있도록 허용한다는 의미를 지님

 → 같은 이름의 애너테이션이 여러 번 적용 될 수 있기 때문에, 하나로 묶어 주는 애너테이션도 별도로 작성 해야 함

 

 

- 사용자 정의 애너테이션

 → 사용자가 직접 애너테이션을 정의해서 사용

 → 애너테이션은 java.lang.annotation인터페이스를 상속받기 때문에 다른 클래스나 인터페이스를 상속받을 수 없음

 

 

 

# 람다 (Lambda)

1. 람다식(Lambda Expression) 이란?

 : 함수형 프로그래밍 기법을 지원하는 자바의 문법요소

 : 메서드를 하나의 식(expression)으로 표현한 것

 → 그렇기 때문에 코드를 매우 간결하고 명확하게 표현이 가능

 

- 기본 문법
 : 람다식 에서는 기본적으로 반환타입과 이름을 생략 할 수 있음

 → 익명 함수 (anonymous function) 라고도 부름

        // 기존 방식
        int sum(int num1, int num2) {
            return num1 + num2;
        }
        // 람다식
        (int num1, int num2) -> {return num1 + num2;}   //반환타입과 메서드명 제거 + 화살표 추가
        (int num1, int num2) -> num1 + num2
        // 메서드 바디에 실행문이 하나만 있다면 중괄호{}와 return문, ;세미콜론 까지 생략 가능
        // 단, 실행문이 두개 이상이라면 생략 물가능
        (num1, num2) -> num1 + num2
        // 매개변수 타입을 인터페이스를 통해 유추할 수 있다면 생략 가능



        // 기존 방식
        int example2() {
            return 10;
        }
        // 람다식
        () -> {return 10;}

 

2. 함수형 인터페이스

 : 람다식 또한 하나의 객체

 : 정확히는 이름이 없기 때문에 익명 객체 라고 함

 

- 익명 객체
 : 익명 클래스를 통해 만들 수 있음

 → 익명 클래스란 객체의 선언과 생성을 동시에 하여 오직 하나의 객체를 생성, 단 한번만 사용되는 일회용 클래스

 

- 함수형 인터페이스

 : 기존의 인터페이스 문법을 활용하여 람다식을 다루는 것이라고 할 수 있음

 : 단 하나의 추상 메서드만 선언될 수있음, 이는 람다식과 인터페이스의 메서드가 1:1로 매칭되어야 하기 때문

public class LamdaExample1 {
    public static void main(String[] args) {
    
	ExampleFunction exampleFunction = (num1, num2) -> num1 + num2;
        System.out.println(exampleFunction.sum(10,15));
}

@FunctionalInterface // 컴파일러가 인터페이스가 바르게 정의되었는지 확인하도록 합니다. 
interface ExampleFunction {
		int sum(int num1, int num2);
}

// 출력값
25

 

* 여러개의 람다식을 선언하려면 메소드도 여러개가 있어야 할까?

하나의 인터페이스 메서드 안에 하나의 추상 메서드를 가지는 함수형 인터페이스에 대해

여러 개의 람다식을 할당할 때에는,

람다식 선언 후,

해당 람다식을 사용하여 함수형 인터페이스 객체를 생성하는 방법으로 사용 가능

// 사용자 정의 함수형 인터페이스를 선언
@FunctionalInterface
public interface MyFunctionalInterface {
    int calculate(int x, int y);	// int타입 매개변수 2개
}

// 메서드랑 시그니처가 일치하면 여러 람다식을 만들 수있음
public class Main {
    public static void main(String[] args) {
        // 함수형 인터페이스를 구현하는 람다식을 선언
        // int 타입 매개변수 2개 사용
        MyFunctionalInterface add = (x, y) -> x + y;
        MyFunctionalInterface subtract = (x, y) -> x - y;
        MyFunctionalInterface multiply = (x, y) -> x * y;

        // 람다식을 사용하여 함수형 인터페이스의 메서드를 호출하고, 결과값을 변수에 저장
        // int 타입 매개변수 2개에 값 할당
        int result1 = add.calculate(5, 3); // 5 + 3 = 8
        int result2 = subtract.calculate(5, 3); // 5 - 3 = 2
        int result3 = multiply.calculate(5, 3); // 5 * 3 = 15

        // 결과값 출력
        System.out.println(result1);
        System.out.println(result2);
        System.out.println(result3);
    }
}

 

- 매개변수과 리턴값이 없는 람다식

@FunctionalInterface
interface MyFunctionalInterface {  // 사용자 정의 함수형 인터페이스를 선언
    void accept();  // 하나의 추상 메서드만 선언
}

public class MyFunctionalInterfaceExample { 
    public static void main(String[] args) throws Exception {
    
        MyFunctionalInterface example = () -> System.out.println("accept() 호출");  // 람다식을 사용하여 함수형 인터페이스를 구현
        example.accept();  // 람다식을 호출하여 실행
    }
}

// 출력값
accept() 호출

 

- 매개변수가 있고 리턴값이 없는 람다식

@FunctionalInterface
public interface MyFunctionalInterface {	// 사용자 정의 함수 인터페이스 선언
    void accept(int x);	// 하나의 추상 인터페이스 선언
}

public class MyFunctionalInterfaceExample {

    public static void main(String[] args) throws Exception {

        MyFunctionalInterface example;	// MyFunctionalInterface 타입의 변수 example 선언

        example = (x) -> {	// 람다식 구현 (블록 구문)
            int result = x * 5;
            System.out.println(result);	// result 출력
        };
        example.accept(2);	// 위 람다식에 매개변수 할당

        example = (x) -> System.out.println(x * 5);	// 람다식 구현 (식 구문)
        example.accept(2);	// 위 람다식에 매개변수 할당
    }
}

// 출력값
10
10

 

- 리턴값이 있는 람다식

public class MyFunctionalInterfaceExample {

    public static void main(String[] args) throws Exception {

        MyFunctionalInterface example;

        example = (x, y) -> {
            int result = x + y;
            return result;
        };
        int result1 = example.accept(2, 5);
        System.out.println(result1);
        

        example = (x, y) -> { return x + y; };
        int result2 = example.accept(2, 5);
        System.out.println(result2);
       

        example = (x, y) ->  x + y;
        //return문만 있으면, 중괄호 {}와 return문 생략 가능
        int result3 = example.accept(2, 5);
        System.out.println(result3);
       

        example = (x, y) -> sum(x, y);
        //return문만 있으면, 중괄호 {}와 return문 생략 가능
        int result4 = example.accept(2, 5);
        System.out.println(result4);
 
    }

    public static int sum(int x, int y){
        return x + y;
    }
}

//출력값
7
7
7
7

 

 

3. 메서드 레퍼런스

 : 메서드 참조는 람다식에서 불필요한 매개변수를 제거할 때 사용

 : 즉, 람다식으로 간단해진 익명 개체를 더욱더 간단하게 사용하고 싶을 때

(left, right) -> Math.max(left, right)	// 입력값과 출력값의 반환타입을 쉽게 유추할 수 있을 때
Math :: max 	// 이렇게 메서드 참조를 사용하기도 함

 → 인터페이스의 추상 메서드가 어떤 매개 변수를 가지고, 리턴타입이 무엇인가에 따라 달라짐

 → 정적 메서드, 인스턴스 매서드, 생성자 모두 참조 가능

 

 

- 정적 메서드와 인스턴스 메서드 참조

 * 정적 메서드

 → 클래스 이름 뒤에 :: 기호를 붙이고 정적 메서드 이름을 기술

클래스 :: 메서드

 * 인스턴스 메서드

 → 객체 생성 후 참조 변수 뒤에 :: 기호를 붙이고 인스턴스 메서드 이름을 기술

참조 변수 :: 메서드
public class Calculator {
  public static int staticMethod(int x, int y) {
                        return x + y;
  }

  public int instanceMethod(int x, int y) {
   return x * y;
  }
}

import java.util.function.IntBinaryOperator;

public class MethodReferences {
  public static void main(String[] args) throws Exception {
    IntBinaryOperator operator;	// 메서드 선언

    operator = Calculator::staticMethod;	// operator은 Calculator::staticMethod 메서드를 참조함
    System.out.println("정적메서드 결과 : " + operator.applyAsInt(3, 5));	// 매개변수 대입

    Calculator calculator = new Calculator();
    operator = calculator::instanceMethod;
    System.out.println("인스턴스 메서드 결과 : "+ operator.applyAsInt(3, 5));
  }
}
/*
정적메서드 결과 : 8
인스턴스 메서드 결과 : 15
*/

 

 

- 생성자 참조

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

 → 클래스 이름 뒤에 :: 기호륿 붙이고 new 연산자를 기술

클래스 :: new

 → 생성자가 오버로딩 되어 여러개 있으면, 컴파일러는 함수형 인터페이스의 추상 메서드와 동일한 매개 변수 타입과 개수가 있는 생성자를 찾아 실행 

public class Member {
  private String name;
  private String id;

  public Member() {
    System.out.println("Member() 실행");
  }

  public Member(String id) {
    System.out.println("Member(String id) 실행");
    this.id = id;
  }

  public Member(String name, String id) {
    System.out.println("Member(String name, String id) 실행");
    this.id = id;
    this.name = name;
  }

  public String getName() {
    return name;
  }

public String getId() {
    return id;
  }
}


import java.util.function.BiFunction;
import java.util.function.Function;

public class ConstructorRef {
  public static void main(String[] args) throws Exception {
    Function<String, Member> function1 = Member::new;
    Member member1 = function1.apply("kimcoding");

    BiFunction<String, String, Member> function2 = Member::new;
    Member member2 = function2.apply("kimcoding", "김코딩");
  }
}

/*
Member(String id) 실행
Member(String name, String id) 실행
*/

 

 

 

# 스트림 (Stream)

1. 스트림 이란?

 : 배열, 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자

 : 다양한 데이터 소스로 부터 스트림을 만들고 표준화된 방법으로 다룰 수 있음

 : 데이터 소스를 다루는 풍부한 메서드를 제공

 → 선언형 프로그래밍 방식으로 데이터 처리가 가능함 (인간 친화적이고 직관적인 코드 작성)

선언형 프로그래밍 (Declarative Programming) 명령형 프로그래밍 (Imperative Programming)
무엇을 위한 코드인지가 중요 어떻게 코드를 작성할지가 중요
내부의 동작 원리를 모르더라도
어떤 코드가 어떤 역할을 하는지 직관적으로 이해 가능
코드 한줄 한줄의 동작원리가 중요
순차적이고 세세하게 규정
어떻게 에 해당하는 부분은 추상화 되어 있음  

 → 데이터 소스가 무엇이냐에 관계없이 같은 방식으로 데이터를 가공 / 처리가 가능

 → 배열이냐 컬렉션이냐에 관계업이 하나의 통합된 방식으로 데이터를 다룰 수 있음

 

- 스트림의 특징
 : 스트림 처리 과정은 생성, 중간 연산, 최종 연산 세 단계의 파이프라인으로 구성될 수 있다.

 → 최종 연산 후 다시 데이터 처리가 필요하다면 스트림을 재생성 해야함(스트림 당 단 한 번의 연산만 가능)

 : 스트림은 원본 데이터 소스를 변경하지 않는다 (read-only).

 → 스트림은 데이터를 읽어와서 생성된 스트림 안에서만 변경 또는 처리를 함

 : 스트림은 일회용이다(onetime-only).

 : 스트림은 내부 반복자이다.

 → 외부반복자는 (for문, Iterator-while문)개발자가 코드로 직접 컬렉션의 요소를 반복해서 가져오는 코드패턴을 의미

 → 반면 스트림은 데이터 처리코드만 컬렉션 내부로 주입해서, 여러번 요소를 불러오지 않음

 

 

 

2. 스트림 생성

- 배열 스트림 생성

 : Arrays.stream(), Stream.of()

// Arrays.stream()
public class StreamCreator {
   
       public static void main(String[] args) {
           // 문자열 배열 선언 및 할당
           String[] arr = new String[]{"김코딩", "이자바", "박해커"};
   
           // 문자열 스트림 생성
           Stream<String> stream = Arrays.stream(arr);
   
           // 출력
           stream.forEach(System.out::println);
   
       }
   }
   
// Stream.of()
public class StreamCreator {

public static void main(String[] args) {
    // 문자열 배열 선언 및 할당
    String[] arr = new String[]{"김코딩", "이자바", "박해커"};

    // 문자열 스트림 생성
    Stream<String> stream = Stream.of(arr);

    // 출력
    stream.forEach(System.out::println);

    }
}

 : 두 메서드 모두 같은 값을 출력하기 때문에 배열로 스트림을 생성할 때는 둘 중 더 편한 메서드를 임의로 선택해서 사용할 수 있음

 

- 컬렉션 스트림 생성

 : 컬렉션 타입으로부터 확장된 하위클래스 List와 set을 구현한 컬렉션 클래스들은 모두 stream() 메서드를 사용하여 스트림생성이 가능

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamCreator {

    public static void main(String[] args) {
				// 요소들을 리스트
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
        Stream<Integer> stream = list.stream();

        stream.forEach(System.out::print);
    }
}

//출력값
1234567

 

- 임의의 수 스트림 생성

 : 난수를 생성하는 Random 클래스 안에는 해당 타입의 난수들을 반환하는 스트림을 생성하는 메서드 들이 정의되어 있음

import java.util.Random;
import java.util.stream.IntStream;

public class StreamCreator {

    public static void main(String[] args) {
			
        IntStream ints = new Random().ints(5);				// 랜덤한 정수 5개 생성
	IntStream ints = new Random().ints().distinct().limit(5);	// 중복 제외 랜덤한 정수 5개 생성
        IntStream ints = new Random().ints(1, 11).limit(5);		// 1~10 까지의 랜덤한 정수 5개 생성
        IntStream ints = new Random().range(1, 11).limit(5);		// 1~10 까지의 정수 중 앞의 5개 생성
        IntStream ints = new Random().range(1, 11);			// 1~10까지의 정수가 순차적으로 생성
        IntStream ints = new Random().rangeClosed(1, 11);		// 1~11까지의 정수가 순차적으로 생성
        
        ints.forEach(System.out::println);
    }
}

 

 

3. 스트림 중간 연산

 : 생성 이후 수행

 

- 필터링

 : 조건에 맞는 데이터만 정제하는 역할을 함

 

* distinct() [ de'stiNG(k)t ]

 → Stream의 요소들에 중복된 데이터가 존재하는 경우, 중복을 제거하기 위해 사용

*filter()

 → Stream에서 조건에 맞는 데이터만을 정제하여, 더 작은 컬렉션을 만들어 냄.

 → 매개값으로 조건 (Predicate)를 주고, 조건이 참이 되는 요소만을 필터링 

 → 조건은 람다식을 사용하여 정의 가능

import java.util.Arrays;
import java.util.List;

public class FilteringExample {
    public static void main(String[] args) throws Exception {

        List<String> names = Arrays.asList("김코딩", "이자바", "박해커", "김코딩", "박해커");

        names.stream()
                .distinct() //중복 제거
                .forEach(element -> System.out.println(element));
        System.out.println();

        names.stream()
                .filter(element -> element.startsWith("김")) // 김씨 성을 가진 요소만 필터링 
                .forEach(element -> System.out.println(element));
        System.out.println();

        names.stream()
                .distinct() //중복제거
                .filter(element -> element.startsWith("김")) // 김씨 성을 가진 요소만 필터링 
                .forEach(element -> System.out.println(element));
    }
}

// 출력값
김코딩
이자바
박해커

김코딩
김코딩

김코딩

 

- 매핑

* map()

 → 스트림 내 요소들 중에서 원하는 필드만 추출하거나 특정 형태로 변환 할 때 사용

import java.util.Arrays;
import java.util.List;

public class IntermediateOperationExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("kimcoding", "javalee", "hackerna", "luckyguy");
        names.stream()
                .map(element -> element.toUpperCase()) // 요소들을 하나씩 대문자로 변환
                .forEach(element->System.out.println(element));
             
             
             
        List<Integer> list = Arrays.asList(1, 3, 6, 9);

        // 각 요소에 3을 곱한 값을 반환
        list.stream().map(number -> number * 3).forEach(System.out::println);
    }
}

// 출력값
KIMCODING
JAVALEE
HACKERNA
LUCKYGUY

// 출력값
3
6
18
27

 

* flatMap()

 → 중첩구조의 배열을 제거하고 단일 컬렉션(Stream<String>)으로 만들어 줌

String[][] namesArray = new String[][]{{"박해커", "이자바"}, {"김코딩", "나박사"}};

Arrays.stream(namesArray).flatMap(Arrays::stream).forEach(System.out::println);

 

 

- 정렬

* sorted()

 → 정렬시 사용

 → 괄호 안에 Comparator 라는 인터페이스에 정의된 static 메서드와 디폴트 메서드를 사용하여 간편하게 정렬 가능

 → 만약 괄호 안에 아무것도 없으면 오름차순으로 정렬 됨

import java.util.Arrays;
import java.util.List;

public class IntermediateOperationExample {
    public static void main(String[] args) {
				// 동물들의 이름을 모아둔 리스트 
        List<String> animals = Arrays.asList("Tiger", "Lion", "Monkey", "Duck", "Horse", "Cow");
				
				// 인자값 없는 sort() 호출 (오름차순 정렬)
        animals.stream().sorted().forEach(System.out::println);
        
        		// 인자값에 Comparator 인터페이스에 규정된 메서드 사용 (내림차순 정렬)
        animals.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
        
    }
}

// 오름차순 출력 값
Cow
Duck
Horse
Lion
Monkey
Tiger

// 내림차순 출력 값
Tiger
Monkey
Lion
Horse
Duck
Cow

 

- 기타

* skip()

 → 스트림의 일부 요소를 건너뜀

// 앞의 5개의 숫자를 건너뛰고 숫자 6부터 출력
intStream.skip(5).forEach(System.out::println);

 

* limit()

 → 스트림의 일부를 자름 (원하는 수만큼 출력)

// 앞에서부터 5개의 숫자만 출력
intStream.limit(5).forEach(System.out::println);

 

*peek()

 → forEach()와 마찬가지로 요소를 순회하며 특정 작업을 수행

 → forEach()와는 달리 중간연산자 이기 때문에 여러번 연결하여 사용 가능

 → 주로 디버깅 용도로 사용

IntStream intStream3 = IntStream.of(1, 2, 2, 3, 3, 4, 5, 5, 7, 7, 7, 8);

int sum = intStream3.filter(element -> element % 2 == 0)
        .peek(System.out::println)
        .sum();

System.out.println("합계 = " + sum);

// 출력값
2
2
4
8
합계 = 16

 

 

 

4. 스트림의 최종 연산

 : 실행 후 해당 스트림은 닫히고 모든 연산이 종료 됨

 

- 기본 집계

 : 숫자와 관련된 기본적인 집계는 대부분 최종 연산자

* sum(), count(), average(), max(), min()

import java.util.Arrays;

public class TerminalOperationExample {
    public static void main(String[] args) {
        // int형 배열 생성
        int[] intArray = {1,2,3,4,5};

        // 카운팅
        long count = Arrays.stream(intArray).count();
        System.out.println("intArr의 전체 요소 개수 " + count);

        // 합계
        long sum = Arrays.stream(intArray).sum();
        System.out.println("intArr의 전체 요소 합 " + sum);

        // 평균
        double average = Arrays.stream(intArray).average().getAsDouble();
        System.out.println("전체 요소의 평균값 " + average);

        // 최대값
        int max = Arrays.stream(intArray).max().getAsInt();
        System.out.println("최대값 " + max);

        // 최소값
        int min = Arrays.stream(intArray).min().getAsInt();
        System.out.println("최소값 " + min);

        // 배열의 첫 번째 요소 
        int first = Arrays.stream(intArray).findFirst().getAsInt();
        System.out.println("배열의 첫 번째 요소 " + first);
    }
}

// 출력값
intArr의 전체 요소 개수 5
intArr의 전체 요소 합 15
전체 요소의 평균값 3.0
최대값 5
최소값 1
배열의 첫 번째 요소 1

 → 기본연산자 뒤에 붙는 .getAsInt()나 .getAsDouble()은 스트림과는 관계 없는 메서드

 → 따라서 최종 연산자 뒤에 있을 수 있음! (최종연산이 끝난 후 객체로 반환되는 값을 래퍼클래스객체가 아닌 기본형으로 변환 하기 위해 마지막에 붙는 것)

 

 

- 매칭

 : 조건식 람다 Predicate를 매개변수로 넘겨 스트림의 각 데이터 요소가 특정한 조건을 충족하는지 여부를 검사하여 그 결과를 boolean 값으로 반환

 

* allMatch()

 → 모든 요소가 조건을 만족하는지 여부를 판단

* noneMatch()

 → 모든 요소가 조건을 만족하지 않는지 여부를 판단

* anyMatch()

 → 하나라도 조건을 만족하는 요소가 있는지 여부를 판단

import java.util.Arrays;

public class TerminalOperationExample {
    public static void main(String[] args) throws Exception {
        // int형 배열 생성
        int[] intArray = {2,4,6};

        // allMatch()
        boolean result = Arrays.stream(intArray).allMatch(element-> element % 2 == 0);
        System.out.println("요소 모두 2의 배수인가요? " + result);

        // anyMatch()
        result = Arrays.stream(intArray).anyMatch(element-> element % 3 == 0);
        System.out.println("요소 중 하나라도 3의 배수가 있나요? " + result);

        // noneMatch()
        result = Arrays.stream(intArray).noneMatch(element -> element % 3 == 0);
        System.out.println("요소 중 3의 배수가 하나도 없나요? " + result);
    }

}

// 출력값
요소 모두 2의 배수인가요? true
요소 중 하나라도 3의 배수가 있나요? true
요소 중 3의 배수가 하나도 없나요? false

 

- 요소 소모

* reduce()

 → 스트림의 요소를 줄여나가면서 연산을 수행하고 최종적인 결과 반환

 → 첫번째와 두번째 요소를 가지고 연산을 수행 => 세번째 요소를 가지고 연산 수행 하는 식으로 연산이 끝날 때 까지 반복

 → 첫번째 매개변수 identity(특정 연산을 시작할 때 설정되는 초기값), 두번째 accumulator(각 요소를 연산하여 나온누적된 결과값을 생성하는데 사용하는 조건식)

import java.util.Arrays;

public class TerminalOperationExample {
    public static void main(String[] args) throws Exception {
        int[] intArray = {1,2,3,4,5};

        // sum()
        long sum = Arrays.stream(intArray).sum();
        System.out.println("intArray 전체 요소 합: " + sum);

        // 초기값이 없는 reduce()
        int sum1 = Arrays.stream(intArray)
                .map(element -> element * 2)
	                .reduce((a , b) -> a + b)
                .getAsInt();
        System.out.println("초기값이 없는 reduce(): " + sum1);

        // 초기값이 있는 reduce()
        int sum2= Arrays.stream(intArray)
                .map(element -> element * 2)
                .reduce(5, (a ,b) -> a + b);
        System.out.println("초기값이 있는 reduce(): " + sum2);
    }
}

// 출력값
intArray 전체 요소 합: 15
초기값이 없는 reduce(): 30
초기값이 있는 reduce(): 35

// 초기값이 없는 reduce()

// a : 누적된 값, b : 새롭게 더해질 다음 값

.reduce((a,b) -> a + b) 

1+2 = 3+3 = 6+4 = 10+5 = 15

// 계산이 끝나면 위로 올라가서

.map(element -> element * 2)

15*2 = 30

// 초기값이 없는 reduce() : 30

 

// 초기값이 있는 reduce()

.reduce(5, (a,b) -> a + b)

1+2 = 3+3 = 6+4 = 10+5 = 15

// 계산이 끝나면 위로 올라가서

.map(element -> element * 2)

15*2 = 30

// 다시 내려와서 초기값 5를 더함

30+5 = 35

// 초기값이 있는 reduce() : 35

 

 

- 요소 수집

*collect()

 → 중간연산을 통한 요소들의 데이터 가공 후 요소들을 수집하는 최종 처리 메서드

 → collector 인터페이스 타입의 인자를 받아서 처리할 수 있음

 → 요소 그룹핑 및 분할 등의 기능도 제공함

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TerminalOperationExample {

    public static void main(String[] args) {
        // Student 객체로 구성된 배열 리스트 생성 
        List<Student> totalList = Arrays.asList(
                new Student("김코딩", 100, Student.Gender.Male),
                new Student("박해커", 80, Student.Gender.Male),
                new Student("이자바", 90, Student.Gender.Female),
                new Student("나미녀", 60, Student.Gender.Female)
        );
        
        // 스트림 연산 결과를 Map으로 반환
        Map<String, Integer> maleMap = totalList.stream()
                .filter(s -> s.getGender() == Student.Gender.Male)
                .collect(Collectors.toMap(
                        student -> student.getName(), // Key
                        student -> student.getScore() // Value
                ));

        // 출력
        System.out.println(maleMap);
    }
}

class Student {
    public enum Gender {Male, Female};
    private String name;
    private int score;
    private Gender gender;

    public Student(String name, int score, Gender gender) {
        this.name = name;
        this.score = score;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public int getScore() {
        return score;
    }

    public Gender getGender() {
        return gender;
    }
}

// 출력값
{김코딩=100, 박해커=80}

 

 

- 기타

* findAny()

 

* findFirst()

 

* toArray()

 

 

 

*****

* 람다식 축약 표현 더 알아보기

 : 

 

* IntStream 기능

 → 

 

*forEach() 메서드

 → 데이터 소스의  요소를 순회하면서 람다식 안에 정의된 어떤 명령을 실행하는 데 사용하는 최종 연산자

 

* startsWith()메서드

element.startsWith("김")

 : String 클래스 매서드 중 하나

 → 괄호 안의 문자열이 특정 문자열 또는 문자열의 접두어로 시작하는지 여부를 판단하는 기능

String str = "김철수";
String prefix = "김";
boolean result = str.startsWith(prefix); // true (문자열 객체를 인자로 전달)
boolean result = str.startsWith("김"); // true (문자열 그 자체를 인자로 전달)

 

 

 

 

 

Tomorrow Chapter

# 파일 입출력 (I/O)

 

 

 


 

 

↓ 이전 글 ↓

 

[코드스테이츠] 05_01_TIL : 컬렉션 (Collection)

Today I Lean 컬렉션 (Collection) 역시나 실습보다는 이론공부가 그나마 쉽다. 실제로 사용하라고 하면 쉽지 않겠지만, 일.단. 오늘 배운 이론으로서는 이해할 수 있었다. 내일은 다른 분과 페어프로그

theflower01.tistory.com

 

↓ 코트스테이츠 부트캠프 관련 글 한번에 보기 ↓

 

'IT/코드스테이츠 부트캠프' 카테고리의 글 목록

Flower, Plant, Study

theflower01.tistory.com

728x90
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading