πŸ‘¨πŸ»β€πŸ’» μ΄νŽ™ν‹°λΈŒ μžλ°” - λžŒλ‹€μ™€ 슀트림

PeterΒ·2022λ…„ 3μ›” 21일
0
post-thumbnail

7μž₯ - λžŒλ‹€μ™€ 슀트림


πŸ’‘ 읡λͺ… ν΄λž˜μŠ€λ³΄λ‹€λŠ” λžŒλ‹€λ₯Ό μ‚¬μš©ν•˜λΌ

β€œμ΅λͺ… ν΄λž˜μŠ€λŠ” (ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ μ•„λ‹Œ) νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€ λ•Œλ§Œ μ‚¬μš©ν•˜λΌβ€

1. 읡λͺ… 클래슀

  • μžλ°”8 μ΄μ „μ—λŠ” ν•¨μˆ˜ 객체λ₯Ό λ§Œλ“œλŠ” μ£Όμš” μˆ˜λ‹¨μœΌλ‘œ 읡λͺ… 클래슀λ₯Ό μ‚¬μš©ν–ˆμ—ˆμŒ
    • 읡λͺ… 클래슀 방식은 μ½”λ“œκ°€ λ„ˆλ¬΄ κΈΈλ‹€..
// 읡λͺ… 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό ν•¨μˆ˜ 객체둜 μ‚¬μš© - 낑은 기법이닀!
Collections.sort(words, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
});
System.out.println(words);
Collections.shuffle(words);

2. λžŒλ‹€

  • μžλ°”8에 μ™€μ„œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λžŒλ‹€μ‹μ„ μ΄μš©ν•΄ 짧게 λ§Œλ“€ 수 있게 됨.
    • μ•„λž˜ μ½”λ“œμ˜ λžŒλ‹€μ‹μ„ μ‚΄νŽ΄λ³΄λ©΄ λ§€κ°œλ³€μˆ˜, λ°˜ν™˜κ°’μ˜ νƒ€μž…μ΄ λͺ…μ‹œλ˜μ–΄ μžˆμ§€ μ•ŠμŒ.
      • μ»΄νŒŒμΌλŸ¬κ°€ νƒ€μž…μ„ μ•Œμ•„μ„œ μΆ”λ‘ ν•΄ 쀌.
      • 컴파일러의 νƒ€μž… μΆ”λ‘  κ·œμΉ™μ€ 맀우 λ³΅μž‘ν•˜λ―€λ‘œ 잘 μ•Œμ§€ λͺ»ν•΄λ„ 상관 μ—†μŒ
    • νƒ€μž…μ„ λͺ…μ‹œν•΄μ•Ό μ½”λ“œκ°€ 더 λͺ…ν™•ν•  λ•Œλ₯Ό μ œμ™Έν•˜κ³ λŠ”, μ•„λž˜ μ½”λ“œμ²˜λŸΌ λͺ¨λ“  λ§€κ°œλ³€μˆ˜ νƒ€μž…μ€ μƒλž΅ν•˜μž.
// μ½”λ“œ 42-2 λžŒλ‹€μ‹μ„ ν•¨μˆ˜ 객체둜 μ‚¬μš© - 읡λͺ… 클래슀 λŒ€μ²΄ 
Collections.sort(words,
        (s1, s2) -> Integer.compare(s1.length(), s2.length()));
System.out.println(words);
Collections.shuffle(words);
  • λžŒλ‹€λ₯Ό μ‚¬μš©ν•΄ μƒμˆ˜λ³„ λ™μž‘μ„ κ΅¬ν˜„ν•œ μ—΄κ±° νƒ€μž… μ˜ˆμ‹œ
    • μ—΄κ±° νƒ€μž… μƒμˆ˜μ˜ λ™μž‘μ„ ν‘œν˜„ν•œ λžŒλ‹€λ₯Ό DoubleBinaryOperator μΈν„°νŽ˜μ΄μŠ€ λ³€μˆ˜μ— 할당함.
    • DoubleBinaryOperator μΈν„°νŽ˜μ΄μŠ€λŠ” double νƒ€μž… 인수 2개λ₯Ό λ°›μ•„ double νƒ€μž… κ²°κ³Όλ₯Ό λ°˜ν™˜ν•¨.
// ν•¨μˆ˜ 객체(λžŒλ‹€)λ₯Ό μΈμŠ€ν„΄μŠ€ ν•„λ“œμ— μ €μž₯ν•΄ μƒμˆ˜λ³„ λ™μž‘μ„ κ΅¬ν˜„ν•œ μ—΄κ±° νƒ€μž…
public enum Operation {
    PLUS  ("+", (x, y) -> x + y),
    MINUS ("-", (x, y) -> x - y),
    TIMES ("*", (x, y) -> x * y),
    DIVIDE("/", (x, y) -> x / y);

    private final String symbol;
    private final DoubleBinaryOperator op;

    Operation(String symbol, DoubleBinaryOperator op) {
        this.symbol = symbol;
        this.op = op;
    }

    @Override public String toString() { return symbol; }

    public double apply(double x, double y) {
        return op.applyAsDouble(x, y);
    }

}
  • λžŒλ‹€λ₯Ό μ‚¬μš©ν•˜μ§€ 말아야 ν•  경우
    • μ½”λ“œ 자체둜 λ™μž‘μ΄ λͺ…ν™•νžˆ μ„€λͺ…λ˜μ§€ μ•Šκ±°λ‚˜ μ½”λ“œ 쀄 μˆ˜κ°€ λ§Žμ•„μ§ˆ λ•Œ.
      • λžŒλ‹€λŠ” 이름이 μ—†κ³  λ¬Έμ„œν™”λ„ λͺ» ν•˜κΈ° λ•Œλ¬Έ.
      • λžŒλ‹€λŠ” ν•œ 쀄일 λ•Œ κ°€μž₯ μ’‹κ³ , κΈΈμ–΄μ•Ό μ„Έ 쀄 μ•ˆμ— λλ‚΄λŠ” 게 μ’‹μŒ
    • μžμ‹ μ„ μ°Έμ‘°ν•΄μ•Όν•  경우
      • λžŒλ‹€λŠ” μžμ‹ μ„ μ°Έμ‘°ν•  수 μ—†μŒ.
      • λžŒλ‹€μ—μ„œ thisν‚€μ›Œλ“œλŠ” λ°”κΉ₯ μΈμŠ€ν„΄μŠ€, 읡λͺ… ν΄λž˜μŠ€μ—μ„œμ˜ thisλŠ” μžμ‹ 


