Back-End/Java

μžλ°”μ˜ 정석 14μž₯: λžŒλ‹€μ™€ 슀트림(Lamda & Stream)

μ±”πŸ» 2024. 1. 24. 15:38
4️⃣

[ 좜처 ]
μžλ°”μ˜ 정석:
https://product.kyobobook.co.kr/detail/S000001550352
μžλ°”μ˜ 정석 유튜브:
https://www.youtube.com/playlist?list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp

14-1 λžŒλ‹€μ‹(Lambda Expression )

  • ν•¨μˆ˜(λ©”μ„œλ“œ)λ₯Ό κ°„λ‹¨ν•œ β€˜μ‹(expression)β€™μœΌλ‘œ ν‘œν˜„ν•˜λŠ” 방법이닀.
    int max(int a, int b) { // λ©”μ„œλ“œ
    		return a > b ? a : b;
    }
    (a, b) -> a > b ? a : b // λžŒλ‹€μ‹

  • 읡λͺ… ν•¨μˆ˜(이름이 μ—†λŠ” ν•¨μˆ˜, anonymous)

    λ©”μ„œλ“œλ₯Ό λžŒλ‹€μ‹μœΌλ‘œ ν‘œν˜„ν•˜λ©΄ λ©”μ„œλ“œμ˜ 이름과 λ°˜ν™˜κ°’μ΄ μ—†μ–΄μ§€λ―€λ‘œ 읡λͺ… ν•¨μˆ˜λΌκ³  ν•œλ‹€.

    int max(int a, int b) { // λ©”μ„œλ“œ
    		return a > b ? a : b;
    }
    int max(int a, int b) -> { // λžŒλ‹€μ‹
    		return a > b ? a : b;
    }
  • ν•¨μˆ˜μ™€ λ©”μ„œλ“œμ˜ 차이
    • 근볡적으둜 동일. ν•¨μˆ˜λŠ” 일반적 μš©μ–΄, λ©”μ„œλ“œλŠ” 객체지ν–₯κ°œλ… μš©μ–΄
    • ν•¨μˆ˜λŠ” ν΄λž˜μŠ€μ— 독립적, λ©”μ„œλ“œλŠ” ν΄λž˜μŠ€μ— 쒅속적

JavaλŠ” μ›λž˜ OOPμ–Έμ–΄μž„

JDK1.8λΆ€ν„° ν•¨μˆ˜ν˜• μ–Έμ–΄μ˜ κΈ°λŠ₯을 ν¬ν•¨μ‹œν‚΄

oop와 ν•¨μˆ˜ν˜•μ–Έμ–΄λΌκ³  ν•  μˆ˜λŠ” μžˆμœΌλ‚˜ ν•¨μˆ˜ν˜•μ–Έμ–΄λΌκ³  ν•˜κΈ°μ—λŠ” κΈ°λŠ₯이 μ’€ 약함 κ·Έλƒ₯ ν•¨μˆ˜ν˜• μ–Έμ–΄μ˜ κΈ°λŠ₯을 ν¬ν•¨ν•˜κ³  μžˆλ‹€ μ •λ„λ‘œ μ•Œκ³  있으면 됨

python, js β†’ λ‘˜λ‹€ oop, fp

정리 : λžŒλ‹€μ‹μ€ ν•¨μˆ˜(λ©”μ„œλ“œ)λ₯Ό κ°„λ‹¨νžˆ ν‘œν˜„ν•˜λ €κ³  μ“°λŠ” 것이닀.

14-2 λžŒλ‹€μ‹ μž‘μ„±ν•˜κΈ°

λžŒλ‹€μ‹ μž‘μ„±ν•˜κΈ°

  1. λ©”μ„œλ“œμ˜ 이름과 λ°˜ν™˜νƒ€μž…μ„ μ œκ±°ν•˜κ³ , λ§€κ°œλ³€μˆ˜ 선언뢀와 λͺΈν†΅{} 사이에 β€˜β†’β€™λ₯Ό μΆ”κ°€ν•œλ‹€.
    int max(int a, int b) { // λ©”μ„œλ“œ
    		return a > b ? a : b;
    }
    int max(int a, int b) -> { // λžŒλ‹€μ‹
    		return a > b ? a : b;
    }
  1. λ°˜ν™˜κ°’μ΄ μžˆλŠ” 경우, μ‹μ΄λ‚˜ κ°’λ§Œ 적고 returnλ¬Έ μƒλž΅ κ°€λŠ₯(끝에 β€˜;’ μ•ˆ λΆ™μž„)
    (int a, int b) -> {
    		return a > b ? a : b;
    }
    (int a, int b) -> a > b ? a : b
  1. λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ΄ μΆ”λ‘  κ°€λŠ₯ν•˜λ©΄ μƒλž΅κ°€λŠ₯(λŒ€λΆ€λΆ„μ˜ 경우 μƒλž΅κ°€λŠ₯)
    (int a, int b) -> a > b ? a : b
    
    (a, b) -> a > b ? a : b

μ£Όμ˜μ‚¬ν•­

  1. λ§€κ°œλ³€μˆ˜κ°€ ν•˜λ‚˜μΈ 경우, κ΄„ν˜Έ() μƒλž΅κ°€λŠ₯(νƒ€μž…μ΄ 없을 λ•Œλ§Œ)
    (a) -> a * a
    (int a) -> a * a
    a -> a * a     // OK
    int a -> a * a // μ—λŸ¬
  1. 블둝 μ•ˆμ˜ λ¬Έμž₯이 ν•˜λ‚˜λΏμΌ λ•Œ, κ΄„ν˜Έ{} μƒλž΅κ°€λŠ₯(끝에 β€˜;’ μ•ˆ λΆ™μž„)
    (int i) -> {
    		System.out.println(i);
    }

    (int i) -> System.out.println(i);

    단, ν•˜λ‚˜λΏμΈ λ¬Έμž₯이 return문이면 κ΄„ν˜Έ{} μƒλž΅λΆˆκ°€

    (int a, int b) -> { return a > b ? a : b; } // OK
    (int a, int b) -> return a > b ? a : b      // μ—λŸ¬

λžŒλ‹€μ‹ μž‘μ„±ν•˜κΈ° ν€΄μ¦ˆ

λ©”μ„œλ“œλžŒλ‹€μ‹
int max(int a, int b) {
return a > b ? a : b
}
(a, b) β†’ a > b ? a : b
int printVar(String name, int i) {
System.out.println(name + β€œ=” + i);
}
(name, i) β†’ System.out.println(name + β€œ=” + i)
int square(int x) {
return x * x;
}
x β†’ x * x
int roll() {
return (int) (Math.random() * 6);
}
() β†’ (int) (Math.random() * 6)

