-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#45][B팀]스트림은 주의해서 사용하라
- Loading branch information
Showing
1 changed file
with
169 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
# 스트림은 주의해서 사용하라 | ||
|
||
--- | ||
|
||
스트림은 다량의 데이터 처리 작업을 돕고자 java 8 에 추가되었다. | ||
|
||
## 💡스트림의 핵심 개념 2가지 | ||
|
||
- 데이터 원소의 무한 또는 유한 시퀀스(순서있는 나열) | ||
- 스트림 파이프 라인 : 원소들로 수행하는 연산 단계 | ||
|
||
## 스트림 파이프 라인 | ||
|
||
- 소스 스트림 - 중간 연산(스트림을 변환 ex: 조건, 함수 적용) - 종단 연산 | ||
- 지연 평가(lazy evaluation) : 종단 연산이 수행될 때 평가된다 - 종단 연산이 수행되지 않으면 아무일도 일어나지 않는다. | ||
- 일단 한 값을 다른 값에 매핑하고 나면 원래의 값은 잃는 구조 | ||
|
||
## 지연 평가(lazy evaluation) | ||
|
||
그때 그때 값을 평가하지 않고, 정말 결과값이 필요한 시점까지 평가를 미루는 것 | ||
|
||
1. 필요할 때만 평가가 되므로 메모리를 효율적으로 사용할 수 있다. | ||
2. 무한 자료구조를 만들 수 있음 | ||
3. 런타임 에러를 방지 할 수 있다. ( 컴파일 시 에러를 체킹) | ||
4. 컴파일러 최적화 가능 | ||
|
||
[지연 평가 링크](http://filimanjaro.com/blog/2014/introducing-lazy-evaluation/) | ||
|
||
```java | ||
public static void main(String[] args) { | ||
List<Integer> numList = Arrays.asList(10, 20, 30, 40, 50, 60); | ||
System.out.println(numList.stream() | ||
.filter(num -> { | ||
System.out.println("num < 50"); | ||
return num < 50; | ||
}) | ||
.filter(num ->{ | ||
System.out.println("num < 30"); | ||
return num < 30; | ||
}) | ||
.map(num ->{ | ||
System.out.println("num * 100"); | ||
return num * 100; | ||
}) | ||
.collect(Collectors.toList()) | ||
); | ||
} | ||
``` | ||
|
||
```java | ||
/** | ||
num < 50 | ||
num < 30 | ||
num * 100 | ||
num < 50 | ||
num < 30 | ||
num * 100 | ||
num < 50 | ||
num < 30 | ||
num < 50 | ||
num < 30 | ||
num < 50 | ||
num < 50 | ||
[1000, 2000] | ||
**/ | ||
``` | ||
|
||
## 반복문의 장점(코드 블럭 사용의 장점) | ||
|
||
스트림 → 함수 객체 사용 | ||
|
||
반복문 → 코드 블록 사용 | ||
|
||
- 범위 안의 지역 변수를 읽고 수정이 가능 | ||
- break, continue를 활용하여 반복문을 종료, 반복을 뛰어 넘을 수 있음 | ||
- 메서드 선언에 예외 처리가 가능함 | ||
|
||
## 스트림을 사용을 추천하는 경우 | ||
|
||
- 원소들의 시퀀스를 일관되게 변환하는 경우 | ||
- 원소들의 시퀀스를 필터링하는 경우 | ||
- 원소들의 시퀀스를 하나의 연산을 사용해 결합하는 경우 | ||
- 원소들의 시퀀스를 컬렉션에 모으는 경우(공통된 속성을 기준으로) | ||
- 원소들의 시퀀스에서 특정 조건을 만족하는 원소를 찾는 경우 | ||
|
||
--- | ||
|
||
## 스트림을 사용하여 가독성이 높아진 예제 | ||
|
||
```java | ||
public static void main(String[] args) { | ||
File dectionary = new File(args[0]); | ||
int minGroupSize = Integer.parseInt(args[1]); | ||
|
||
Map<String, Set<String>> groups = new HashMap<>(); | ||
try (Scanner s = new Scanner(dectionary)) { | ||
while(s.hasNext()) { | ||
String word = s.next(); | ||
groups.computeIfAbsent(alphabetize(word), | ||
(unused) -> new TreeSet<>()).add(word); | ||
} | ||
} catch (FileNotFoundException e) { | ||
e.printStackTrace(); | ||
} | ||
|
||
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); | ||
} | ||
``` | ||
|
||
- computeIfAbsent 함수: 맵 안에 키가 있는지 찾은 후, 있으면 단순히 키가 매핑된 값을 반환한다. | ||
|
||
```java | ||
public static void main(String[] args) throws IOException { | ||
File dectionary = new File(args[0]); | ||
int minGroupSize = Integer.parseInt(args[1]); | ||
|
||
try(Stream<String> words = Files.lines(dectionary.toPath())) { | ||
words.collect(groupingBy(word -> alphabetize(word))) | ||
.values().stream() | ||
.filter(group -> group.size() >= minGroupSize) | ||
.forEach(group -> System.out.println(group.size() + ": " + group)); | ||
} | ||
} | ||
``` | ||
|
||
😡스트림을 과용하면 프로그램을 읽거나 유지보수하는 것이 어려워 진다. | ||
|
||
💡람다 사용시 매개변수의 변수를 잘 지어야 스트림 파이프 라인의 가독성이 올라간다. | ||
|
||
--- | ||
|
||
## 스트림과 반복 중 어떤 쪽을 써야할지 알기 어려운 경우 | ||
|
||
데카르트 곱 : 가능한 모든 조합을 구하는 경우 | ||
|
||
```java | ||
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; | ||
} | ||
``` | ||
|
||
```java | ||
private static List<Card> newDeck() { | ||
return Stream.of(Suit.values()) | ||
.flatMap(suit -> Stream.of(Rank.values()) | ||
.map(rank -> new Card(suit, rank))) | ||
.collect(toList()); | ||
} | ||
``` | ||
|
||
두 가지 경우가 있는데, 제 기준 반복을 활용한 코드가 더 가독성이 높았습니다. | ||
|
||
--- | ||
|
||
## 🤷♀️Stream과 반복중 어느 쪽이 더 유리한지 확신이 서지 않을 경우, 둘 다 해보고 더 나은 쪽을 선택하기! |