πŸ’‘ λžŒλ‹€λ³΄λ‹€λŠ” λ©”μ„œλ“œ μ°Έμ‘°λ₯Ό μ‚¬μš©ν•˜λΌ

β€œλ©”μ„œλ“œ μ°Έμ‘° μͺ½μ΄ 짧고 λͺ…ν™•ν•˜λ‹€λ©΄ λ©”μ„œλ“œ μ°Έμ‘°λ₯Ό μ“°κ³ , 그렇지 μ•Šμ„ λ•Œλ§Œ λžŒλ‹€λ₯Ό μ‚¬μš©ν•˜λΌβ€

1. λžŒλ‹€λ₯Ό λ©”μ„œλ“œ 참쑰둜

  • λ©”μ†Œλ“œ μ°Έμ‘°(method reference) : λžŒλ‹€ ν‘œν˜„μ‹μ΄ 단 ν•˜λ‚˜μ˜ λ©”μ†Œλ“œλ§Œμ„ ν˜ΈμΆœν•˜λŠ” κ²½μš°μ— ν•΄λ‹Ή λžŒλ‹€ ν‘œν˜„μ‹μ—μ„œ λΆˆν•„μš”ν•œ λ§€κ°œλ³€μˆ˜λ₯Ό μ œκ±°ν•˜κ³  μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•΄μ€Œ
  • ν•¨μˆ˜ 객체λ₯Ό λžŒλ‹€λ³΄λ‹€ 더 κ°„κ²°ν•˜κ²Œ λ§Œλ“œλŠ” μ˜ˆμ‹œ
// map.mergeλ₯Ό μ΄μš©ν•΄ κ΅¬ν˜„ν•œ λΉˆλ„ν‘œ - λžŒλ‹€ 방식과 λ©”μ„œλ“œ μ°Έμ‘° 방식을 λΉ„κ΅ν•΄λ³΄μž.
public class Freq {
    public static void main(String[] args) {
        Map<String, Integer> frequencyTable = new TreeMap<>();
        
        for (String s : args)
            frequencyTable.merge(s, 1, (count, incr) -> count + incr); // λžŒλ‹€
        System.out.println(frequencyTable);

        frequencyTable.clear();
        for (String s : args)
            frequencyTable.merge(s, 1, Integer::sum); // λ©”μ„œλ“œ μ°Έμ‘°
        System.out.println(frequencyTable);

    }
}
  • λ©”μ„œλ“œ μ°Έμ‘°λ₯Ό μ‚¬μš©ν•˜λŠ” 편이 보톡은 더 짧고 간결함
  • λ•Œλ‘œλŠ” λžŒλ‹€μ—μ„œ λ§€κ°œλ³€μˆ˜μ˜ 이름 μžμ²΄κ°€ ν”„λ‘œκ·Έλž˜λ¨Έμ—κ²Œ 쒋은 κ°€μ΄λ“œκ°€ λ˜κΈ°λ„ 함.

2. 5가지 λ©”μ„œλ“œ μ°Έμ‘° μœ ν˜•

λ©”μ„œλ“œ μ°Έμ‘° μœ ν˜•μ˜ˆμ‹œλžŒλ‹€ ν‘œν˜„
정적Integer::parseIntstr β†’ Integer.parseInt(str)
ν•œμ •μ (μΈμŠ€ν„΄μŠ€)Instant.now()::isAfterInstant then = Instant.now();
t β†’ then.isAfter(t)
λΉ„ν•œμ •μ (μΈμŠ€ν„΄μŠ€)String::toLowerCasestr β†’ str.toLowerCase()
클래슀 μƒμ„±μžTreeMap<K, V>::new() β†’ new TreeMap<K, V>()
λ°°μ—΄ μƒμ„±μžint[]::newlen β†’ new int[len]


πŸ’‘ ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λΌ

β€œλ³΄ν†΅μ€ java.util.function νŒ¨ν‚€μ§€μ˜ ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 κ°€μž₯ 쒋은 선택”

1. ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

  • java.util.function νŒ¨ν‚€μ§€λ₯Ό 보면 λ‹€μ–‘ν•œ μš©λ„μ˜ ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ 담겨 있음.
  • ν•„μš”ν•œ μš©λ„μ— λ§žλŠ” 게 μžˆλ‹€λ©΄, 직접 κ΅¬ν˜„ν•˜μ§€ 말고 ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•˜μž
  • κΈ°λ³Έ μΈν„°νŽ˜μ΄μŠ€ 6개
μΈν„°νŽ˜μ΄μŠ€ν•¨μˆ˜ μ‹œκ·Έλ‹ˆμ²˜μ˜ˆμ„€λͺ…
UnaryOperatorT apply(T t)String::toLowerCaseλ°˜ν™˜κ°’κ³Ό 인수의 νƒ€μž…μ΄ 같은 ν•¨μˆ˜(인수 1개)
BinaryOperatorT apply(T t1, T t2)BigInteger::addλ°˜ν™˜κ°’κ³Ό 인수의 νƒ€μž…μ΄ 같은 ν•¨μˆ˜(인수 2개)
Predicateboolean test(T t)Collection::isEmpty인수 ν•˜λ‚˜λ₯Ό λ°›μ•„ boolean을 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜
Function<T, R>R apply(T t)Arrays::asListμΈμˆ˜μ™€ λ°˜ν™˜ νƒ€μž…μ΄ λ‹€λ₯Έ ν•¨μˆ˜
SupplierT get()Instance::now인수λ₯Ό 받지 μ•Šκ³  값을 λ°˜ν™˜(ν˜Ήμ€ 제곡)ν•˜λŠ” ν•¨μˆ˜
Consumervoid accept(T t)System.out::println인수λ₯Ό ν•˜λ‚˜ λ°›κ³  λ°˜ν™˜κ°’μ€ μ—†λŠ”(특히 인수λ₯Ό μ†ŒλΉ„ν•˜λŠ” ) ν•¨μˆ˜