14-4 λžŒλ‹€μ‹μ€ 읡λͺ… ν•¨μˆ˜? 읡λͺ… 객체!

  • λžŒλ‹€μ‹μ€ 읡λͺ… ν•¨μˆ˜κ°€ μ•„λ‹ˆλΌ 읡λͺ… 객체이닀.

    μžλ°”μ—μ„œλŠ” λ©”μ„œλ“œλ§Œ μ‘΄μž¬ν•  수 μ—†κΈ° λ•Œλ¬Έμ—(λ°˜λ“œμ‹œ 클래슀 μ•ˆμ— 속해야 함)

    (a, b) -> a > b ? a : b

    λžŒλ‹€μ‹ == 객체

    new Object() {
    		int max(int a, int b) {
    				return a > b ? a : b;
    		}
    }

    객체의 μ„ μ–Έκ³Ό 생성을 λ™μ‹œμ— ν•œ 것이닀.

  • λžŒλ‹€μ‹(읡λͺ… 객체)을 닀루기 μœ„ν•œ μ°Έμ‘°λ³€μˆ˜κ°€ ν•„μš”. μ°Έμ‘°λ³€μˆ˜μ˜ νƒ€μž…μ€?

    객체λ₯Ό 닀루기 μœ„ν•΄μ„  μ°Έμ‘°λ³€μˆ˜κ°€ ν•„μš”ν•˜λ‹€.

    Object obj = new Object() {
    		int max(int a, int b) {
    				return a > b ? a : b;
    		}
    }
    νƒ€μž… obj = (a, b) -> a > b ? a : b; // μ–΄λ–€ νƒ€μž…?
    int value = obj.max(3, 5); // μ—λŸ¬. Object ν΄λž˜μŠ€μ— max()κ°€ μ—†μŒ

    ObjectλΌλŠ” 리λͺ¨μ»¨μ—μ„œλŠ” max()κ°€ μ—†λ‹€.

    그러면, μ°Έμ‘°λ³€μˆ˜ f의 νƒ€μž…μ€ μ–΄λ–€ 것이어야 ν• κΉŒ? μ°Έμ‘°ν˜•μ΄λ‹ˆκΉŒ 클래슀 λ˜λŠ” μΈν„°νŽ˜μ΄μŠ€κ°€ κ°€λŠ₯ν•˜λ‹€. 그리고 λžŒλ‹€μ‹κ³Ό λ™λ“±ν•œ λ©”μ„œλ“œκ°€ μ •μ˜λ˜μ–΄ μžˆλŠ” 것이어야 ν•œλ‹€. κ·Έλž˜μ•Ό μ°Έμ‘°λ³€μˆ˜λ‘œ 읡λͺ… 객체(λžŒλ‹€μ‹)의 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  수 있기 λ•Œλ¬Έμ΄λ‹€.

  • 14-0 예제
    package Test;
    
    class Main {
    	public static void main(String args[]) {
            // The target type of this expression must be a functional interfaceJava(553648781)
            // λžŒλ‹€μ‹μ€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ 닀뀄야 ν•œλ‹€λŠ” μ—λŸ¬ λ©”μ‹œμ§€κ°€ λ‚˜μ˜¨λ‹€.
            // Object obj = (a, b) -> a > b ? a : b ;// λžŒλ‹€μ‹. 읡λͺ…객체 / 객체이기 λ•Œλ¬Έμ— μ°Έμ‘°λ³€μˆ˜λ‘œ 닀뀄야 ν•œλ‹€.
    
            Object obj = new Object() {
                int max(int a, int b) {
                    return a > b ? a : b;
                }
            };
    
            int value = obj.max(3, 5); // Obejct type의 리λͺ¨μ»¨μ—λŠ” max λ©”μ„œλ“œκ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠμŒ -> ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ ν•„μš”
     	} // main
    }

14-5 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ - 단 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œλ§Œ μ„ μ–Έλœ μΈν„°νŽ˜μ΄μŠ€

    λžŒλ‹€μ‹μ„ 닀루기 μœ„ν•œ μΈν„°νŽ˜μ΄μŠ€μ΄λ‹€.

    // ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ˜¬λ°”λ₯΄κ²Œ μž‘μ„±ν–ˆλŠ”μ§€ 확인해쀀닀. / ν•„μˆ˜λŠ” μ•„λ‹˜, 뢙이면 μ’‹λ‹€λŠ” 것
    @FunctionalInterface 
    interface MyFunction {
    		public abstract int max(int a, int b);
    }

    MyFunction f = new MyFunction() { // 읡λͺ… 클래슀의 μ„ μ–Έ, 객체 생성 λ™μ‹œμ—
    		public int max(int a, int b) {
    				return a > b ? a : b;
    		}
    }

    new 쑰상이름(클래슀 or μΈν„°νŽ˜μ΄μŠ€) { // 멀버 }

    int value = f.max(3, 5); // OK. MyFunctioin에 max()κ°€ 있음
  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ˜ μ°Έμ‘°λ³€μˆ˜λ‘œ λžŒλ‹€μ‹μ„ μ°Έμ‘°ν•  수 있음.
    (단, ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ λ©”μ„œλ“œμ™€ λžŒλ‹€μ‹μ˜
    λ§€κ°œλ³€μˆ˜ κ°œμˆ˜μ™€ λ°˜ν™˜νƒ€μž…μ΄ μΌμΉ˜ν•΄μ•Ό 함)
    MyFunction f = (a, b) -> a > b > a : b;
    int value = f.max(3, 5); // μ‹€μ œλ‘œλŠ” λžŒλ‹€μ‹(읡λͺ… ν•¨μˆ˜)이 호좜됨
  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ 예제
    class Main {
    	public static void main(String args[]) {
            // MyFunction f = new MyFunction() {
            //     // int max(int a, int b) { // μ˜€λ²„λΌμ΄λ”© κ·œμΉ™ : 쑰상 λ©”μ„œλ“œλ³΄λ‹€ 쒁근 μ ‘κ·Όμ œμ–΄μžλ₯Ό κ°€μ§ˆ 수 μ—†μŒ.
            //     public int max(int a, int b) {
            //         return a > b ? a : b;
            //     }
            // };
            
            // λžŒλ‹€μ‹(읡λͺ…객체)을 닀루기 μœ„ν•œ μ°Έμ‘°λ³€μˆ˜μ˜ νƒ€μž…μ€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ ν•œλ‹€.
            MyFunction f = (a, b) -> a > b ? a : b; // λžŒλ‹€μ‹
            int value = f.max(3, 5); // ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€
    
            System.out.println(value);
     	} // main
    }
    @FunctionalInterface // ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” 단 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œλ§Œ κ°€μ Έμ•Ό ν•œλ‹€.
    interface MyFunction {
        int max(int a, int b); // μΈν„°νŽ˜μ΄μŠ€μ˜ λͺ¨λ“  λ©”μ„œλ“œλŠ” publicμ΄λ©΄μ„œ abstractλ‹€. μƒλž΅ κ°€λŠ₯.
        // public abstract int max(int a, int b);
        // public abstract int max(int a, int b); // FunctionalInterface μ• λ„ˆν…Œμ΄μ…˜μ„ λΆ™μ˜€μ„ 경우 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ˜¬λ°”λ₯΄κ²Œ μž‘μ„±ν–ˆλŠ”μ§€ 확인해쀀닀.
    
    }
    class Main {
    	public static void main(String args[]) {
            // λžŒλ‹€μ‹(읡λͺ…객체)을 닀루기 μœ„ν•œ μ°Έμ‘°λ³€μˆ˜μ˜ νƒ€μž…μ€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ ν•œλ‹€.
            MyFunction f = (a, b) -> a > b ? a : b; // λžŒλ‹€μ‹
            int value = f.max(3, 5); // ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€
    
            System.out.println(value);
     	} // main
    }
    @FunctionalInterface // ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” 단 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œλ§Œ κ°€μ Έμ•Ό ν•œλ‹€.
    interface MyFunction {
        int max(int a, int b); // μΈν„°νŽ˜μ΄μŠ€μ˜ λͺ¨λ“  λ©”μ„œλ“œλŠ” publicμ΄λ©΄μ„œ abstractλ‹€. μƒλž΅ κ°€λŠ₯.
    }

  • λ³€ν™˜ κ³Όμ •(23.04.03. μΆ”κ°€)
    public class Main2 {
        public static void main(String[] args) {
            MyFunction f = new Test();
    
        }
    }
    
    interface MyFunction {
        int max(int a, int b);
    }
    
    class Test implements MyFunction {
        @Override
        public int max(int a, int b) {
            return a > b ? a : b;
        }
    }

    public class Main2 {
        public static void main(String[] args) {
            MyFunction f = new MyFunction() {
                @Override
                public int max(int a, int b) {
                    return a > b ? a : b;
                }
            };
        }
    }
    
    interface MyFunction {
        int max(int a, int b);
    }
    public class Main2 {
        public static void main(String[] args) {
            MyFunction f1 = (a, b) -> a > b ? a : b;
        }
    }
    
    interface MyFunction {
        int max(int a, int b);
    }

14-5 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ - example

  • 읡λͺ… 객체λ₯Ό λžŒλ‹€μ‹μœΌλ‘œ λŒ€μ²΄
    List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");
    Collections.sort(list, new Comparator<String>() {
                                public int compare(String s1, String s2) {
                                    return s2.compareTo(s1);
                                }
                            });

    ⏬

    @FunctionalInterface // ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€
    interface Comparator<T> {
    		int compare(T o1, T o2);
    }

    List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");
    Collections.sort(list, (s1, s2) -> s2.compareTo(s1));
  • λ³€ν™˜ κ³Όμ •(23.04.03. μΆ”κ°€)
    public class Main2 {
        public static void main(String[] args) {
            List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");
            Collections.sort(list, new SortTest());
        }
    }
    
    class SortTest implements Comparator<String> {
    
        @Override
        public int compare(String o1, String o2) {
            return o2.compareTo(o1); // μ •λ ¬κΈ°μ€€ 제곡
        }
    }
    public class Main2 {
        public static void main(String[] args) {
            List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");
            Collections.sort(list, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return o2.compareTo(o1);
                }
            });
        }
    }
    public class Main2 {
        public static void main(String[] args) {
            List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");
            Collections.sort(list, (o1, o2) -> o2.compareTo(o1));
        }
    }

