250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- 도메인 주도 개발 시작하기
- 자료구조
- 기술면접
- 이펙티브 자바
- 알고리즘
- MariaDB
- AWS
- 클린코드
- 이펙티브자바
- 이팩티브 자바
- jpa
- 혼공SQL
- 인프런김영한
- DDD
- 알고리즘분석
- SQL쿡북
- 인프런백기선
- 인덱스
- java
- mysql
- 네트워크
- CleanCode
- AWS RDS
- vue.js
- react
- 자바
- 자바예외
- 스프링부트와AWS로혼자구현하는웹서비스
- 자바스터디
- aop
Archives
- Today
- Total
기록이 힘이다.
[이펙티브 자바] 아이템45 스트림은 주의해서 사용하라 본문
728x90
스트림 API는 다량의 데이터 처리 작업(순차적이든 병렬적이든)을 돕고자 자바8에 추가되었다.
- 스트림은 데이터 원소의 유한 혹은 무한 시퀀스를 뜻한다.
- 스트림 파이프라인은 이 원소들로 수행하는 연산 단계를 표현하는 개념이다.
스트림 파이프라인은 지연 평가된다.
기본적으로 스트림 파이프라인은 순차적으로 수행된다.
어떠한 계산이라도 해낼 수 있다. 하지만 잘못 사용하면 읽기 어렵고 유지보수도 힘들어 진다.
ex) 아나그램이란 철자를 구성하는 알파벳이 같고 순서만 다른 단어를 말한다.
package effectivejava.chapter7.item45.anagrams;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Stream;
import static java.util.stream.Collectors.groupingBy;
public class StreamAnagrams {
// 코드 45-1 사전 하나를 훑어 원소 수가 많은 아나그램 그룹들을 출력한다. (269-270쪽)
public static void main(String[] args) throws IOException {
File dictionary = new File(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
Map<String, Set<String>> groups = new HashMap<>();
try (Scanner s = new Scanner(dictionary)) {
while (s.hasNext()) {
String word = s.next();
groups.**computeIfAbsent**(alphabetize(word),
(unused) -> new TreeSet<>()).add(word);
}
}
for (Set<String> group : groups.values())
if (group.size() >= minGroupSize)
System.out.println(group.size() + ": " + group);
}
private static String alphabetize(String s) {
char[] a = s.toCharArray();
Arrays.sort(a);
return new String(a);
}
// 코드 45-2 스트림을 과하게 사용했다. - 따라 하지 말 것! (270-271쪽)
public static void main(String[] args) throws IOException {
Path dictionary = Paths.get(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
try (Stream<String> words = Files.lines(dictionary)) {
words.collect(
groupingBy(word -> **word.chars().sorted()
.collect(StringBuilder::new,
(sb, c) -> sb.append((char) c),
StringBuilder::append).toString()**))
.values().stream()
.filter(group -> group.size() >= minGroupSize)
.map(group -> group.size() + ": " + group)
.forEach(System.out::println);
}
}
}
// 코드 45-3 스트림을 적절히 활용하면 깔끔하고 명료해진다. (271쪽)
public class HybridAnagrams {
public static void main(String[] args) throws IOException {
Path dictionary = Paths.get(args[0]);
int minGroupSize = Integer.parseInt(args[1]);
try (Stream<String> words = Files.lines(dictionary)) {
words.collect(groupingBy(word -> alphabetize(word)))
.values().stream()
.filter(group -> group.size() >= minGroupSize)
.forEach(g -> System.out.println(g.size() + ": " + g));
}
}
private static String alphabetize(String s) {
char[] a = s.toCharArray();
Arrays.sort(a);
return new String(a);
}
}
람다에서는 타입 이름을 자주 생략하므로 매개변수 이름을 잘 지어야 스트림 파이프라인의 가독성이 유지된다.
기존 코드는 스트림을 사용하도록 리팩터링하되, 새 코드가 더 나아 보일 때만 반영하자.
스트림 파이프라인은 일단 한 값을 다른 값에 매핑하고 나면 원래의 값을 잃는 구조이다.
package effectivejava.chapter7.item45;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import static java.util.stream.Collectors.*;
// 반복 방식과 스트림 방식으로 두 리스트의 데카르트 곱을 생성한다. (275-276쪽)
public class Card {
public enum Suit { SPADE, HEART, DIAMOND, CLUB }
public enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN,
EIGHT, NINE, TEN, JACK, QUEEN, KING }
private final Suit suit;
private final Rank rank;
@Override public String toString() {
return rank + " of " + suit + "S";
}
public Card(Suit suit, Rank rank) {
this.suit = suit;
this.rank = rank;
}
private static final List<Card> NEW_DECK = newDeck();
// 코드 45-4 데카르트 곱 계산을 반복 방식으로 구현 (275쪽)
private static List<Card> newDeck() {
List<Card> result = new ArrayList<>();
for (Suit suit : Suit.values())
for (Rank rank : Rank.values())
result.add(new Card(suit, rank));
return result;
}
// // 코드 45-5 데카르트 곱 계산을 스트림 방식으로 구현 (276쪽)
private static List<Card> newDeck() {
return Stream.of(Suit.values())
.flatMap(suit ->
Stream.of(Rank.values())
.map(rank -> new Card(suit, rank)))
.collect(toList());
}
public static void main(String[] args) {
System.out.println(NEW_DECK);
}
}
스트림 방식이 나아 보이고 동료들도 스트림 코드를 이해할 수 있고 선호한다면 스트림 방식을 사용하자.
스트림의 코드를 프로젝트에서 본 적이 없는 것 같다. 아무래도 나에게는 반복문이 더 익숙한 것 같다. 코딩테스트를 잠깐 준비할 때 스트림을 보기도 하였는데 자바8이라는 비교적 구버전에서 나온 것이라는 걸 이번에 알게 되었다.
'JAVA' 카테고리의 다른 글
[이펙티브 자바]아이템 57 지역변수의 범위를 최소화하라 (0) | 2023.04.11 |
---|---|
[이펙티브 자바]아이템55. 옵셔널 반환은 신중히 하라 (0) | 2023.04.10 |
[이펙티브 자바]아이템44 표준 함수형 인터페이스를 사용하라 (0) | 2023.04.07 |
[이펙티브 자바] 6장 열거타입과 애너테이션 (0) | 2023.03.29 |
과제) 자바의 Record를 공부하세요. (0) | 2023.03.26 |