2. μ „μš© ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

  • ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ , μ „μš© ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Όν•˜λŠ” λ•ŒλŠ” μ–Έμ œμΌκΉŒ?
    • μ•„λž˜ μ„Έ 가지 쑰건 쀑 ν•˜λ‚˜μ΄μƒμ„ λ§Œμ‘±ν•  λ•Œ.
      1. 자주 쓰이며, 이름 μžμ²΄κ°€ μš©λ„λ₯Ό λͺ…ν™•νžˆ μ„€λͺ…ν•΄μ€€λ‹€.
      2. λ°˜λ“œμ‹œ 따라야 ν•˜λŠ” κ·œμ•½μ΄ μžˆλ‹€.
      3. μœ μš©ν•œ λ””ν΄νŠΈ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•  수 μžˆλ‹€.
  • ex) Compartor<T> μΈν„°νŽ˜μ΄μŠ€
    • 이 μΈν„°νŽ˜μ΄μŠ€λŠ” ꡬ쑰적으둜 ToIntBiFunction<T, U> 와 동일함.
    • 이 μΈν„°νŽ˜μ΄μŠ€κ°€ λ…μžμ μœΌλ‘œ 살아남은 이유
      • 세가지 쑰건을 λͺ¨λ‘ λ§Œμ‘±ν•¨.
      1. APIμ—μ„œ ꡉμž₯히 자주 쓰이며 이름이 μš©λ„λ₯Ό λͺ…ν™•νžˆ μ„€λͺ…함
      2. κ΅¬ν˜„ν•˜λŠ” μͺ½μ—μ„œ λ°˜λ“œμ‹œ μ§€μΌœμ•Ό ν•  κ·œμ•½μ„ λ‹΄κ³  있음.
      3. λΉ„κ΅μžλ“€μ„ λ³€ν™˜ν•˜κ³  μ‘°ν•©ν•΄μ£ΌλŠ” μœ μš©ν•œ λ””ν΄νŠΈ λ©”μ„œλ“œλ“€μ„ λ‹΄κ³  있음.
  • μ „μš© ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μž‘μ„±ν•  λ•Œ
    1. μžμ‹ μ΄ μž‘μ„±ν•˜λŠ” 게 β€˜μΈν„°νŽ˜μ΄μŠ€β€™μž„μ„ λͺ…심해야 함.(μ£Όμ˜ν•΄μ„œ 섀계해야 함)
    2. 항상 @FunctionalInterface μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λΌ.
  • @FunctionalInterface μ–΄λ…Έν…Œμ΄μ…˜μ˜ κΈ°λŠ₯
    1. ν•΄λ‹Ή 클래슀의 μ½”λ“œλ‚˜ μ„€λͺ… λ¬Έμ„œλ₯Ό 읽을 μ΄μ—κ²Œ κ·Έ μΈν„°νŽ˜μ΄μŠ€κ°€ λžŒλ‹€μš©μœΌλ‘œ μ„€κ³„λœ κ²ƒμž„μ„ μ•Œλ €μ€Œ
    2. ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€κ°€ 좔상 λ©”μ„œλ“œλ₯Ό 였직 ν•˜λ‚˜λ§Œ 가지고 μžˆμ–΄μ•Ό 컴파일되게 ν•΄μ€Œ
      • μœ μ§€λ³΄μˆ˜ κ³Όμ •μ—μ„œ λˆ„κ΅°κ°€ μ‹€μˆ˜λ‘œ λ©”μ„œλ“œλ₯Ό μΆ”κ°€ν•˜μ§€ λͺ»ν•˜κ²Œ λ§‰μ•„μ€Œ.


πŸ’‘ μŠ€νŠΈλ¦Όμ€ μ£Όμ˜ν•΄μ„œ μ‚¬μš©ν•˜λΌ

β€œμŠ€νŠΈλ¦Όκ³Ό 반볡 쀑 μ–΄λŠ μͺ½μ΄ λ‚˜μ€μ§€ ν™•μ‹ ν•˜κΈ° μ–΄λ ΅λ‹€λ©΄ λ‘˜ λ‹€ 해보고 더 λ‚˜μ€ μͺ½μ„ νƒν•˜λΌβ€