14-6 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ˜ λ§€κ°œλ³€μˆ˜, λ°˜ν™˜νƒ€μž…

  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ˜ λ§€κ°œλ³€μˆ˜ β†’ λžŒλ‹€μ‹μ„ λ§€κ°œλ³€μˆ˜λ‘œ λ°›κ² λ‹€λŠ” λœ»μ΄λ‹€.
    void aMethod(MyFunction f) { // λžŒλ‹€μ‹μ„ λ§€κ°œλ³€μˆ˜λ‘œ λ°›κ² λ‹€λŠ” λœ»μ΄λ‹€.
    		f.myMethod(); // MyFunction에 μ •μ˜λœ λ©”μ„œλ“œ 호좜
    }
    MyFunction f = () -> System.out.println("test");
    aMethod(f);
    // aMethod(() -> System.out.println("test"));
    interface MyFunction {
    		void myMethod();
    }

  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ˜ λ°˜ν™˜νƒ€μž… β†’ λžŒλ‹€μ‹μ„ λ°˜ν™˜ν•˜λΌλŠ” 뜻
    MyFunction myMethod() { // λžŒλ‹€μ‹ λ°˜ν™˜
    		MyFunction f = () -> {}:
    		return f;
    }

    ⏩

    MyFunction myMethod() {
    		return () -> {};
    }

  • 예제 14-1
    package Test;
    
    @FunctionalInterface
    interface MyFunction {
        void run(); // public abstract void run();
    }
    
    class Main {
        static void execute(MyFunction f) { // λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ΄ MyFunction인 λ©”μ„œλ“œ
            f.run();
        }
    
        static MyFunction getMyFunction() { // λ°˜ν™˜ νƒ€μž…μ΄ MyFunction인 λ©”μ„œλ“œ
            return () -> System.out.println("f3.run()");
        }
    
    	public static void main(String args[]) {
            MyFunction f1 = () -> System.out.println("f1.run()");
    
            MyFunction f2 = new MyFunction() {
                public void run() { // public을 λ°˜λ“œμ‹œ λΆ™μ—¬μ•Ό 함
                    System.out.println("f2.run()");
                }
            };
    
            MyFunction f3 = getMyFunction();
            // MyFunction f3 = () -> System.out.println("f3.run()"); // μœ„μ™€ 동일
    
            f1.run();
            f2.run();
            f3.run();
    
            System.out.println("======= execute =========");
    
            execute(f1);
            execute(f2);
            execute(f3);
            execute(() -> System.out.println("run()"));
     	} // main
    }
  • 예제 14-1 λ³€ν™˜κ³Όμ •(23.04.03. μ›” μΆ”κ°€)
    @FunctionalInterface
    interface MyFunction {
        void run(); // public abstract μƒλž΅
    }
    
    class Test implements MyFunction {
        @Override
        public void run() {
            System.out.println("f1.run");
        }
    }
    public class Main2 {
        public static void main(String[] args) {
            MyFunction f1 = new Test();
            f1.run();
    
        }
    }

    @FunctionalInterface
    interface MyFunction {
        void run(); // public abstract μƒλž΅
    }
    
    public class Main2 {
        public static void main(String[] args) {
            MyFunction f1 = new MyFunction() {
                @Override
                public void run() {
                    System.out.println("f1.run");
                }
            };
            f1.run();
        }
    }
    @FunctionalInterface
    interface MyFunction {
        void run(); // public abstract μƒλž΅
    }
    
    public class Main2 {
        public static void main(String[] args) {
            MyFunction f1 = () -> System.out.println("f1.run");
            f1.run();
        }
    }

14-7 java.util.function νŒ¨ν‚€μ§€

  • 자주 μ‚¬μš©λ˜λŠ” λ‹€μ–‘ν•œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 제곡

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ©”μ„œλ“œμ„€λͺ…
java.lang.Runnablevoid run()λ§€κ°œλ³€μˆ˜λ„ μ—†κ³ , λ°˜ν™˜κ°’λ„ μ—†μŒ
Supplier<T>T get()λ§€κ°œλ³€μˆ˜λŠ” μ—†κ³ , λ°˜ν™˜κ°’λ§Œ 있음
Consumer<T>void accept(T t)Supplier와 λ°˜λŒ€λ‘œ λ§€κ°œλ³€μˆ˜λ§Œ 있고, λ°˜ν™˜κ°’μ΄ μ—†μŒ
Function<T,R>R apply(T t)일반적인 ν•¨μˆ˜, ν•˜λ‚˜μ˜ λ§€κ°œλ³€μˆ˜λ₯Ό λ°›μ•„μ„œ κ²°κ³Όλ₯Ό λ°˜ν™˜
Predicate<T>boolean test(T t)쑰건식을 ν‘œν˜„ν•˜λŠ”λ° μ‚¬μš©λ¨. λ§€κ°œλ³€μˆ˜λŠ” ν•˜λ‚˜, λ°˜ν™˜ νƒ€μž…μ€ boolean
Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";

if (isEmptyStr.test(s)) { // if(s.length() == 0)
    System.out.println("This is an empty String.");
}
  • Q. μ•„λž˜μ˜ 빈 칸에 μ•Œλ§žμ€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€(java.util.function νŒ¨ν‚€μ§€)λ₯Ό μ μœΌμ‹œμ˜€.
    [   β‘    ] f = () -> (int)(Math.random() * 100) + 1;
    [   β‘‘   ] f = i -> System.out.print(i + ", ");
    [   β‘’   ] f = i -> i%2 == 0;
    [   β‘£   ] f = i -> i/10*10;
    • μ •λ‹΅
      Supplier<Interger> f = () -> (int)(Math.random() * 100) + 1; // input x output O
      Consumer<Integer> f = i -> System.out.print(i + ", ");
      Predicate<Integer> f = i -> i%2 == 0;
      Function<Integer, Integer> f = i -> i/10*10;

λ§€κ°œλ³€μˆ˜κ°€ 2개인 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ©”μ„œλ“œμ„€λͺ…
BiConsumer<T,U>void accept(T t, U u)λ‘κ°œμ˜ λ§€κ°œλ³€μˆ˜λ§Œ 있고, λ°˜ν™˜κ°’μ΄ μ—†μŒ
BiPredicate<T,U>boolean test(T t, U u)쑰건식을 ν‘œν˜„ν•˜λŠ”λ° μ‚¬μš©λ¨.
λ§€κ°œλ³€μˆ˜λŠ” λ‘˜, λ°˜ν™˜κ°’μ€ boolean
BiFunction<T,U,R>R apply(T t, U u)두 개의 λ§€κ°œλ³€μˆ˜λ₯Ό λ°›μ•„μ„œ ν•˜λ‚˜μ˜ κ²°κ³Όλ₯Ό λ°˜ν™˜
@FunctionalInterface
interface TriFunction<T,U,V,R> {
		R apply(T t, U u, V v);
}

λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…κ³Ό λ°˜ν™˜νƒ€μž…μ΄ μΌμΉ˜ν•˜λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ§€μ„œλ“œμ„€λͺ…
UnaryOperator<T>T apply(T t)Function의 μžμ†, Functionκ³Ό 달리 λ§€κ°œλ³€μˆ˜μ™€ 결과의 νƒ€μž…μ΄ κ°™λ‹€.
BinaryOperator<T>T apply(T t, T t)BiFunction의 μžμ†, BiFunctionκ³Ό 달리 λ§€κ°œλ³€μˆ˜μ™€ 결과의 νƒ€μž…μ΄ κ°™λ‹€.

Unary : 단항

Binary : 이항

Operator : μ—°μ‚°μž

  • 예제 14-2
    package Test;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.function.*;
    
    class Main {
    	public static void main(String args[]) {
            Supplier<Integer> s = () -> (int)(Math.random() * 100) + 1;
            Consumer<Integer> c = i -> System.out.print(i + ", ");
            Predicate<Integer> p = i -> i % 2 == 0;
            Function<Integer, Integer> f = i -> i / 10 * 10;
    
            List<Integer> list = new ArrayList<>();
            makeRandomList(s, list); // 1~100 λ²”μœ„μ˜ λžœλ€κ°’μ„ List에 10개 λ„£λŠ”λ‹€.
            System.out.println(list);
            printEvenNum(p, c, list);
            List<Integer> newList = doSomething(f, list);
            System.out.println(newList);
            
     	} // main
    
        static <T> List<T> doSomething(Function<T, T> f, List<T> list) {
            List<T> newList = new ArrayList<T>(list.size());
    
            for (T i : list) {
                newList.add(f.apply(i));
            }
    
            return newList;
        }
    
         static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
             System.out.print("[");
     
             for (T i : list) { // list에 μžˆλŠ” 값을 ν•˜λ‚˜μ”© κ°€μ Έμ˜¨λ‹€.
                 if (p.test(i)) { // i의 값이 짝수면
                     c.accept(i); // i의 값을 좜λ ₯ν•œλ‹€.
                 }
             }
             System.out.println("]");
         }
    
        static <T> void makeRandomList(Supplier<T> s, List<T> list) {
            for (int i = 0; i < 10; i++) {
                list.add(s.get()); // 1~100 λ²”μœ„μ˜ λžœλ€κ°’μ„ List에 10개 λ„£λŠ”λ‹€.
            }
        }
    }

