본문 바로가기
Language/Java

[Java] 자바 스트림(stream) 사용법 2 - 스트림의 중간연산, Optional

by 계범 2022. 3. 16.

스트림 자르기 - skip(), limit()

skip(n)은 n만큼 요소를 건너뛰는 것이고,

limit(n)은 n만큼 요소의 개수를 제한한다.

 

IntStream intStream = IntStream.rangeClosed(1,10); // 1~10의 요소를 가진 스트림
intStream.skip(3).limit(5).forEach(System.out::print); // 1,2,3건너뛰고 4~8까지 5개 출력

 

스트림 요소 걸러내기 -filter(), distinct()

distinct()는 스트림에서 중복된 요소들을 제거하고,

filter()는 주어진 조건에 맞지 않는 요소를 걸러낸다.

 

IntStream int Stream = IntStream.of(1,2,2,3,3,3,4,5,6);
intStream.distinct().forEach(System.out::print); // 123456

IntStream intStream = IntStream.rangeClosed(1,10);
intStream.filter(i ->i%2 != 0).filter(i -> i%3 != 0).forEach(System.out::print); // 157

 

정렬 - sorted()

스트림 원소를 정렬해준다. 매개변수에 Comparator를 넣어주면 그에 따른 정렬을 시행한다.

// 문자열 기준
strStream.sorted() // 기본정렬
strStream.sorted(Comparator.reverseOrder()) // 역순 정렬

 

예제 - 반별로 정렬한 후 내부 클래스에서 총점별로 내림차순 정렬한다.

import java.util.*;
import java.util.stream.*;

class StreamEx1 {
	public static void main(String[] args) {
	     Stream<Student> studentStream = Stream.of(
							new Student("이자바", 3, 300),
							new Student("김자바", 1, 200),
							new Student("안자바", 2, 100),
							new Student("박자바", 2, 150),
							new Student("소자바", 1, 200),
							new Student("나자바", 3, 290),
							new Student("감자바", 3, 180)
						);

	     studentStream.sorted(Comparator.comparing(Student::getBan) // 반별 정렬
			    	  .thenComparing(Comparator.naturalOrder()))    // 기본 정렬
					  .forEach(System.out::println);
	}
}

class Student implements Comparable<Student> {
	String name;
	int ban;
	int totalScore;

	Student(String name, int ban, int totalScore) { 
		this.name =name;
		this.ban =ban;
		this.totalScore =totalScore;
	}

	public String toString() { 
	    return String.format("[%s, %d, %d]", name, ban, totalScore).toString(); 
	}

	String getName()     { return name;}
	int getBan()         { return ban;}
	int getTotalScore()  { return totalScore;}

   // 총점 내림차순을 기본 정렬로 한다.
	public int compareTo(Student s) { 
		return s.totalScore - this.totalScore;
	}
}

 

변환 - map()

원하는 필드만 뽑아내거나 특정 형태로 변환해준다.

 

Stream<File> fileStream = Stream.of(new File("ex1.java"), ....);

fileStream.map(File::getName) // Stream<File> -> Stream<String>
    .filter(s -> s.indexOf('.') != -1) // 확장자가 없는것은 제외
    .map(s -> s.substring(s.indexOf('.')+1)) // 확장자이름만 뽑기
    .map(String::toUpperCase) // 모두 대문자로 변환
    .distinct() //중복제거
    .forEach(System.out::print); // 중간 연산 다 시행한 결과 원소 모두 출력

 

조회 - peek()

연산과 연산 사이에 올바르게 처리되었는지 확인하고 싶을 때 사용한다.

 

fileStream.map(File::getName)
    .filter(s -> s.indexOf('.') != -1)
    .peek(s -> System.out.printf("filename=%s%n", s)) // 파일명 출력
    .map(s -> s.substring(s.indexOf('.')+1))
    .peek(s -> System.out.printf("extension=%s%n", s)) // 확장자 출력
    .forEach(System.out::print);

 

mapToInt(), mapToLong(), mapToDouble()

스트림의 요소를 숫자로 변환하는 경우에 쓰인다.

 