1. 슀트림과 슀트림 νŒŒμ΄ν”„λΌμΈ

  • 슀트림(stream)μ΄λž€?
    • 데이터 μ›μ†Œμ˜ μœ ν•œ ν˜Ήμ€ λ¬΄ν•œ μ‹œν€€μŠ€(sequence)
  • 슀트림 νŒŒμ΄ν”„λΌμΈ(stream pipeline)μ΄λž€?
    • 슀트림의 μ›μ†Œλ“€λ‘œ μˆ˜ν–‰ν•˜λŠ” μ—°μ‚° 단계λ₯Ό ν‘œν˜„
    • μ†ŒμŠ€ μŠ€νŠΈλ¦Όμ—μ„œ μ‹œμž‘ν•΄ 쒅단 μ—°μ‚°(terminal operation)으둜 λλ‚˜λ©°, κ·Έ 사이에 ν•˜λ‚˜ μ΄μƒμ˜ 쀑간 μ—°μ‚°(intermediate operation)이 μžˆμ„ 수 있음.
      • 각 쀑간 연산은 μŠ€νŠΈλ¦Όμ„ μ–΄λ– ν•œ λ°©μ‹μœΌλ‘œ λ³€ν™˜(transform)함
    • 슀트림 νŒŒμ΄ν”„λΌμΈμ€ 지연 평가됨. ν‰κ°€λŠ” 쒅단 연산이 호좜될 λ•Œ 이뀄지며, 쒅단 연산에 쓰이지 μ•ŠλŠ” 데이터 μ›μ†ŒλŠ” 계산에 쓰이지 μ•ŠμŒ.
      • 1개 μ΄μƒμ˜ 쀑간연산듀은 계속합쳐진 ν›„ 쒅단연산 μ‹œ μˆ˜ν–‰λœλ‹€λŠ” 뜻.
      • λ¬΄ν•œ μŠ€νŠΈλ¦Όμ„ λ‹€λ£° 수 있게 ν•΄μ£ΌλŠ” μ—΄μ‡ 
    • 쒅단 연산이 μ—†λŠ” 슀트림 νŒŒμ΄ν”„λΌμΈμ€ 아무 일도 ν•˜μ§€ μ•ŠλŠ” λͺ…령어인 no-opκ³Ό κ°™μœΌλ‹ˆ, 쒅단 연산을 λΉΌλ¨ΉλŠ” 일이 μ ˆλŒ€ 없도둝 ν•˜μž.
    • 슀트림 νŒŒμ΄ν”„λΌμΈμ€ 순차적으둜 μˆ˜ν–‰ 됨.
  • 슀트림 API의 νŠΉμ§•
    • 슀트림 APIλŠ” λ©”μ„œλ“œ 체이닝을 μ§€μ›ν•˜λŠ” ν”Œλ£¨μ–ΈνŠΈ API(fluent API)μž„
    • 슀트림 APIλŠ” λ‹€μž¬λ‹€λŠ₯ν•˜μ—¬ 사싀상 μ–΄λ– ν•œ 계산이라도 ν•΄λ‚Ό 수 있음.

2. 슀트림의 μ‚¬μš©

  • μŠ€νŠΈλ¦Όμ„ 적절히 ν™œμš©ν•΄ μ•„λ‚˜κ·Έλž¨ 그룹을 좜λ ₯ν•˜λŠ” μ˜ˆμ‹œ
// μŠ€νŠΈλ¦Όμ„ 적절히 ν™œμš©ν•˜λ©΄ κΉ”λ”ν•˜κ³  λͺ…λ£Œν•΄μ§„λ‹€.
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);
    }
}
  • μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•˜κΈ° μ μ ˆν•œ 경우
    • μ›μ†Œλ“€μ˜ μ‹œν€€μŠ€λ₯Ό μΌκ΄€λ˜κ²Œ λ³€ν™˜ν•œλ‹€.
    • μ›μ†Œλ“€μ˜ μ‹œν€€μŠ€λ₯Ό ν•„ν„°λ§ν•œλ‹€.
    • μ›μ†Œλ“€μ˜ μ‹œν€€μŠ€λ₯Ό ν•˜λ‚˜μ˜ 연산을 μ‚¬μš©ν•΄ κ²°ν•©ν•œλ‹€(λ”ν•˜κΈ°, μ—°κ²°ν•˜κΈ°, μ΅œμ†Ÿκ°’ κ΅¬ν•˜κΈ° λ“±)
    • μ›μ†Œλ“€μ˜ μ‹œν€€μŠ€λ₯Ό μ»¬λ ‰μ…˜μ— λͺ¨μ€λ‹€(μ•„λ§ˆλ„ κ³΅ν†΅λœ 속성을 κΈ°μ€€μœΌλ‘œ λ¬Άμ–΄κ°€λ©°)
    • μ›μ†Œλ“€μ˜ μ‹œν€€μŠ€μ—μ„œ νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜λŠ” μ›μ†Œλ₯Ό μ°ΎλŠ”λ‹€.
  • 슀트림 μ‚¬μš© μ‹œ 주의점
    • μŠ€νŠΈλ¦Όμ„ κ³Όμš©ν•˜λ©΄ ν”„λ‘œκ·Έλž¨μ΄ μ½κ±°λ‚˜ μœ μ§€λ³΄μˆ˜ν•˜κΈ° μ–΄λ €μ›Œμ§„λ‹€.
    • char 값듀을 μ²˜λ¦¬ν•  λ•ŒλŠ” μŠ€νŠΈλ¦Όμ„ μ‚Όκ°€λŠ” 편이 λ‚«λ‹€.
      • μžλ°”κ°€ κΈ°λ³Έ νƒ€μž…μΈ char용 μŠ€νŠΈλ¦Όμ„ μ§€μ›ν•˜μ§€ μ•ŠμŒ.
      • CharSequence μΈν„°νŽ˜μ΄μŠ€μ˜ chars() λ©”μ„œλ“œλŠ” λ°˜ν™˜ 값이 IntStream μž„.
    • λ°˜λ³΅λ¬Έμ„ 슀트림으둜 λ°”κΎΈλŠ” 게 κ°€λŠ₯할지라도 μ½”λ“œ 가독성과 μœ μ§€λ³΄μˆ˜ μΈ‘λ©΄μ—μ„œλŠ” 손해λ₯Ό λ³Ό μˆ˜λ„ 있음
  • ꢌμž₯
    • 슀트림과 λ°˜λ³΅λ¬Έμ„ 적절히 μ‘°ν•©ν•˜μž
      • ν•¨μˆ˜ κ°μ²΄λ‘œλŠ” ν•  수 μ—†μ§€λ§Œ 반볡문(μ½”λ“œ 블둝)으둜만 ν•  수 μžˆλŠ” 일듀도 있음
        1. λ²”μœ„ μ•ˆμ˜ μ§€μ—­λ³€μˆ˜ 읽기 / μˆ˜μ •. (λžŒλ‹€μ—μ„œλŠ” finalμ΄κ±°λ‚˜ 사싀상 final인 λ³€μˆ˜λ§Œ 읽을 수 있음)
        2. return, break, continueλ¬Έ μ‚¬μš©. (λžŒλ‹€μ—μ„œλŠ” λΆˆκ°€λŠ₯)
        3. λ©”μ„œλ“œ 선언에 λͺ…μ‹œλœ 검사 μ˜ˆμ™Έ λ˜μ§€κΈ°. (λžŒλ‹€μ—μ„œλŠ” λΆˆκ°€λŠ₯)
    • κΈ°μ‘΄ μ½”λ“œλŠ” μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•˜λ„λ‘ λ¦¬νŒ©ν„°λ§ν•˜λ˜, μƒˆ μ½”λ“œκ°€ 더 λ‚˜μ•„ 보일 λ•Œλ§Œ λ°˜μ˜ν•˜μž
    • μŠ€νŠΈλ¦Όμ„ λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œ 이름은 μ›μ†Œμ˜ 정체λ₯Ό μ•Œλ €μ£ΌλŠ” 볡수λͺ…μ‚¬λ‘œ μ“°κΈ°λ₯Ό κ°•λ ₯히 μΆ”μ²œ
      • ex) static Stream<BigInteger> primes() { ... }