14-8 Predicate의 κ²°ν•©

  • and(), or(), negate()둜 두 Predicateλ₯Ό ν•˜λ‚˜λ‘œ κ²°ν•©(default λ©”μ„œλ“œ)

    μΈν„°νŽ˜μ΄μŠ€ = μΆ”μƒλ©”μ„œλ“œ, static λ©”μ„œλ“œ, default λ©”μ„œλ“œ

    Predicate<Integer> p = i -> i < 100;
    Predicate<Integer> q = i -> i < 200;
    Predicate<Integer> r = i -> i % 2 == 0;
    Predicate<Integer> notP = p.negate(); // i >= 100
    Predicate<Integer> all = notP.and(q).or(r); // 100 <= i && i < 200 || i % 2 == 0
    Predicate<Integer> all2 = notP.and(q.or(r)); // 100 <= i && (i < 200 || i % 2 == 0)
    System.out.println(all.test(2));  // true
    System.out.println(all2.test(2)); // false
  • 등가비ꡐλ₯Ό μœ„ν•œ Predicate의 μž‘μ„±μ—λŠ” isEqual()λ₯Ό μ‚¬μš©(staticλ©”μ„œλ“œ)
    Predicate<String> p = Predicate.isEqual(str1); // isEqual()λŠ” staticλ©”μ„œλ“œ
    Boolean result = p.test(str2); // str1κ³Ό str2κ°€ 같은지 λΉ„κ΅ν•œ κ²°κ³Όλ₯Ό λ°˜ν™˜
    
    ->
    boolean result = Predicate.isEqual(str1).test(str2);

  • 예제 14-3
    // "abc" == "abc" -> true
    // new String("abc") == "abc" -> false(μ£Όμ†Œ 비ꡐ이기 λ•Œλ¬Έμ΄λ‹€.)
    String str1 = new String("abc");
    String str2 = new String("abc");
    
    System.out.println(str1 == str2);
    
    // 이 결과값이 trueλΌλŠ” 건 등가비ꡐ가 μ•„λ‹ˆλΌ equalsλ₯Ό μΌλ‹€λŠ” κ±Έ μ•Œ 수 μžˆλ‹€.
    boolean result = Predicate.isEqual(str1).test(str2);
    System.out.println(result);
    package chapter14;
    
    import java.util.function.Function;
    import java.util.function.Predicate;
    
    class Ex14_3 {
    	public static void main(String args[]) {
            Function<String, Integer> f = (s) -> Integer.parseInt(s, 16); // 16μ§„μˆ˜λ₯Ό 10μ§„μˆ˜λ‘œ λ³€ν™˜
            Function<Integer, String> g = (i) -> Integer.toBinaryString(i);
            
            Function<String, String> h = f.andThen(g);    // f μ μš©ν•˜κ³  gλ₯Ό μ μš©ν•΄λΌ
            Function<Integer, Integer> h2 = f.compose(g); // g μ μš©ν•˜κ³  fλ₯Ό μ μš©ν•΄λΌ
    
            System.out.println(h.apply("FF")); // "FF" -> 255 -> "11111111"
            System.out.println(h2.apply(2));   // 2 -> "10" -> 16
    
            Function<String, String> f2 = x -> x;   // ν•­λ“± ν•¨μˆ˜(identity function)
            System.out.println(f2.apply("AAA")); // AAAκ°€ κ·ΈλŒ€λ‘œ 좜λ ₯됨
    
            Predicate<Integer> p = i -> i < 100;
            Predicate<Integer> q = i -> i < 200;
            Predicate<Integer> r = i -> i % 2 == 0;
            Predicate<Integer> notP = p.negate(); // negate == not / i >= 100
    
            Predicate<Integer> all = notP.and(q.or(r)); // i >= 100 && (i < 200 || i % 2 == 0)
            System.out.println(notP.test(150)); // true
    
            String str1 = "abc";
            String str2 = "abc";
    
            // Predicate<String> p2 = Predicate.isEqual(str1); // isEqual의 λ°˜ν™˜νƒ€μž…μ€ Predicate(λžŒλ‹€μ‹ λ°˜ν™˜)
            // boolean result = p2.test(str2);
            System.out.println(Predicate.isEqual(str1).test(str2));
     	} // main
    }

14-11 μ»¬λ ‰μ…˜ ν”„λ ˆμž„μ›κ³Ό ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

  • ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” μ»¬λ ‰μ…˜ ν”„λ ˆμž„μ›Œν¬μ˜ λ””ν΄νŠΈ λ©”μ„œλ“œ(μ™€μΌλ“œ μΉ΄λ“œ μƒλž΅)
    πŸ’‘
    λ©”μ„œλ“œ μ°Έμ‘° == ν΄λž˜μŠ€μ΄λ¦„::λ©”μ„œλ“œμ΄λ¦„
    λ©”μ„œλ“œ μ°Έμ‘°λŠ” λžŒλ‹€μ‹μ„ 더 κ°„λ‹¨νžˆ ν•œ 것이닀.
μΈν„°νŽ˜μ΄μŠ€λ©”μ„œλ“œμ„€λͺ…
Collectionboolean removeIf(Predicate<E> filter)쑰건에 λ§žλŠ” μš”μ†Œλ₯Ό μ‚­μ œ
Listvoid replaceAll(UnaryOperator<E> operatorλͺ¨λ“  μš”μ†Œλ₯Ό λ³€ν™˜ν•˜μ—¬ λŒ€μ²΄
Iterablevoid forEach(Consumer<T> action)λͺ¨λ“  μš”μ†Œμ— μž‘μ—… action을 μˆ˜ν–‰
MapV compute(K key, BiFunction<K,V,V> f)μ§€μ •λœ ν‚€μ˜ 값에 μž‘μ—… fλ₯Ό μˆ˜ν–‰
β€œβ€V computeIfAbsent(K key, Function<K,V> f)ν‚€κ°€ μ—†μœΌλ©΄, μž‘μ—… f μˆ˜ν–‰ ν›„ μΆ”κ°€
β€œβ€V computeIfPresent(K key, BiFunction<K,V,V> f)μ§€μ •λœ ν‚€κ°€ μžˆμ„ λ•Œ, μž‘μ—… f μˆ˜ν–‰
β€œβ€V merge(K key, V value, BiFunction<V,V,V> f)λͺ¨λ“  μš”μ†Œμ— λ³‘ν•©μž‘μ—… fλ₯Ό μˆ˜ν–‰
β€œβ€void forEach(BiConsumer<K,V> action)λͺ¨λ“  μš”μ†Œμ— μž‘μ—… action을 μˆ˜ν–‰
β€œβ€void replaceAll(BiFunction<K,V,V> f)λͺ¨λ“  μš”μ†Œμ— μΉ˜ν™˜μž‘μ—… fλ₯Ό μˆ˜ν–‰

compute β†’ map의 valueλ₯Ό λ³€ν™˜

  • 예제 14-4
    package Test;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    
    class Main {
    	public static void main(String args[]) {
            ArrayList<Integer> list = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                list.add(i);
            }
            // Iterator it = list.iterator();
            // while (it.hasNext()) {
            //     System.out.println(it.next());
            // }
            list.forEach(i -> System.out.print(i + ","));
            System.out.println();
            
            list.removeIf(x -> x % 2 == 0 || x % 3 == 0); // listμ—μ„œ 2λ˜λŠ” 3의 배수λ₯Ό μ œκ±°ν•œλ‹€.
            System.out.println(list);
    
            list.replaceAll(i -> i * 10); // list의 각 μš”μ†Œμ— 10을 κ³±ν•œλ‹€.
            System.out.println(list);
    
            Map<String, String> map = new HashMap<>();
            map.put("1", "1");
            map.put("2", "2");
            map.put("3", "3");
            map.put("4", "4");
    
            // Iterator it = map.entrySet().iterator();
            // while (it.hasNext()) {
            //     System.out.println(it.next());
            // }
            // map의 λͺ¨λ“  μš”μ†Œλ₯Ό {k, v}의 ν˜•μ‹μœΌλ‘œ 좜λ ₯ν•œλ‹€.
            map.forEach((k, v) -> System.out.printf("{%s, %s}, ", k, v));
    
     	} // main
    }