//학생클래스가 들어있는 스트림에서 getTotalScore메서드를 통해 IntStream으로 변경
IntStream studentScoreStream = studentStream.mapToInt(Student::getTotalScore);

// .sum()메서드를 통해 총합계 구하기
int allTotalScore = studentScoreStream.sum();

 

 

 

Stream<T>는 count()만 지원하는데, IntStream같은 기본형 스트림은 숫자를 다루는데 편리한 메서드를 제공한다.

 

IntStream기준으로 설명

메서드 설 명
int sum() 스트림의 모든 요소의 총합
OptionalDouble average() sum() / (double)count()
OptionalInt max() 스트림의 요소 중 제일 큰 값
OptionalInt min() 스트림의 요소 중 제일 작은 값

 

sum()을 제외한 다른 메서드들은 단순히 0을 반환할 수 없어서 Optional클래스를 씀. 추후 설명.

 

이 메서드들은 최종연산이기때문에 호출 후에 스트림이 닫힌다.

만약 하나의 스트림에서 이러한 메서드들을 여러번 써야한다면 summaryStatistics() 메서드를 사용하면 된다.

 

IntSummaryStatistics stat = scoreStream.summaryStatistics();

long totalCount = stat.getCount();
long totalScore = stat.getSum();
double avgScore = stat.getAverage();
int minScore = stat.getMin();
int maxScore = stat.getMax();

 

flatMap() - Stream<T[]> 를 Stream<T>로 변환

스트림의 요소가 배열이거나 map()의 연산결과가 배열인 경우,

즉 Stream<T[]>인 경우, Stream<T>로 다루는 것이 더 편리할 때가 있다.

이럴 경우에 map() 대신 flatMap()을 통해 해결한다.

 

Stream<String[]>을 'map(Arrays:stream)' 변환한 결과는 Stream<Stream<String>>이 된다.

 

Stream<string[]>을 'flatmap(Arrays:stream)' 변환한 결과는 Stream<String>이 된다.

 

Optional<t> 와 OptionalInt

Optional클래스

Optional<T>는 제네릭 클래스로 'T타입의 객체'를 감싸는 래퍼 클래스이다.

Optional타입의 객체에는 모든 타입의 참조변수를 담을 수 있다.

 

최종 연산의 결과를 그냥 반환하는 게 아니라 Optional객체에 담아서 반환하는 것이다.

이처럼 객체에 담아서 반환을 하면, 반환된 결과가 null인지 if문으로 체크하는 대신,

Optional에 정의된 메서드 isNull() 등을 통해 확인할 수 있다.

그렇기 때문에 if문이 없어도 NullPointerException이 발생하지 않는다.

 

 

Optional객체 생성

Optional.of() 와 Optional.ofNullable() 로 생성가능하다.

만일 참조변수의 값이 null일 가능성이 있으면, ofNullable()을 사용한다.

 

Optional<String> optVal = Optional.of("abc");

Optional<String> optVal = Optional.ofNullable(null);

//기본값으로 초기화할 때는 empty() 사용할 것.
Optional<String> optVal = Optional.empty();

 

Optional객체의 값 가져오기

get() 사용.

값이 null일 경우에 NoSuchElementException이 발생하며, 이럴땐 orElse() 메서드로 대체할 값을 지정할 수 있다.

 

Optional<String> optVal = Optional.of("abc");

String str1 = optVal.get();
String str2 = optVal.orElse(""); // null일경우, "" 반환

 

orElseGet()은 null을 대체할 값을 반환하는 람다식을 지정 가능

orElseThrow()는 null일 때 지정된 예외 발생.

 

Optional객체도 Stream처럼 filter(), map(), flatMap() 사용 가능하다. 만약, null값이면 이 메서드들은 아무일도 하지 않는다.

 

Stream클래스에 정의된 메서드 중 Optional<T>를 반환하는 것들은 다음과 같다.

findAny()
findFirst()
max()
min()
reduce()

 

OptionalInt, OptionalLong, OptionalDouble

기본형을 값으로하는 Optional이다.

 

OptionalInt getAsInt()
OptionalLong getAsLong()
OptionalDouble getAsDouble()

 

 

댓글