πŸ’‘ μŠ€νŠΈλ¦Όμ—μ„œλŠ” λΆ€μž‘μš© μ—†λŠ” ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λΌ

β€œμŠ€νŠΈλ¦Ό 연산에 κ±΄λ„€λŠ” ν•¨μˆ˜ κ°μ²΄λŠ” λͺ¨λ‘ λΆ€μž‘μš©(side effect)이 μ—†μ–΄μ•Ό ν•œλ‹€β€

1. 슀트림 νŒ¨λŸ¬λ‹€μž„

  • 슀트림 νŒ¨λŸ¬λ‹€μž„μ˜ 핡심은 계산을 일련의 λ³€ν™˜(transformation)으둜 μž¬κ΅¬μ„±ν•˜λŠ” λΆ€λΆ„
  • 각 λ³€ν™˜ λ‹¨κ³„λŠ” κ°€λŠ₯ν•œ ν•œ 이전 λ‹¨κ³„μ˜ κ²°κ³Όλ₯Ό λ°›μ•„ μ²˜λ¦¬ν•˜λŠ” 순수 ν•¨μˆ˜μ—¬μ•Ό 함.
    • 순수 ν•¨μˆ˜ : λ‹€λ₯Έ κ°€λ³€ μƒνƒœλ₯Ό μ°Έμ‘°ν•˜κ±°λ‚˜ ν•¨μˆ˜ 슀슀둜 λ‹€λ₯Έ μƒνƒœλ₯Ό λ³€κ²½ν•˜μ§€ μ•ŠμœΌλ©° 였직 μž…λ ₯만이 결과에 영ν–₯을 μ£ΌλŠ” ν•¨μˆ˜
  • 슀트림 연산에 κ±΄λ„€λŠ” ν•¨μˆ˜ κ°μ²΄λŠ” λͺ¨λ‘ λΆ€μž‘μš©(side effect)이 μ—†μ–΄μ•Ό 함.

2. forEach 쒅단 μ—°μ‚°

  • forEach 연산은 쒅단 μ—°μ‚° 쀑 κΈ°λŠ₯이 κ°€μž₯ 적고 κ°€μž₯ β€˜λœβ€™ μŠ€νŠΈλ¦Όλ‹€μ›€.
  • λŒ€λ†“κ³  λ°˜λ³΅μ μ΄λΌμ„œ 병렬화할 μˆ˜λ„ μ—†μŒ.

β†’ forEach 연산은 슀트림 계산 κ²°κ³Όλ₯Ό 보고할 λ•Œλ§Œ μ‚¬μš©ν•˜κ³ , κ³„μ‚°ν•˜λŠ” λ°λŠ” 쓰지 말자.


3. μˆ˜μ§‘κΈ°(collector)

  • μŠ€νŠΈλ¦Όμ„ μ˜¬λ°”λ‘œ μ‚¬μš©ν•˜λ €λ©΄ μˆ˜μ§‘κΈ°λ₯Ό 잘 μ•Œμ•„λ‘¬μ•Ό 함.
  • 가독성을 μœ„ν•΄ 일반적으둜 java.util.stream.Collectors 의 멀버λ₯Ό 정적 μž„ν¬νŠΈν•˜μ—¬ μ‚¬μš©ν•¨.
  • μˆ˜μ§‘κΈ°κ°€ μƒμ„±ν•˜λŠ” κ°μ²΄λŠ” 일반적으둜 μ»¬λ ‰μ…˜μž„.
  • μ€‘μš”ν•œ 5가지 μˆ˜μ§‘κΈ° νŒ©ν„°λ¦¬λ₯Ό μ•Œμ•„λ³΄μž
  1. toList() : 리슀트 λ°˜ν™˜
// λΉˆλ„ν‘œμ—μ„œ κ°€μž₯ ν”ν•œ 단어 10개λ₯Ό λ½‘μ•„λ‚΄λŠ” νŒŒμ΄ν”„λΌμΈ
List<String> topTen = freq.keySet().stream()
        .sorted(comparing(freq::get).reversed())
        .limit(10)
        .collect(toList());
  1. toMap(keyMapper, valueMapper) : ν‚€ 맀퍼와 κ°’ 맀퍼λ₯Ό λ°›μ•„ 맡을 λ°˜ν™˜
Map<String, Operation> stringToEnum =
		Stream.of(values()).collect(
				toMap(Object::toString, e -> e));
  1. toSet() : 집합 λ°˜ν™˜
Set<String> result = givenList.stream()
  .collect(toSet());
  1. joining() : μ›μ†Œλ“€μ„ μ—°κ²°ν•˜μ—¬ λ¬Έμžμ—΄ λ°˜ν™˜
