Stream API

2023. 11. 30. 14:01
728x90

: Java 8 버전에 소개된 데이터 모음을 다루는 방법.

 

원래 데이터의 모음이 있다면...? 

데이터 모음의 데이터가 하나씩 나와서 특정 작업(함수)의 목록을 거쳐 결과 데이터를 만들어내는 방식으로 동작한다.

~> 어떤 작업을 진행할지를 전달하려면 함수를 인자로 전달한다.

 

- 데이터 모음을 Stream으로 변환, 해당 데이터에 할 작업들을 선택, 그 결과를 필요한 형태로 반환하는 과정..

3단계

1) Stream 만들기 

2) Intermediate Operation

3. Terminal Operation 순서로 동작

 

> 훨씬 간결하게 코드 작성 가능.

작업을 가하는 것이 아니라 작업을 전달해주는 것! 

List<String> namesWithA = nameList.stream() //Stream을 만들고
				.filter(name -> name.contains("a")) //작업내역 전달
                		.sorted()
                        	.toList();	// 데이터를 가공해서 결과 반환
                            
                   
                   
// 같은 기능을 하는 stream 사용하지 않은 코드
List<String> nameList = new ArrayList<>();
...
List<String> namesWithA = new ArrayList<>();
for (String name : nameList) {
    if (name.contains("a")) namesWithA.add(name);
}
namesWithA.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println(namesWithA);

 

1. Functional Interface (함수형 인터페이스)

: 단 하나의 추상 메서드만 가지는 인터페이스.

- 만들어진 함수형 인터페이스를 바탕으로, 람다 함수(익명함수)를 만들 수 있음. 

// 함수형 인터페이스
@FunctionalInterface
public interface NoArgRetFunction {
    void noArgNoReturn();
}

 

1) 전통적 방식? 

// 인터페이스를 상속받은 클래스 생성하여 main에서 호출
public class NoArgRetFunctionImpl implements NoArgRetFunction {
    @Override
    public void noArgNoReturn() {
        System.out.println("Traditional Implementation");
    }
}

 

 

2) 익명 내부 클래스 사용

- 한 번만 쓰고 말 클래스를 ~ > 메서드 안에다 만들어 버린다.

- 익명 내부 클래스 (anonymous inner class) : 메서드 내부에서 즉석으로 새로운 구현체를 만들어 사용할 수 있음. 

// 익명 내부 클래스
        NoArgRetFunction anonymousClass = new NoArgRetFunction() {
            @Override
            public void noArgNoReturn() {
                System.out.println("anonymous inner class");
            }
        };

 

3) 람다 함수 사용

- 람다 표현식 : 매개변수를 ( )로, 함수 본문은 ->로 연결. 함수형 인터페이스는 어차피 구현할 메서드가 하나이므로 그 메서드만 표시하는 것. 

NoArgRetFunction lambda = () -> System.out.println("Lambda Expression");

final int a = 10;
int b= 20;
// 여러 줄은 { } 활용
// 람다 내부에서는 final 변수만 사용 가능 
// 함수의 기능이 예측이 불가해지기 때문에 변경 가능한 변수를 허용하지 않음.
NoArgRetFunction lambda = () -> {
	System.out.println(a);
    b=30; //에러
    System.out.println("Hello Lambda!");
    System.out.println("Multiline Lambda!");
};
lambda.noArgNoReturn();

 

- 메서드 직접 참조도 가능하다 (::)

public static void main(String[] args) {
    useFunction(String::length);
}

public static void useFunction(StringArgFunction function) {
    System.out.println(function.run("lorem ipsum"));
}

 

함수형 인터페이스 람다 표현식 실제 메서드
Function T -> R R apply(T t)
Predicate T -> boolean boolean test(T t)
Consumer T -> void void accept(T t)
Supplier () -> T T get()
Comparator (T, T) -> int int compare(T o1, T o2)

 

2. Stream 만들기

- 배열이나 Collection을 활용하면 가장 편하다.

- 배열 : Arrays.stream() / 원시타입은 제공되는 별도의 클래스를 활용

- 문자열 : IntStream /CharStream은 없음.

- Collection : stream() 메서드 

// String 
String[] nameArray = {"Alex", "Dave", "Chad", "Brad"};
Stream<String> nameArrStream = Arrays.stream(nameArray);

// 원시타입 스트림
int[] numbers = {1, 23, 14, 53, 22};
IntStream intStream = Arrays.stream(numbers);

// 문자열 스트림 : CharStream은 없음
IntStream charStream = "hellojava".chars();