14-13 λ©”μ„œλ“œ μ°Έμ‘°(method reference)

  • ν•˜λ‚˜μ˜ λ©”μ„œλ“œλ§Œ ν˜ΈμΆœν•˜λŠ” λžŒλ‹€μ‹μ€ β€˜λ©”μ„œλ“œ μ°Έμ‘°β€™λ‘œ 더 κ°„λ‹¨νžˆ ν•  수 μžˆλ‹€.

    β†’ λ©”μ„œλ“œ μ°Έμ‘° == λžŒλ‹€μ‹μ„ 더 κ°„λ‹¨νžˆ ν•œ 것

    μ’…λ₯˜λžŒλ‹€λ©”μ„œλ“œ μ°Έμ‘°
    static λ©”μ„œλ“œ μ°Έμ‘°(x) β†’ ClassName.method(x)ClassName::method
    μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œ μ°Έμ‘°(obj, x) β†’ obj.method(x)ClassName::method
    νŠΉμ • 객체 μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œ μ°Έμ‘°(x) β†’ obj.method(x)obj::method
  • static λ©”μ„œλ“œ μ°Έμ‘°

    β–Ό

    β–Ό

// κ·Έμ € Integer.parseInt(String s) 만 호좜
Integer method(String s) { 
		return Integer.parseInt(s);
}

int result = obj.method("123");
int result = Integer.parseInt("123");

Function<String, Integer> f 
= (String s) -> Integer.parseInt(s); // λžŒλ‹€μ‹

// ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ— 정보가 λ‹€ 있기 λ•Œλ¬Έμ— μ§€μšΈ 수 있음
Function<String, Integer> f = Integer::parseInt; // λ©”μ„œλ“œ μ°Έμ‘°

  • 예제
    Function<String, Integer> f = s -> Integer.parseInt(s); // λžŒλ‹€μ‹
    Function<String, Integer> f2 = Integer::parseInt; // λ©”μ„œλ“œ μ°Έμ‘°, μž…λ ₯이 String μ΄λΌλŠ” 건 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ— 정보가 λ‹€ 있기 λ•Œλ¬Έμ— μ•ˆ μ“Έ 수 μžˆλ‹€.
    System.out.println(f.apply("100") + 200);
    System.out.println(f2.apply("300") + 100);
    
    // λ©”μ„œλ“œ μ°Έμ‘° -> λžŒλ‹€μ‹
    // 1. μž…λ ₯을 μ“΄λ‹€.
    // 2. "::" -> "."
    // 3. λ§€κ°œλ³€μˆ˜λ₯Ό λ„£μ–΄μ€€λ‹€.
    Function<String, Integer> f3 = (s) -> Integer.parseInt(s);

14-14 μƒμ„±μžμ™€ λ©”μ„œλ“œ μ°Έμ‘°

  • μƒμ„±μžμ™€ λ©”μ„œλ“œ μ°Έμ‘°
    Supplier<MyClass> s = () -> new MyClass();

    β–ΆοΈŽ

    Supplier<MyClass> s = MyClass::new;

    μƒμ„±μžμ— λ§€κ°œλ³€μˆ˜κ°€ μžˆλŠ” 경우

    Function<Integer, MyClass> s = i -> new MyClass(i);

    β–ΆοΈŽ

    Function<Integer, MyClass> s = MyClass:new;
  • λ°°μ—΄κ³Ό λ©”μ„œλ“œ μ°Έμ‘°
    Function<Integer, int[]> f = x -> new int[x]; // λžŒλ‹€μ‹
    Function<Integer, int[]> f = int[]::new;      // λ©”μ„œλ“œμ°Έμ‘° - 이거 많이 μ“΄λ‹€κ³  함

  • 예제
    Supplier<MyClass> s = () -> new MyClass(); // λžŒλ‹€μ‹
    Supplier<MyClass> s2 = MyClass::new;       // λ©”μ„œλ“œ μ°Έμ‘°
    MyClass mc = s.get();
    System.out.println(mc);
    
    Function<Integer, MyClass> f = i -> new MyClass(i);
    Function<Integer, MyClass> f2 = MyClass::new;
    System.out.println(f2.apply(100).iv);
    
    
    BiFunction<Integer, String, MyClass> b = (i, str) -> new MyClass(i, str);
    BiFunction<Integer, String, MyClass> b2 = MyClass::new;
    System.out.println(b2.apply(21, "방채민").str);
    
    Function<Integer, int[]> f3 = i -> new int[i]; // λžŒλ‹€μ‹
    Function<Integer, int[]> f4 = int[]::new;      // λ©”μ„œλ“œ μ°Έμ‘°
    int[] arr = f4.apply(99);
    System.out.println("arr.length = " + arr.length);

14-15 슀트림(Stream)

  • λ‹€μ–‘ν•œ 데이터 μ†ŒμŠ€λ₯Ό ν‘œμ€€ν™”λœ λ°©λ²•μœΌλ‘œ 닀루기 μœ„ν•œ 것

슀트림 : λ°μ΄ν„°μ˜ 연속적인 흐름

<슀트림 μž‘μ—… 3단계>

  1. 슀트림 λ§Œλ“€κΈ°
  1. 쀑간 μ—°μ‚°(0~n번)
  1. μ΅œμ’…μ—°μ‚°(0~1번)
  • 슀트림이 μ œκ³΅ν•˜λŠ” κΈ°λŠ₯ - 쀑간 μ—°μ‚°κ³Ό μ΅œμ’… μ—°μ‚°
    πŸ’‘
    쀑간 μ—°μ‚° - μ—°μ‚° κ²°κ³Όκ°€ 슀트림인 μ—°μ‚°. 반볡적으둜 적용 κ°€λŠ₯
    μ΅œμ’… μ—°μ‚° - μ—°μ‚° κ²°κ³Όκ°€ 슀트림이 μ•„λ‹Œ μ—°μ‚°. 단 ν•œ 번만 μ μš©κ°€λŠ₯(슀트림의 μš”μ†Œλ₯Ό μ†Œλͺ¨)

14-17~22 슀트림 λ§Œλ“€κΈ°

μ»¬λ ‰μ…˜

Collection μΈν„°νŽ˜μ΄μŠ€μ˜ stream()으둜 μ»¬λ ‰μ…˜μ„ 슀트림으둜 λ³€κ²½

πŸ’‘
Stream<E> stream() // Collection μΈν„°νŽ˜μ΄μŠ€μ˜ λ©”μ„œλ“œ
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> intStream = list.stream(); // listλ₯Ό stream으둜 λ³€ν™˜(listλ₯Ό 데이터 μ†ŒμŠ€λ‘œ ν•˜λŠ” μƒˆλ‘œμš΄ μŠ€νŠΈλ¦Όμ„ 생성)

// stream은 1회용. stream에 λŒ€ν•΄ μ΅œμ’…μ—°μ‚°μ„ μˆ˜ν–‰ν•˜λ©΄ stream이 λ‹«νžŒλ‹€.
intStream.forEach(System.out::print); // λ©”μ„œλ“œ μ°Έμ‘°, 12345
intStream.forEach(System.out::print); // μ—λŸ¬. 슀트림이 이미 λ‹«ν˜”λ‹€.

λ°°μ—΄

  • 객체 λ°°μ—΄λ‘œλΆ€ν„° 슀트림 μƒμ„±ν•˜κΈ°
    Stream<T> Stream.of(T... values) // κ°€λ³€μΈμž
    Stream<T> Stream.of(T[])
    Stream<T> Arrays.stream(T[])
    Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive)
    
    Stream<String> strStream = Stream.of("a", "b", "c");
    Stream<String> strStream = Stream.of(new String[] {"a", "b", "c"});
    Stream<String> strStream = Arrays.stream(new String[] {"a", "b", "c"});
    Stream<String> strStream = Arrays.stream(new String[] {"a", "b", "c"}, 0, 3);
  • κΈ°λ³Έν˜• λ°°μ—΄λ‘œλΆ€ν„° 슀트림 μƒμ„±ν•˜κΈ°
    IntStream IntStream.of(int... values) // Stream이 μ•„λ‹ˆλΌ IntStream
    IntStream IntStream.of(int[])
    IntStream Arrays.stream(int[])
    IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)