// λ§€κ°œλ³€μˆ˜κ°€ 없을 경우 : "abbcccdd" 좜λ ₯
String result = givenList.stream()
  .collect(joining());

// λ§€κ°œλ³€μˆ˜κ°€ μžˆμ„ 경우(κ΅¬λΆ„λ¬Έμžλ₯Ό μ—°κ²°λΆ€μœ„μ— μ‚½μž…ν•΄ 쀌) : "a bb ccc dd" 좜λ ₯
String result = givenList.stream()
  .collect(joining(" "));
  1. groupingBy(classifier) : λΆ„λ₯˜ ν•¨μˆ˜λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›μ•„ μ›μ†Œλ“€μ„ μΉ΄ν…Œκ³ λ¦¬λ³„λ‘œ λͺ¨μ•„ 놓은 맡을 담은 μˆ˜μ§‘κΈ° λ°˜ν™˜.
// κ°„λ‹¨ν•œ μ˜ˆμ‹œ : μ•ŒνŒŒλ²³ν™”ν•œ 단어λ₯Ό μ•ŒνŒŒλ²³ν™” κ²°κ³Όκ°€ 같은 λ‹¨μ–΄λ“€μ˜ 리슀트둜 λ§€ν•‘ν•˜λŠ” 맡 생성.
private static String alphabetize(String s) {
    char[] a = s.toCharArray();
    Arrays.sort(a);
    return new String(a);
}

words.collect(groupingBy(word -> alphabetize(word))); 

// λΆ„λ₯˜ ν•¨μˆ˜μ™€ λ‹€μš΄ μŠ€νŠΈλ¦Όμ„ λ°›λŠ” μ˜ˆμ‹œ
/** λ‹€μš΄μŠ€νŠΈλ¦Ό μˆ˜μ§‘κΈ°λ‘œ counting()을 κ±΄λ„€μ„œ 각 μΉ΄ν…Œκ³ λ¦¬(ν‚€)λ₯Ό (μ›μ†Œλ₯Ό 담은 μ»¬λ ‰μ…˜μ΄ μ•„λ‹Œ)
	* ν•΄λ‹Ή μΉ΄ν…Œκ³ λ¦¬μ— μ†ν•˜λŠ” μ›μ†Œμ˜ 개수(κ°’)와 λ§€ν•‘ν•œ 맡을 μ–»μŒ
	*/
// μŠ€νŠΈλ¦Όμ„ μ œλŒ€λ‘œ ν™œμš©ν•΄ λΉˆλ„ν‘œλ₯Ό μ΄ˆκΈ°ν™”ν•œλ‹€.
Map<String, Long> freq;
try (Stream<String> words = new Scanner(file).tokens()) {
    freq = words
            .collect(groupingBy(String::toLowerCase, counting()));
}
  • [μ°Έκ³ ] groupingBy λͺ…μ„Έ
static <T,K> Collector<T,?,Map<K,List<T>>> 
  groupingBy(Function<? super T,? extends K> classifier)

static <T,K,A,D> Collector<T,?,Map<K,D>>
  groupingBy(Function<? super T,? extends K> classifier, 
    Collector<? super T,A,D> downstream)

static <T,K,D,A,M extends Map<K,D>> Collector<T,?,M>
  groupingBy(Function<? super T,? extends K> classifier, 
    Supplier<M> mapFactory, Collector<? super T,A,D> downstream)


πŸ’‘ λ°˜ν™˜ νƒ€μž…μœΌλ‘œλŠ” μŠ€νŠΈλ¦Όλ³΄λ‹€ μ»¬λ ‰μ…˜μ΄ λ‚«λ‹€

β€œμ›μ†Œ μ‹œν€€μŠ€λ₯Ό λ°˜ν™˜ν•˜λŠ” 곡개 API의 λ°˜ν™˜ νƒ€μž…μ—λŠ” Collectionμ΄λ‚˜ κ·Έ ν•˜μœ„ νƒ€μž…μ„ μ“°λŠ” 게 일반적으둜 μ΅œμ„ β€

1. 슀트림 λ°˜ν™˜μ˜ 문제점

  • μŠ€νŠΈλ¦Όμ€ 반볡(iteration)을 μ§€μ›ν•˜μ§€ μ•ŠμŒ
  • APIλ₯Ό 슀트림만 λ°˜ν™˜ν•˜λ„λ‘ μ§œλ†“μœΌλ©΄ λ°˜ν™˜λœ μŠ€νŠΈλ¦Όμ„ for-each둜 λ°˜λ³΅ν•˜κΈΈ μ›ν•˜λŠ” μ‚¬μš©μžλŠ” λΆˆλ§Œμ„ ν† λ‘œν•  κ²ƒμž„.
  • Stream<E>λ₯Ό Iterable<E>둜 μ€‘κ°œν•΄μ£ΌλŠ” μ–΄λŒ‘ν„°λ₯Ό μ‚¬μš©ν•˜λ©΄ μŠ€νŠΈλ¦Όμ„ for-each문으둜 λ°˜λ³΅ν•  수 있음
    • κ·ΈλŸ¬λ‚˜ μ–΄λŒ‘ν„°λŠ” ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œλ₯Ό μ–΄μˆ˜μ„ ν•˜κ²Œ λ§Œλ“€κ³  느림.
// 슀트림 <-> 반볡자 μ–΄λŒ‘ν„°
public class Adapters {
    // Stream<E>λ₯Ό Iterable<E>둜 μ€‘κ°œν•΄μ£ΌλŠ” μ–΄λŒ‘ν„°
    public static <E> Iterable<E> iterableOf(Stream<E> stream) {
        return stream::iterator;
    }

    // Iterable<E>λ₯Ό Stream<E>둜 μ€‘κ°œν•΄μ£ΌλŠ” μ–΄λŒ‘ν„°
    public static <E> Stream<E> streamOf(Iterable<E> iterable) {
        return StreamSupport.stream(iterable.spliterator(), false);
    }
}