// Collection
List<String> nameList = new ArrayList<>();
nameList.add("Chad");
nameList.add("Alex");
Stream<String> nameListStream = nameList.stream();

 

3. Intermediate Operations (Stream에 적용할 작업 전달)

: 각 데이터에 중간 작업을 진행한다.

- 다른 데이터를 만들거나(map), 일부 데이터만 골라내거나(filter), 정렬(sort) 등의 작업 진행.

- 메서드의 결과로 새로운 Stream을 반환.

- 그러면 다시 다음 작업을 메서드로 선택할 수 있다. (메서드 체이닝 가능)

- 원본 데이터는 그대로 남아있다. (데이터 조작이 아니라 선별해서 사용하는 것!)

Stream<String> nameListStream1
        = nameList.stream();
Stream<String> nameListStream2
        = nameListStream1.filter(name -> name.contains("a"));
Stream<String> nameListStream3
        = nameListStream2.sorted((o1, o2) -> o1.length() - o2.length());
  • filter : boolean을 반환하는 함수를 인자로 받음. 해당 함수에 인자로 전달되었을 때, true가 나오는 데이터 선별
  • map : 값을 인자로 받아 하나의 값을 반환. 반환값이 바뀌면 Stream 타입도 바뀜.
  • sort : 데이터 정렬. (Comparable을 기준으로 정렬 | Comparator를 람다식으로 구현 가능 | 미리 정해진 Comparator도 사용가능)
// filter
Stream<String> nameListStream = nameList.stream()
	.filter(name -> name.contains("a")); // a가 포함되면 true
    
    
// map
Stream<String> nameListStream = nameList.stream()
	.filter(name -> name.contains("a"))
    	.map(name -> name.toUpperCase()); // name을 대문자로 변환
        
nameList.stream()
.map(name -> name.length()); //각 단어의 길이로 이루어진 Stream 반환

nameList.stream()
.mapToInt(name -> name.length()); //각 단어의 길이로 이루어진 IntStream으로 반환
     
     
// sort
nameList.stream()
.sorted(); // 오름차순 정렬

nameList.stream()
.sorted(Comparator.reverseOrder()); //역순정렬

nameList.stream()
.sorted((o1, o2) -> o2.length() - o1.length()) //임시 Comparator 정의 (둘의 길이를 비교해 더 긴게 앞으로 간다)

 

4. Terminal Operations (Stream 결과 반환)

: 중간 작업으로 선별/변환된 데이터를 (Stream) 최종적으로 활용하고 싶은 형태로 반환하는 작업.

- Stream에 남은 데이터를 계산하거나, 내부 데이터가 어떤 조건을 만족하거나, 다시 Collection과 같은 형태로 변환하는 작업

- 이미 종결작업(Terminal operations)이 호출된 Stream은 재사용 불가

- 기술적으로 최종작업이 선택되어야 Stream 전체 완료.

  • forEach : 간단한 순회
  • count : 총 갯수 조회
  • toList : 결과를 리스트로 정리
  • allMatch, anyMatch, noneMatch : 주어진 함수에 인자로 넣은 결과가 true인 애들을 기준으로 모든 원소가 통과하는지 (allMatch)/ 원소 하나라도 통과하는지 (anyMatch)/ 하나도 통과하지 않는지 (noneMatch)
  • sum : IntStream과 같은 숫자 스트림은 집계 가능.
  • max / min / average : 최댓값/ 최솟값/ 평균
//forEach
nameListStream.forEach(System.out::println);

// count 
System.out.println(nameListStream.count());

// toList
List<String> result = nameListStream.toList();

// allMatch 모든 원소가 통과하는지
boolean allMatch = nameListStream.allMatch(name -> name.contains("a"));
System.out.println(allMatch);

// anyMatch 원소 하나라도 통과하는지
boolean anyMatch = nameListStream.anyMatch(name -> name.contains("e"));
System.out.println(anyMatch);

// noneMatch 하나도 통과하지 않는지
boolean noneMatch = nameListStream.noneMatch(name -> !name.contains("e"));
System.out.println(noneMatch);

// sum
System.out.println(intStream.sum());

// max, min, average
System.out.println(intStream.max());
System.out.println(intStream.min());
System.out.println(intStream.average());

 

 

 

728x90

'Programming > Java' 카테고리의 다른 글

String | StringBuffer | StringBuilder  (1) 2023.12.04
File I/O  (1) 2023.12.03
제너릭 (Generics)  (1) 2023.11.29
예외처리 (Exception Handling)  (1) 2023.11.28
객체지향 프로그래밍  (1) 2023.11.28

BELATED ARTICLES

more