String[] strArr = {"a", "b", "c", "d"};
// Stream<String> strStream = Stream.of(strArr);
Stream<String> strStream = Arrays.stream(strArr);
strStream.forEach(System.out::print);
int[] intArr = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(intArr);
System.out.println(intStream.sum()); // count(), average() - μ΅œμ’…μ—°μ‚°

,
Integer[] intArr = {new Integer(1), 2, 3, 4, 5};
Stream<Integer> intStream = Arrays.stream(intArr);
// 객체 μŠ€νŠΈλ¦Όμ€ count()밖에 μ—†μŒ
//  숫자 외에도 μ—¬λŸ¬ νƒ€μž…μ˜ 슀트림이 κ°€λŠ₯ν•΄μ•Όν•˜λ―€λ‘œ 숫자 μŠ€νŠΈλ¦Όμ—λ§Œ μ‚¬μš©ν•  수 μžˆλŠ” sum(), average()λ₯Ό λ„£μ§€ μ•Šμ€ 것
System.out.println(intStream.count());

κΈ°λ³Έν˜• 배열일 λ•ŒλŠ” IntStream μ¨μ£ΌλŠ” 게 μ’‹λ‹€.

μž„μ˜μ˜ 수

  • λ‚œμˆ˜λ₯Ό μš”μ†Œλ‘œ κ°–λŠ” 슀트림 μƒμ„±ν•˜κΈ°
    IntStream intStream = new Random().inits(); // λ¬΄ν•œ 슀트림
    intStream.limit(5).forEach(System.out::println); // 5개의 μš”μ†Œλ§Œ 좜λ ₯ν•œλ‹€.
    
    IntStream intStream = new Random().ints(5); // 크기가 5인 λ‚œμˆ˜ μŠ€νŠΈλ¦Όμ„ λ°˜ν™˜
  • μ§€μ •λœ λ²”μœ„μ˜ λ‚œμˆ˜λ₯Ό μš”μ†Œλ‘œ κ°–λŠ” μŠ€νŠΈλ¦Όμ„ μƒμ„±ν•˜λŠ” λ©”μ„œλ“œ(Random 클래슀)
    IntStream ints(int begin, int end)                    // λ¬΄ν•œ 슀트림
    LongStream longs(long begin, long end)
    DoubleStream doubles(double begin, double end)
    
    IntStream ints(long, streamSize, int begin, int end)  // μœ ν•œ 슀트림
    LongStream longs(long, streamSize, long begin, long end)
    DoulbeStream longs(long, streamSize, double begin, double end)

  • 예제?
    // IntStream intStream = new Random().ints(); // λ¬΄ν•œ 슀트림
    // IntStream intStream = new Random().ints(5); // μœ ν•œ 슀트림
    IntStream intStream = new Random().ints(5, 100); // λ¬΄ν•œ 슀트림
    intStream
        .limit(10) // 5개만 자λ₯΄κΈ°
        .forEach(System.out::println);

슀트림 λ§Œλ“€κΈ° - νŠΉμ • λ²”μœ„μ˜ μ •μˆ˜

νŠΉμ • λ²”μœ„μ˜ μ •μˆ˜λ₯Ό μš”μ†Œλ‘œ κ°–λŠ” 슀트림 μƒμ„±ν•˜κΈ°(IntStream, LongStream)

πŸ’‘
IntStream IntStream.range(int begin, int end) // end 미포함
IntStream IntStream.rangeClosed(int begin, int end) // end 포함
IntStream intStream = IntStream.range(1, 5);
IntStream intStream = IntStream.rangeClosed(1, 5);

슀트림 λ§Œλ“€κΈ° - λžŒλ‹€μ‹ iterate(), generate()

  • λžŒλ‹€μ‹μ„ μ†ŒμŠ€λ‘œ ν•˜λŠ” 슀트림 μƒμ„±ν•˜κΈ°
    static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) // 이전 μš”μ†Œμ— 쒅속적
    static <T> Stream<T> generate(Supplier<T> s)             // 이전 μš”μ†Œμ— 독립적
  • iterate()λŠ” 이전 μš”μ†Œλ₯Ό seed둜 ν•΄μ„œ λ‹€μŒ μš”μ†Œλ₯Ό κ³„μ‚°ν•œλ‹€.
    Stream<Integer> evenStream = Stream.iterator(0, n -> n + 2); // 0, 2, 4, 6 ... λ¬΄ν•œ 슀트림 
  • generate()λŠ” seedλ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€.
    Stream<Double> randomStream = Stream.generate(Math::random);
    Stream<Double> oneStream    = Stream.generate(() -> 1);

슀트림 λ§Œλ“€κΈ° - 파일과 빈 슀트림

  • νŒŒμΌμ„ μ†ŒμŠ€λ‘œ ν•˜λŠ” 슀트림 μƒμ„±ν•˜κΈ°
    Stream<Path> Files.list(Path dir) // PathλŠ” 파일 λ˜λŠ” 디렉토리
    Stream<String> Files.lines(Path path)
    Stream<String> Files.lines(Path path, Charset cs)
    Stream<String> lines() // BufferedReader 클래슀의 λ©”μ„œλ“œ
  • λΉ„μ–΄μžˆλŠ” 슀트림 μƒμ„±ν•˜κΈ°
    Stream emptyStream = Stream.empty(); // empty()λŠ” 빈 μŠ€νŠΈλ¦Όμ„ μƒμ„±ν•΄μ„œ λ°˜ν™˜ν•œλ‹€.
    long count = emptyStream.count();    // count의 값은 0

14-23 슀트림의 μ—°μ‚°

  • 슀트림이 μ œκ³΅ν•˜λŠ” κΈ°λŠ₯ - 쀑간 μ—°μ‚°κ³Ό μ΅œμ’… μ—°μ‚°

14-26~29 슀트림의 쀑간연산

슀트림 자λ₯΄κΈ° - skip(), limit()

Stream<T> skip(long n)         // μ•žμ—μ„œλΆ€ν„° n개 κ±΄λ„ˆλ›°κΈ°
Stream<T> limit(long maxSize() // maxSize μ΄ν›„μ˜ μš”μ†ŒλŠ” μž˜λΌλƒ„
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.skip(3).limit(5).forEach(System.out::print);; // 45678

슀트림의 μš”μ†Œ κ±ΈλŸ¬λ‚΄κΈ° - filter(), distinct()

Stream<T> filter(Predicate<? super T> predicate) // 쑰건에 λ§žμ§€ μ•ŠλŠ” μš”μ†Œ 제거
Stream<T> distinct()                             // μ€‘λ³΅μ œκ±°
IntStream intStream = IntStream.of(1, 2, 2, 3, 3, 4, 5, 5, 6);
intStream.distinct().forEach(System.out::print); // 123456
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.filter(i -> i % 2 == 0).forEach(System.out::print);; // 246810 짝수 κ°’λ§Œ 남긴닀.

// 쀑간연산이라 filter μ—¬λŸ¬λ²ˆ μ‚¬μš© κ°€λŠ₯
intStream.filter(i -> i % 2 != 0 && i % 3 != 0).forEach(System.out::print);
intStream.filter(i -> i % 2 != 0).filter(i -> i % 3 != 0).forEach(System.out::print);

슀트림 μ •λ ¬ν•˜κΈ° - sorted()

Stream<T> sorted()                                  // 슀트림 μš”μ†Œμ˜ κΈ°λ³Έ μ •λ ¬(Comparable)둜 μ •λ ¬
Stream<T> sorted(Comparator<? supter T> comparator) // μ§€μ •λœ Comparator둜 μ •λ ¬

Comparator의 comparing()으둜 μ •λ ¬ 기쀀을 제곡(λ°˜ν™˜νƒ€μž…μ΄ comparatorμž„)

πŸ’‘
comparing(Function<T,U> keyExtractor)
comparing(Function<T,U> keyExtractor, Comparator<U> keyComparator)
studentStream.sorted(Comparator.comparing(Student::getBan)
						 .forEach(System.out::println);

μΆ”κ°€ μ •λ ¬ 기쀀을 μ œκ³΅ν•  λ•ŒλŠ” thenComparing()을 μ‚¬μš©

πŸ’‘
thenComparing(Comparator<T> other)
thenComparing(Function<T,U> keyExtractor)
thenComparing(Function<T,U> keyExtractor, Comparator<U> keyComp)
studentStream.sorted(Comparator.comparing(Student::getBan) // λ°˜λ³„λ‘œ μ •λ ¬
						 .thenComparing(Student::getTotalScore)        // μ΄μ λ³„λ‘œ μ •λ ¬
						 .thenComparing(Student::getName))             // μ΄λ¦„λ³„λ‘œ μ •λ ¬
						 .forEach(System.out::println);

슀트림의 μš”μ†Œ λ°˜ν™˜ν•˜κΈ° - map()

πŸ’‘
Stream<R> map(Function<? super T, ? extends R> mapper) // Stream<T> β†’ Stream<R>
Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex1.bak"),
					new File("Ex2.java"), new File("Ex1"), new File("Ex1.txt")); // μœ„μ˜ file 객체λ₯Ό stream으둜 λ§Œλ“€μ—ˆλ‹€.

Stream<String> fileNameStream = fileStream.map(File::getName);
fileNameStream.forEach(System.out::println); // 슀트림의 λͺ¨λ“  파일의 이름을 좜λ ₯

예제) 파일 슀트림(Stream<File>)μ—μ„œ 파일 ν™•μž₯자(λŒ€λ¬Έμž)λ₯Ό 쀑볡없이 뽑아내기

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::println);               // JAVABAKTXT

  • 예제 14-6
    package chapter14;
    
    import java.io.*;
    import java.util.stream.*;
    
    class Main {
        public static void main(String[] args) {
            File[] fileArr = {new File("Ex1.java"), new File("Ex1.bak")
                , new File("Ex2.java"), new File("Ex1"), new File("Ex1.txt")
            }; // file 객체듀이 λ“€μ–΄κ°€μžˆλŠ” λ°°μ—΄
    
            Stream<File> fileStream = Stream.of(fileArr); // μœ„μ˜ file 객체λ₯Ό stream으둜 λ§Œλ“€μ—ˆλ‹€.
            Stream<String> fileNameStream = fileStream.map(File::getName);
            fileNameStream.forEach(System.out::println);
            System.out.println("###########");
    
            fileStream = Stream.of(fileArr);                            // fileStream을 λ‹€μ‹œ 생성
            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::println);                    // JAVABAKTXT
        }
    }