2. Collection λ°˜ν™˜

  • Collection μΈν„°νŽ˜μ΄μŠ€λŠ” Iterable의 ν•˜μœ„ νƒ€μž…μ΄κ³  stream λ©”μ„œλ“œλ„ μ œκ³΅ν•˜λ‹ˆ 반볡과 μŠ€νŠΈλ¦Όμ„ λ™μ‹œμ— 지원함.

β†’ μ›μ†Œ μ‹œν€€μŠ€λ₯Ό λ°˜ν™˜ν•˜λŠ” 곡개 API의 λ°˜ν™˜ νƒ€μž…μ—λŠ” Collection μ΄λ‚˜ κ·Έ ν•˜μœ„ νƒ€μž…μ„ μ“°λŠ” 게 일반적으둜 μ΅œμ„ μž„.

  • λ°˜ν™˜ν•˜λŠ” μ‹œν€€μŠ€μ˜ 크기가 λ©”λͺ¨λ¦¬μ— μ˜¬λ €λ„ μ•ˆμ „ν•  만큼 μž‘μ€ 경우

β†’ ArrayListλ‚˜ HashSet 같은 ν‘œμ€€ μ»¬λ ‰μ…˜ κ΅¬ν˜„μ²΄λ₯Ό λ°˜ν™˜ν•˜μž

  • λ°˜ν™˜ν•˜λŠ” μ‹œν€€μŠ€μ˜ 크기가 λ©μΉ˜κ°€ 큰 경우

β†’ μ „μš© μ»¬λ ‰μ…˜μ„ κ΅¬ν˜„ν•˜λŠ” λ°©μ•ˆμ„ κ²€ν† ν•΄λ³΄μž.

  • μ „μš© μ»¬λ ‰μ…˜ κ΅¬ν˜„ μ˜ˆμ‹œ
public class PowerSet {
    // μž…λ ₯ μ§‘ν•©μ˜ 멱집합을 μ „μš© μ»¬λ ‰μ…˜μ— λ‹΄μ•„ λ°˜ν™˜ν•œλ‹€.
    public static final <E> Collection<Set<E>> of(Set<E> s) {
        List<E> src = new ArrayList<>(s);
        if (src.size() > 30)
            throw new IllegalArgumentException(
                "집합에 μ›μ†Œκ°€ λ„ˆλ¬΄ λ§ŽμŠ΅λ‹ˆλ‹€(μ΅œλŒ€ 30개).: " + s);
        return new AbstractList<Set<E>>() {
            @Override public int size() {
                // λ©±μ§‘ν•©μ˜ ν¬κΈ°λŠ” 2λ₯Ό μ›λž˜ μ§‘ν•©μ˜ μ›μ†Œ 수만큼 κ±°λ“­μ œκ³± 것과 κ°™λ‹€.
                return 1 << src.size();
            }

            @Override public boolean contains(Object o) {
                return o instanceof Set && src.containsAll((Set)o);
            }

            @Override public Set<E> get(int index) {
                Set<E> result = new HashSet<>();
                for (int i = 0; index != 0; i++, index >>= 1)
                    if ((index & 1) == 1)
                        result.add(src.get(i));
                return result;
            }
        };
    }

    public static void main(String[] args) {
        Set s = new HashSet(Arrays.asList(args));
        System.out.println(PowerSet.of(s));
    }
}
  • (반볡이 μ‹œμž‘λ˜κΈ° μ „μ—λŠ” μ‹œν€€μŠ€μ˜ λ‚΄μš©μ„ ν™•μ •ν•  수 μ—†λŠ” λ“±μ˜ μ‚¬μœ λ‘œ) contains와 sizeλ₯Ό κ΅¬ν˜„ν•˜λŠ” 게 λΆˆκ°€λŠ₯ν•  λ•Œ(즉, μ»¬λ ‰μ…˜μ„ λ°˜ν™˜ν•˜λŠ” 게 λΆˆκ°€λŠ₯ν•  λ•Œ)λŠ” μ»¬λ ‰μ…˜λ³΄λ‹€λŠ” μŠ€νŠΈλ¦Όμ΄λ‚˜ Iterable을 λ°˜ν™˜ν•˜λŠ” 편이 λ‚«λ‹€.


πŸ’‘ 슀트림 λ³‘λ ¬ν™”λŠ” μ£Όμ˜ν•΄μ„œ μ μš©ν•˜λΌ

β€œκ³„μ‚°λ„ μ˜¬λ°”λ‘œ μˆ˜ν–‰ν•˜κ³  μ„±λŠ₯도 빨라질 κ±°λΌλŠ” ν™•μ‹  μ—†μ΄λŠ” 슀트림 νŒŒμ΄ν”„λΌμΈ λ³‘λ ¬ν™”λŠ” μ‹œλ„μ‘°μ°¨ ν•˜μ§€ 말라”

1. 슀트림 λ³‘λ ¬ν™”μ˜ 문제점

// 병렬 μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•΄ 처음 20개의 λ©”λ₯΄μ„Ό μ†Œμˆ˜λ₯Ό μƒμ„±ν•˜λŠ” ν”„λ‘œκ·Έλž¨
// 주의: λ³‘λ ¬ν™”μ˜ 영ν–₯으둜 ν”„λ‘œκ·Έλž¨μ΄ μ’…λ£Œν•˜μ§€ μ•ŠλŠ”λ‹€.
public class ParallelMersennePrimes {
    public static void main(String[] args) {
        primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE))
                .parallel() // 슀트림 병렬화
                .filter(mersenne -> mersenne.isProbablePrime(50))
                .limit(20)
                .forEach(System.out::println);
    }

    static Stream<BigInteger> primes() {
        return Stream.iterate(TWO, BigInteger::nextProbablePrime);
    }
}
  • 이 ν”„λ‘œκ·Έλž¨μ€ 아무것도 좜λ ₯ν•˜μ§€ λͺ»ν•˜λ©΄μ„œ CPUλŠ” 90%λ‚˜ μž‘μ•„λ¨ΉλŠ” μƒνƒœκ°€ 지속됨(응닡 λΆˆκ°€; liveness failure)
  • 무슨 일이 λ²Œμ–΄μ§„ 걸까?