슀트림의 μš”μ†Œλ₯Ό μ†ŒλΉ„ν•˜μ§€ μ•Šκ³  엿보기 - peek()

Stream<T> peek(Consumer<? super T> action)    // 쀑간 μ—°μ‚°(μŠ€νŠΈλ¦Όμ„ μ†ŒλΉ„X)
void      forEach(Consumer<? super T> action) // 쀑간 μ—°μ‚°(μŠ€νŠΈλ¦Όμ„ μ†ŒλΉ„O)
fileStream = Stream.of(fileArr);                               // fileStream을 λ‹€μ‹œ 생성
fileStream.map(File::getName)                                  // Stream<FIle> -> Stream<String>
          .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::println);                       // μ΅œμ’…μ—°μ‚° μŠ€νŠΈλ¦Όμ„ μ†ŒλΉ„

슀트림의 μŠ€νŠΈλ¦Όμ„ 슀트림으둜 λ³€ν™˜ - flatMap()

Stream<String[]> strArrStream = Stream.of()
  • 예제 14-7
    package chapter14;
    
    import java.util.*;
    import java.util.stream.*;
    
    class Ex14_7 {
    	public static void main(String[] args) {
    		Stream<String[]> strArrStrm = Stream.of(
    			new String[]{"abc", "def", "jkl"},
    			new String[]{"ABC", "GHI", "JKL"}
    		);
    
        // Stream<Stream<String>> strStrmStrm = strArrStrm.map(Arrays::stream); // 배열을 μ£Όλ©΄ stream으둜 λ°”κΎΌλ‹€. 슀트림의 슀트림이 생성됨
    		Stream<String> strStrm = strArrStrm.flatMap(Arrays::stream);
    
    		strStrm.map(String::toLowerCase)
    			   .distinct()
    			   .sorted()
    			   .forEach(System.out::println);
    		System.out.println();
    
    		String[] lineArr = {
    			"Believe or not It is true",
    			"Do or do not There is no try",
    		};
    
    		Stream<String> lineStream = Arrays.stream(lineArr);
    
    		lineStream.flatMap(line -> Stream.of(line.split(" +")))
    			.map(String::toLowerCase)
    			.distinct()
    			.sorted()
    			.forEach(System.out::println);
    		System.out.println();
    	}
    }

14-35 Optional<T>

  • T νƒ€μž… 객체의 래퍼클래슀 - Optional<T>
    public final class Optional<T> {
    		private final T value; // Tνƒ€μž…μ˜ μ°Έμ‘°λ³€μˆ˜
    				...
    }

14-36 Optional<T> 객체 μƒμ„±ν•˜κΈ°

  • Optional<T> 객체λ₯Ό μƒμ„±ν•˜λŠ” λ‹€μ–‘ν•œ 방법
    String str = "abc";
    Optional<String> optVal  = Optional.of(str);
    Optional<String> optVal2 = Optional.of("abc");
    Optional<String> optVal3 = Optional.of(null);  // NullPointerException λ°œμƒ
    Optional<String> optVal4 = Optional.ofNullable(null); // OK
    public final class Optional<T> {
    		...
        private final T value;
    		...
    }
  • nullλŒ€μ‹  빈 Optional<T> 객체λ₯Ό μ‚¬μš©ν•˜μž
    Optional<String> optVal = null;              // λ„λ‘œ μ΄ˆκΈ°ν™”. λ°”λžŒμ§ν•˜μ§€ μ•ŠμŒ
    Optional<String> optVal2 = Optional.empty(); // 빈 객체둜 μ΄ˆκΈ°ν™”

14-37 Optional<T> 객체의

  • Optional 객체의 κ°’ κ°€μ Έμ˜€κΈ° - get(), orEls(), orElseGet(), orElseThrow()
    Optional<String> optVal = Optional.of("abc");
    String str1 = optVal.get();                  // optVal에 μ €μž₯된 값을 λ°˜ν™˜. null이면 μ˜ˆμ™Έλ°œμƒ
    String str2 = optVal.orElse("");             // optVal에 μ €μž₯된 값이 null일 λ•ŒλŠ”, ""λ₯Ό λ°˜ν™˜
    String str3 = optVal.orElseGet(String::new); // λžŒλ‹€μ‹ μ‚¬μš©κ°€λŠ₯ () -> new String()
    String str4 = optVal.orElseThrow(NullPointerException::new); // 널이면 μ˜ˆμ™Έλ°œμƒ
  • isPresent() - Optional 객체의 값이 null이면 false, μ•„λ‹ˆλ©΄ trueλ₯Ό λ°˜ν™˜
    if (Optional.ofNullable(str).isPresent()) {
    		System.out.println(str);
    }
    
    // ifPresent(Consumer) - 널이 아닐 λ•Œλ§Œ μž‘μ—… μˆ˜ν–‰, 널이면 아무 일도 μ•ˆν•¨
    Optional.ofNullable(str).ifPresent(System.out::println);

예제

// int[] arr = null; // NEP λ°œμƒ
int[] arr = new int[0];
System.out.println("arr.length = " + arr.length);

// Optional<String> opt = null; //  OK. ν•˜μ§€λ§Œ λ°”λžŒμ§X
Optional<String> opt = Optional.empty();
// Optional<String> opt = Optional.of("abc");
System.out.println("opt" + opt);
// System.out.println("opt" + opt.get());

String str = "";
// try {
//     str = opt.get();
// } catch (Exception e) {
//     str = ""; // μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ©΄ λΉˆλ¬Έμžμ—΄("")둜 μ΄ˆκΈ°ν™”
// }

str = opt.orElse("EMPTY");         // Optinal에 μ €μž₯된 값이 null이면 "EMPTY" λ°˜ν™˜
str = opt.orElseGet(() -> new String()); // Optinal에 μ €μž₯된 값이 null이면 "" λ°˜ν™˜
str = opt.orElseGet(String::new);        // Optinal에 μ €μž₯된 값이 null이면 "" λ°˜ν™˜

System.out.println("str = " + str);

14-38 OptionallInt, OptionalLong, OptionalDouble

  • κΈ°λ³Έν˜• 값을 κ°μ‹ΈλŠ” 래퍼클래슀(μ„±λŠ₯ λ•Œλ¬Έμ— μ‚¬μš©ν•œλ‹€.)
    public final class OptionalInt {
    		...
    		private final boolean isPresent; // 값이 μ €μž₯λ˜μ–΄ 있으면 true
    		private final int value;         // int νƒ€μž…μ˜ λ³€μˆ˜
    }
  • OptionallInt의 κ°’ κ°€μ Έμ˜€κΈ° - int getAsInt()
    Optinal ν΄λž˜μŠ€κ°’μ„ λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œ
    Optional<T>T get()
    OptionalIntint getAsInt()
    OptionalLonglong getAsLong()
    OptionalDoubledouble getAsDouble()
  • 빈 Optional κ°μ²΄μ™€μ˜ 비ꡐ
    OptionalInt opt = OptionalInt.of(0);    // OptionalInt에 0을 μ €μž₯
    OptionalInt opt2 = OptionalInt.empty(); // OptionalInt에 0을 μ €μž₯
    
    System.out.println(opt.isPresent());    // true
    System.out.println(opt2.isPresent());   // false
    System.out.println(opt.equals(opt2));   // false

14-40 슀트림의 μ΅œμ’…μ—°μ‚° - forEach()

  • 슀트림의 λͺ¨λ“  μš”μ†Œμ— μ§€μ •λœ μž‘μ—…μ„ μˆ˜ν–‰ - forEach(), forEachOrdered()

    μ•„λž˜ 두 λ©”μ„œλ“œλŠ” 근본적으둜 같은데 병렬일 λ•Œ μ°¨μ΄κ°€λ‚œλ‹€.

    void forEach(Consumer<? super T> action)        // λ³‘λ ¬μŠ€νŠΈλ¦ΌμΈ 경우 μˆœμ„œκ°€ 보μž₯λ˜μ§€ μ•ŠμŒ
    void forEachOrdered(Consumer<? super T> action) // λ³‘λ ¬μŠ€νŠΈλ¦ΌμΈ κ²½μš°μ—λ„ μˆœμ„œκ°€ 보μž₯됨
    // 직렬 슀트림, μŠ€νŠΈλ¦Όμ€ 기본적으둜 직렬이기 λ•Œλ¬Έμ— μƒλž΅ κ°€λŠ₯
    IntStream.range(1, 10).sequential().forEach(System.out::println);
    IntStream.range(1, 10).sequential().forEachOrdered(System.out::println);
    // 병렬 슀트림
    IntStream.range(1, 10).parallel().forEach(System.out::println);
    IntStream.range(1, 10).parallel().forEachOrdered(System.out::println);

병렬 β†’ μ—¬λŸ¬ μ“°λ ˆλ“œκ°€ λ‚˜λˆ μ„œ μž‘μ—…ν•œλ‹€.

14-41 슀트림의 μ΅œμ’…μ—°μ‚° - 쑰건 검사

  • allMath(), anyMatch(), noneMatch()
    boolean allMatch (Predicate<? super T> predicate) // λͺ¨λ“  μš”μ†Œκ°€ 쑰건을 λ§Œμ‘±μ‹œν‚€λ©΄ true
    boolean anyMatch (Predicate<? super T> predicate) // ν•œ μš”μ†ŒλΌλ„ 쑰건을 λ§Œμ‘±μ‹œν‚€λ©΄ true
    boolean noneMatch (Predicate<? super T> predicate) // λͺ¨λ“  μš”μ†Œκ°€ 쑰건을 λ§Œμ‘±μ‹œν‚€μ§€ μ•ŠμœΌλ©΄ true
    boolean hasFailedStu = studentStream.anyMatch(s -> s.getTotalScore() <= 100); // λ‚™μ œμžκ°€ μžˆλŠ”μ§€?
  • 쑰건에 μΌμΉ˜ν•˜λŠ” μš”μ†Œ μ°ΎκΈ° - findFirst(), findAny()

    쑰건에 λ§žλŠ” 게 없을 μˆ˜λ„ 있기 λ•Œλ¬Έμ— Optional둜 λ˜μ–΄μžˆλ‹€.

    Optional<T> findFirst() // 첫 번째 μš”μ†Œλ₯Ό λ°˜ν™˜. 순차 μŠ€νŠΈλ¦Όμ— μ‚¬μš©
    Optional<T> findAny()   // μ•„λ¬΄κ±°λ‚˜ ν•˜λ‚˜λ₯Ό λ°˜ν™˜. 병렬 μŠ€νŠΈλ¦Όμ— μ‚¬μš©
    Optional<Student> result = studentStream.filter(s -> s.getTotalScore() <= 100).findFirst();
    Optional<Student> result = studentStream.parallel().filter(s -> s.getTotalScore() >= 100).findAny();

14-42, 43 슀트림의 μ΅œμ’…μ—°μ‚° - reduce()

슀트림의 μ΅œμ’…μ—°μ‚° 쀑에 제일 μ€‘μš”ν•˜λ‹€. / accumulate - λˆ„μ ν•˜λ‹€

  • 슀트림의 μš”μ†Œλ₯Ό ν•˜λ‚˜μ”© 쀄여가며 λˆ„μ μ—°μ‚° μˆ˜ν–‰ - reduce()

핡심 : reduce(identity, accumulator)

14-45 collector()와 Collectors

μ΅œμ’…μ—°μ‚°

  1. reduce() : 리듀싱
  1. collect() : 그룹별 리듀싱

collect() : μ΅œμ’…μ—°μ‚°

collector : μΈν„°νŽ˜μ΄μŠ€

collectors : 클래슀

  • collect()λŠ” Collectorλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ ν•˜λŠ” 슀트림의 μ΅œμ’…μ—°μ‚°

14-46 μŠ€νŠΈλ¦Όμ„ μ»¬λ ‰μ…˜, λ°°μ—΄λ‘œ λ³€ν™˜

  • μŠ€νŠΈλ¦Όμ„ μ»¬λ ‰μ…˜μœΌλ‘œ λ³€ν™˜ - toList(), toSet(), toMap(), toCollection()
    List<String> names = stuStream.map(Student::getName)         // Stream<Student> -> Stream<String>
    															.collect(Collectors.toList()); // Stream<String> -> List<String>
    ArrayList<String> list = names.stream()
    	                            .collect(Collectors.toList()); // Stream<String> -> ArrayList<String>
    Map<String, Person> map = personStream
    	.collect(Collectors.toMap(p -> p.getRegId(), p -> p)); // Stream<Person> -> Map<String, Person>
  • μŠ€νŠΈλ¦Όμ„ λ°°μ—΄λ‘œ λ³€ν™˜ - toArray()
    Student[] stuNames = studentStream.toArray(Student[]::new); // OK
    Student[] stuNames = studentStream.toArray(); // μ—λŸ¬
    Object[] stuNames = studentStream.toArray(); // OK.

14-47 슀트림의 톡계 - counting(), summingInt()

  • 슀트림의 톡계정보 제곡 - counting(), summingInt(), maxBy(), minBy(), …
    long count = stuStream.count();
    long count = stuStream.collect(counting()); // Collectors.counting()
    long totalScore = stuStream.mapToInt(Student::getTotalScore).sum(); // IntStream의 sum(), μ „μ²΄λ§Œ κ°€λŠ₯
    long totalScore = stuStream.collect(summingInt(Student::getTotalScore)); // 그룹별 κ°€λŠ₯
    

14-48 μŠ€νŠΈλ¦Όμ„ 리듀싱 - reducing()

  • μŠ€νŠΈλ¦Όμ„ 리듀싱 - reducing()

14-53 슀트림의 κ·Έλ£Ήν™” - groupingBy()

  • 슀트림의 μš”μ†Œλ₯Ό κ·Έλ£Ήν™”
    Collector groupingBy(Function classifier)
    Collector groupingBy(Function classifier, Collector downstream)
    Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)
    Map<Integer, List<Student>> stuByBan = stuStream               // 학생을 λ°˜λ³„λ‘œ κ·Έλ£Ήν™”
    							.collect(groupingBy(Student::getBan, toList())); // toList() μƒλž΅κ°€λŠ₯
    Map<Integer, Map<Integer, List<Student>> stuByHak // 닀쀑 κ·Έλ£Ήν™”
    stuStream.collect(groupingBy(Student::getHak,     // 1. 학년별 κ·Έλ£Ήν™”
    									groupingBy(Student::getBan)     // 2. λ°˜λ³„ κ·Έλ£Ήν™”
    					));
  • 슀트림의 μš”μ†Œλ₯Ό 2λΆ„ν• 


Uploaded by N2T