β†’ 슀트림 λΌμ΄λΈŒλŸ¬λ¦¬κ°€ 이 νŒŒμ΄ν”„λΌμΈμ„ λ³‘λ ¬ν™”ν•˜λŠ” 방법을 찾아내지 λͺ»ν–ˆκΈ° λ•Œλ¬Έ.

  • 데이터 μ†ŒμŠ€κ°€ Stream.iterate κ±°λ‚˜ 쀑간 μ—°μ‚°μœΌλ‘œ limitλ₯Ό μ“°λ©΄ νŒŒμ΄ν”„λΌμΈ λ³‘λ ¬ν™”λ‘œλŠ” μ„±λŠ₯ κ°œμ„ μ„ κΈ°λŒ€ν•  수 μ—†μŒ.

β†’ μœ„ μ½”λ“œλŠ” 두 문제λ₯Ό λͺ¨λ‘ μ§€λ‹ˆκ³  있음..

  • κ΅ν›ˆ : 슀트림 νŒŒμ΄ν”„λΌμΈμ„ 마ꡬ작이둜 λ³‘λ ¬ν™”ν•˜λ©΄ μ•ˆ 됨. 였히렀 λ”μ°ν•œ μ„±λŠ₯μ €ν•˜λ₯Ό κ°€μ Έμ˜¬ 수 있음.
  • μŠ€νŠΈλ¦Όμ„ 잘λͺ» λ³‘λ ¬ν™”ν•˜λ©΄ (응닡 λΆˆκ°€λ₯Ό 포함해) μ„±λŠ₯이 λ‚˜λΉ μ§ˆ 뿐만 μ•„λ‹ˆλΌ κ²°κ³Ό μžμ²΄κ°€ 잘λͺ»λ˜κ±°λ‚˜ μ˜ˆμƒ λͺ»ν•œ λ™μž‘μ΄ λ°œμƒν•  수 있음.

β†’ 슀트림 λ³‘λ ¬ν™”λŠ” 였직 μ„±λŠ₯ μ΅œμ ν™” μˆ˜λ‹¨μž„μ„ κΈ°μ–΅ν•˜μž. λ°˜λ“œμ‹œ λ³€κ²½ μ „ν›„λ‘œ μ„±λŠ₯ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜μ—¬ 병렬화λ₯Ό μ‚¬μš©ν•  κ°€μΉ˜κ°€ μžˆλŠ”μ§€ λ”°μ Έλ³΄μž.


2. 슀트림 병렬화가 μ ν•©ν•œ 경우

  • 슀트림의 μ†ŒμŠ€κ°€ ArrayList, HashMap, HashSet, ConcurrentHashMap 의 μΈμŠ€ν„΄μŠ€κ±°λ‚˜ λ°°μ—΄, int λ²”μœ„, long λ²”μœ„μΌ λ•Œ 적합.
  • μœ„ μžλ£Œκ΅¬μ‘°λ“€μ˜ νŠΉμ§•
    1. 데이터λ₯Ό μ›ν•˜λŠ” 크기둜 μ •ν™•ν•˜κ³  μ†μ‰½κ²Œ λ‚˜λˆŒ 수 μžˆμ–΄μ„œ 일을 λ‹€μˆ˜μ˜ μŠ€λ ˆλ“œμ— λΆ„λ°°ν•˜κΈ°μ— μ’‹μŒ
    2. μ°Έμ‘° 지역성(locality of reference)이 뛰어남
      • μ΄μ›ƒν•œ μ›μ†Œμ˜ 참쑰듀이 λ©”λͺ¨λ¦¬μ— μ—°μ†ν•΄μ„œ μ €μž₯λ˜μ–΄ μžˆλ‹€λŠ” 뜻
      • μ°Έμ‘° 지역성은 λ‹€λŸ‰μ˜ 데이터λ₯Ό μ²˜λ¦¬ν•˜λŠ” 벌크 연산을 병렬화할 λ•Œ μ•„μ£Ό μ€‘μš”ν•œ μš”μ†Œ
  • 쒅단 μ—°μ‚° μ€‘μ—λŠ” μΆ•μ†Œ(reduction)κ°€ 병렬화에 κ°€μž₯ 적합.
    • μΆ•μ†Œ : νŒŒμ΄ν”„λΌμΈμ—μ„œ λ§Œλ“€μ–΄μ§„ μ›μ†Œλ₯Ό ν•˜λ‚˜λ‘œ ν•©μΉ˜λŠ” μž‘μ—…
    • ex) Stream의 reduceλ©”μ„œλ“œ 쀑 ν•˜λ‚˜, ν˜Ήμ€ min, max, count, sum λ“±
  • 쑰건에 맞으면 λ°”λ‘œ λ°˜ν™˜λ˜λŠ” λ©”μ„œλ“œλ„ 병렬화에 적합
    • ex) anyMatch, allMatch, noneMatch λ“±
  • 슀트림 λ³‘λ ¬ν™”μ˜ μ ν•©ν•œ μ˜ˆμ‹œ
public class ParallelPrimeCounting {
    // μ†Œμˆ˜ 계산 슀트림 νŒŒμ΄ν”„λΌμΈ - 병렬화 버전
    static long pi(long n) {
        return LongStream.rangeClosed(2, n)
                .parallel()
                .mapToObj(BigInteger::valueOf)
                .filter(i -> i.isProbablePrime(50))
                .count();
    }

    public static void main(String[] args) {
        System.out.println(pi(10_000_000));
    }
}
profile
https://dev-peter.online/

0개의 λŒ“κΈ€