์ž๋ฐ”์˜ ์ •์„ - ์ŠคํŠธ๋ฆผ(Stream)

๋ธ”๋กœ๊ทธ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค! ๐Ÿก’ integer.blog



์ž๋ฐ”์˜ ์ •์„(๋‚จ๊ถ์„ฑ ์ €) 2๊ถŒ ํ•™์Šต๋‚ด์šฉ ์ •๋ฆฌ

1. ์ŠคํŠธ๋ฆผ(Stream)

์ŠคํŠธ๋ฆผ์€ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์ถ”์ƒํ™”ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š”๋ฐ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ๋“ค์„ ์ •์˜ํ•ด ๋†“์•˜๋‹ค.

Collection์ด๋‚˜ Iterator ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•ด์„œ ์ปฌ๋ ‰์…˜์„ ๋‹ค๋ฃจ๋Š” ๋ฐฉ์‹์„ ํ‘œ์ค€ํ™” ํ–ˆ์ง€๋งŒ, ๊ฐ ์ปฌ๋ ‰์…˜ ํด๋ž˜์Šค์—๋Š” ๊ฐ™์€ ๊ธฐ๋Šฅ์˜ ๋ฉ”์„œ๋“œ๋“ค์ด ์ค‘๋ณตํ•ด์„œ ์ •์˜๋˜์–ด ์žˆ๋‹ค. List๋ฅผ ์ •๋ ฌํ•  ๋•Œ๋Š” Collection.sort()๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๊ณ , ๋ฐฐ์—ด์„ ์ •๋ ฌํ•  ๋•Œ๋Š” Arrays.sort()๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋งˆ๋‹ค ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ๋‹ค๋ฃจ์–ด์•ผํ•˜๋Š” ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ๊ฒƒ์ด Stream ์ด๋‹ค.

// ๊ธฐ์กด
String[] strArr = {"aaa", "bbb", "ccc"};
List<String> strList = Arrays.asList(strArr);

// ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
Stream<String> strStream1 = strList.stream();
Stream<String> strStream2 = Arrays.stream(strArr);

// ์ŠคํŠธ๋ฆผ ์ถœ๋ ฅ
strStream1.sorted().forEach(System.out::println);
strStream2.sorted().forEach(System.out::println);
  • ์ŠคํŠธ๋ฆผ์€ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ธฐ๋งŒ ํ•  ๋ฟ, ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ŠคํŠธ๋ฆผ์€ ํ•œ๋ฒˆ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ซํ˜€์„œ ๋‹ค์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
  • ์ŠคํŠธ๋ฆผ์€ ์ž‘์—…์„ ๋‚ด๋ถ€ ๋ฐ˜๋ณต์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action); // ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๋„ ์ฒดํฌ
    
    for(T t : src)  {
      action.accept(T);
    }
  }
  • ์ŠคํŠธ๋ฆผ์€ ์ตœ์ข… ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋˜๊ธฐ ์ „๊นŒ์ง€ ์ค‘๊ฐ„ ์—ฐ์‚ฐ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ์ค‘๊ฐ„ ์—ฐ์‚ฐ์„ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ๋‹จ์ง€ ์–ด๋–ค ์ž‘์—…์ด ์ˆ˜ํ–‰๋˜์–ด์•ผํ•˜๋Š”์ง€๋ฅผ ์ง€์ •ํ•ด์ฃผ๋Š” ๊ฒƒ์ผ ๋ฟ์ด๋‹ค.
  • ์š”์†Œ์˜ ํƒ€์ž…์ด T์ธ ์ŠคํŠธ๋ฆผ์€ Stream์ด์ง€๋งŒ, ์˜คํ† ๋ฐ•์‹ฑ/์–ธ๋ฐ•์‹ฑ์˜ ๋น„ํšจ์œจ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ ์†Œ์Šค์˜ ์š”์†Œ๋ฅผ ๊ธฐ๋ณธํ˜•์œผ๋กœ ๋‹ค๋ฃจ๋Š” InsStream, LongStream, DoubleStream์ด ์ œ๊ณต๋œ๋‹ค.
  • ๋ณ‘๋ ฌ์ŠคํŠธ๋ฆผ์€ ๋‚ด๋ถ€์ ์œผ๋กœ fork&join ํ”„๋ ˆ์ž„์›์„ ์ด์šฉํ•ด์„œ ์ž๋™์ ์œผ๋กœ ์—ฐ์‚ฐ์„ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ŠคํŠธ๋ฆผ์— parallel() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ณ‘๋ ฌ๋กœ ์—ฐ์‚ฐํ•˜๊ณ , parallel()์„ ์ทจ์†Œํ•˜๋ ค๋ฉด sequential()์„ ํ˜ธ์ถœํ•œ๋‹ค.
  • parallel()๊ณผ sequential()์€ ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๊ทธ์ € ์ŠคํŠธ๋ฆผ์˜ ์†์„ฑ์„ ๋ณ€๊ฒฝํ•  ๋ฟ์ด๋‹ค.
  int sum = strStream.parallelStream()
                     .mapToInt(s -> s.length())
                     .sum();

2. ์ŠคํŠธ๋ฆผ ๋งŒ๋“ค๊ธฐ

์ŠคํŠธ๋ฆผ์˜ ์†Œ์Šค๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ๋Œ€์ƒ์€ ๋ฐฐ์—ด, ์ปฌ๋ ‰์…˜, ์ž„์˜์˜ ์ˆ˜ ๋“ฑ ๋‹ค์–‘ํ•˜๋‹ค.

2.1. ์ปฌ๋ ‰์…˜

์ปฌ๋ ‰์…˜์˜ ์ตœ๊ณ  ์กฐ์ƒ์ธ Collection์— stream()์ด ์ •์˜๋˜์–ด ์žˆ์–ด์„œ Collection์˜ ์ž์†์ธ List์™€ Set์„ ๊ตฌํ˜„ํ•œ ์ปฌ๋ ‰์…˜ ํด๋ž˜์Šค๋“ค์€ ๋ชจ๋‘ stream()์œผ๋กœ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Stream<T> Collection.stream()

// List๋กœ๋ถ€ํ„ฐ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> intStream = list.stream();  // list๋ฅผ ์†Œ์Šค๋กœ ํ•˜๋Š” ์ปฌ๋ ‰์…˜ ์ƒ์„ฑ

2.2. ๋ฐฐ์—ด

๋ฐฐ์—ด์„ ์†Œ์Šค๋กœ ํ•˜๋Š” ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด Stream๊ณผ Arrays์— static๋ฉ”์„œ๋“œ๋กœ ์ •์˜๋˜์–ด ์žˆ๋‹ค.

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);
  • int, long, double๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธํ˜• ๋ฐฐ์—ด์„ ์†Œ์Šค๋กœ ํ•˜๋Š” ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
  IntStream IntStream.of(int...values)
  IntStream IntStream.of(int[])
  IntStream Arrays.stream(int[])
  IntStream Arrays.stream(int[] array, int startInclusive, endExclusive)

2.3. ํŠน์ • ๋ฒ”์œ„์˜ ์ •์ˆ˜

  IntStream intStream = IntStream.range(1, 5);      // 1,2,3,4
  IntStream intStream = IntStream.rangeClosed(1, 5) // 1,2,3,4,5

2.4. ์ž„์˜์˜ ์ˆ˜

๋‚œ์ˆ˜๋ฅผ ์ƒ์ƒํ•˜๋Š” Randomํด๋ž˜์Šค์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.

//ํ•ด๋‹น ํƒ€์ž…์˜ ๋‚œ์ˆ˜๋“ค๋กœ ์ด๋ฃจ์–ด์ง„ ์ŠคํŠธ๋ฆผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋“ค

IntStream ints()
LongStream longs()
DoubleStream doubles()

//์œ„์˜ ๋ฉ”์„œ๋“œ๋“ค์ด ๋ฐ˜ํ™˜ํ•˜๋Š” ์ŠคํŠธ๋ฆผ์€ ํฌ๊ธฐ๊ฐ€ ์ •ํ•ด์ง€์ง€ ์•Š์€ ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ(infinite stream)์ด๋ฏ€๋กœ  
//limit()๋„ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ํฌ๊ธฐ๋ฅผ ์ œํ•œํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค. limit()์€ ์ŠคํŠธ๋ฆผ์˜ ๊ฐœ์ˆ˜๋ฅผ ์ง€์ •ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.

IntStream intStream = new Random().ints();  // ๋ฌดํ•œ์ŠคํŠธ๋ฆผ
intStream.limit(5).forEach(System.out::println);  // 5๊ฐœ์˜ ์š”์†Œ๋งŒ ์ถœ๋ ฅ


//๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ŠคํŠธ๋ฆผ์˜ ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•˜๋ฉด limit()์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
IntStream ints(long streamSize)
LongStream longs(long streamSize)
DoubleStream doubles(long streamSize)

//์ง€์ •๋œ ๋ฒ”์œ„์˜ ๋‚œ์ˆ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์ŠคํŠธ๋ฆผ์„ ์–ป๋Š” ๋ฉ”์„œ๋“œ๋“ค(end๋Š” ๋ฒ”์œ„์— ๋ฏธํฌํ•จ)
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)
DoubleStream doubles(long streamSize, double begin, double end)

2.5. ๋žŒ๋‹ค์‹ - iterate(), generate()

Stream ํด๋ž˜์Šค์˜ iterate()์™€ generate()๋Š” ๋žŒ๋‹ค์‹์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์„œ, ์ด ๋žŒ๋‹ค์‹์— ์˜ํ•ด ๊ณ„์‚ฐ๋˜๋Š” ๊ฐ’๋“ค์„ ์š”์†Œ๋กœ ํ•˜๋Š” ๋ฌดํ•œ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•œ๋‹ค.

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
static <T> Stream<T> generate(Supplier<T> s)
  • iterate()๋Š” ์”จ์•—๊ฐ’(seed)์œผ๋กœ ์ง€์ •๋œ ๊ฐ’๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ๋žŒ๋‹ค์‹ f์— ์˜ํ•ด ๊ณ„์‚ฐ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ seed๊ฐ’์œผ๋กœ ๊ณ„์‚ฐ์„ ๋ฐ˜๋ณตํ•œ๋‹ค.
  • generate()๋Š” iterate()์™€ ๋‹ฌ๋ฆฌ ์ด์ „ ๊ฒฐ๊ณผ๋ฅผ ์ด์šฉํ•ด์„œ ๋‹ค์Œ ์š”์†Œ๋ฅผ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • iterate()์™€ generate()์— ์˜ํ•ด ์ƒ์„ฑ๋œ ์ŠคํŠธ๋ฆผ์€ ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ ํƒ€์ž…์˜ ์ฐธ์กฐ๋ณ€์ˆ˜๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์—†๋‹ค.
// iterate()
Stream<Integer> evenStream = Stream.iterate(0, n->n+2); // 0, 2, 4, 6, ...

// generate()
Stream<Double> randomStream = Stream.generate(Math::random);
Stream<Integer> oneStream = Stream.generate(()->1);

2.6. ๋นˆ ์ŠคํŠธ๋ฆผ

์š”์†Œ๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋Š” ๋นˆ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
์ŠคํŠธ๋ฆผ์— ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ํ•˜๋‚˜๋„ ์—†์„ ๋•Œ, null๋ณด๋‹ค ๋นˆ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ๋‚ซ๋‹ค.

Stream emptyStream = Stream.empty();  // empty()๋Š” ๋นˆ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
long count = emptyStream.count();   // count์˜ ๊ฐ’์€ 0

2.7. ๋‘ ์ŠคํŠธ๋ฆผ์˜ ์—ฐ๊ฒฐ

Stream์˜ static ๋ฉ”์„œ๋“œ์ธ concat()์„ ์‚ฌ์šฉํ•ด์„œ ๋‘ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜๋กœ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
๋‘ ์ŠคํŠธ๋ฆผ์€ ๊ฐ™์€ ํƒ€์ž…์ด์–ด์•ผ ํ•œ๋‹ค.

String[] str1 = {"123", "456", "789"}
String[] str2 = {"ABC", "abc", "DEF"}

Stream<String> strs1 = Stream.of(str1);
Stream<String> strs2 = Stream.of(str2);
Stream<String> strs3 = Stream.concat(strs1, strs2);   // ๋‘ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜๋กœ ์—ฐ๊ฒฐ

3. ์ŠคํŠธ๋ฆผ์˜ ์ค‘๊ฐ„์—ฐ์‚ฐ

3.1. ์ŠคํŠธ๋ฆผ ์ž๋ฅด๊ธฐ

Stream skip(long n) // ์ฒ˜์Œ n๊ฐœ์˜ ์š”์†Œ ๊ฑด๋„ˆ๋›ฐ๊ธฐ
Strema limit(long maxSize) // ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ maxSize๊ฐœ๋กœ ์ œํ•œ

IntStream exampleStream = IntStream.rangeClosed(1, 10); // 1~10์˜ ์š”์†Œ๋ฅผ ๊ฐ€์ง„ ์ŠคํŠธ๋ฆผ
exampleStream.skip(3).limit(5).forEach(System.out::print);  // 45678

3.2. ์ŠคํŠธ๋ฆผ ์š”์†Œ ๊ฑธ๋Ÿฌ๋‚ด๊ธฐ

distinct()๋Š” ์ŠคํŠธ๋ฆผ์—์„œ ์ค‘๋ณต๋œ ์š”์†Œ๋“ค ์ œ๊ฑฐ
filter()๋Š” ์ฃผ์–ด์ง„ ์กฐ๊ฑด(Predicate)์— ๋งž์ง€ ์•Š๋Š” ์š”์†Œ๋ฅผ ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค.

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

// filter()
IntStream example2Stream = IntStream.rangeClosed(1, 10);
example2Stream.filter(i -> i%2 ==0).forEach(System.out::print); // 246810

// filter()๋ฅผ ๋‹ค๋ฅธ ์กฐ๊ฑด์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉ. ๋‘ ๋ฌธ์žฅ์˜ ๊ฒฐ๊ณผ๋Š” ๊ฐ™๋‹ค.
example2Stream.filter(i -> i%2!=0 && i%3!=0).forEach(System.out::print);  //157
example2Stream.filter(i -> i%2!=0).filter(i -> i%3!=0).forEach(System.out::print);

3.3. ์ŠคํŠธ๋ฆผ ์ •๋ ฌ

Stream sorted()
Stream sorted(Comparator<? super T> comparator)

Stream<String> strStream = Stream.of("dd","aaa","CC","cc","b");
strStream.sorted().forEach(System.out::print); // CCaaabccdd

// ๊ธฐ๋ณธ์ •๋ ฌ ์—ญ์ˆœ
strStream.sorted(Comparator.reverseOrder());

// ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์—†์ด
strStream.sorted(String.CASE_INSESITIVE_ORDER)

// ๊ธธ์ด ์ˆœ ์ •๋ ฌ
strStream.sorted(Comparator.comparing(String::length))

//์ •๋ ฌ ์กฐ๊ฑด์„ ์ถ”๊ฐ€ํ•  ๋•Œ๋Š” thenComparing() ์‚ฌ์šฉ
studentStream.sorted(Comparator.comparing(Student::getBan)
                      .thenComparing(Student::getTotalScore)
                      .thenComparing(Student::getName)
                      .forEach(System.out::println);

3.4. ์ŠคํŠธ๋ฆผ ๋ณ€ํ™˜

์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์— ์ €์žฅ๋œ ๊ฐ’ ์ค‘์—์„œ ์›ํ•˜๋Š” ํ•„๋“œ๋งŒ ๋ฝ‘์•„๋‚ด๊ฑฐ๋‚˜, ํŠน์ • ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•  ๋•Œ
map() ์‚ฌ์šฉ

// map()์œผ๋กœ Stream<File>์„ Stream<String>์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

Stream<File> fileStream = Stream.of(new File("Ex1.java"), new File("Ex2.java"));
Stream<String> filenameStream = fileStream.map(File::getName);
filenameStream.forEach(System.out::println);

// map() ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉ

fileStream.map(File::getName)   // Stream<File> -> Stream<String>
  .filter(s -> s.indexOf('.')!= -1)   // ํ™•์žฅ์ž ์—†๋Š” ๊ฒƒ ์ œ์™ธ
  .map(s -> s.substring(s.indexOf('.')+1))    // Stream<String> -> Stream<String>
  .map(String::toUpperCase)   // ๋ชจ๋‘ ๋Œ€๋ฌธ์ž๋กœ ๋ณ€ํ™˜
  .distinct()   
  .forEach(System.out::print);

4. ์ŠคํŠธ๋ฆผ์˜ ์ค‘๊ฐ„์—ฐ์‚ฐ

4.1. ์ŠคํŠธ๋ฆผ ์กฐํšŒ

์—ฐ์‚ฐ๊ณผ ์—ฐ์‚ฐ ์‚ฌ์ด์— ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋ฉด peek() ์‚ฌ์šฉ
forEach()์™€ ๋‹ฌ๋ฆฌ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์†Œ๋ชจํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์—ฐ์‚ฐ ์‚ฌ์ด์— ์—ฌ๋Ÿฌ ๋ฒˆ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

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

4.2. Stream ์ŠคํŠธ๋ฆผ์„ ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜

map()์€ ์—ฐ์‚ฐ์˜ ๊ฒฐ๊ณผ๋กœ Stream ํƒ€์ž…์˜ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ,
์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ˆซ์ž๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ IntStream๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ๋” ์œ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)  
IntStream mapToInt(ToIntFunction<? super T> mapper)
LongStream mapToLong(ToLongFunction<? super T> mapper)

// ์ŠคํŠธ๋ฆผ์— ํฌํ•จ๋œ ๋ชจ๋“  ํ•™์ƒ์˜ ์„ฑ์ ์„ ํ•ฉ์‚ฐํ•˜๊ธฐ ์œ„ํ•ด map()์œผ๋กœ ์ƒˆ๋กœ์šด ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
Stream<Integer> studentScoreStream = studentStream.map(Student::getTotalScore);

// ์• ์ดˆ์— Stream<Integer>๊ฐ€ ์•„๋‹Œ IntStream ํƒ€์ž…์˜ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑํ•˜๊ธฐ
// ์„ฑ์ ์„ ๋”ํ•  ๋•Œ Integer๋ฅผ int๋กœ ๋ณ€ํ™˜ํ•  ํ•„์š” ์—†์–ด์„œ ๋” ํšจ์œจ์ ์ด๋‹ค.
IntStream studentScoreStream = studentStream.mapToInt(Student::getTotalScore);
int allTotalScore = studentScoreStream.sum();

count()๋งŒ ์ง€์›ํ•˜๋Š” Stream์™€ ๋‹ฌ๋ฆฌ IntStream๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆซ์ž๋ฅผ ๋‹ค๋ฃจ๋Š”๋ฐ ํŽธ๋ฆฌํ•œ ๋ฉ”์„œ๋“œ๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.

int sum()                   ์ŠคํŠธ๋ฆผ ๋ชจ๋“  ์š”์†Œ์˜ ์ดํ•ฉ
OptionalDouble average()    sum()/count()
OptionalInt max()           ์ŠคํŠธ๋ฆผ ์š”์†Œ ์ค‘ ์ œ์ผ ํฐ ๊ฐ’
OPtionalInt min()           ์ŠคํŠธ๋ฆผ ์š”์†Œ ์ค‘ ์ œ์ผ ์ž‘์€ ๊ฐ’

* max()์™€ min()์€ Stream์—๋„ ์ •์˜๋˜์–ด ์žˆ์ง€๋งŒ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ Comparator๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.
* ์ด ๋ฉ”์„œ๋“œ๋“ค์€ ์ตœ์ข…์—ฐ์‚ฐ์ด๋‹ค.
* sum()๊ณผ average()๋ฅผ ๋ชจ๋‘ ํ˜ธ์ถœํ•˜๋ ค๋ฉด summaryStatistics() ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ

IntStream์„ Stream๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ๋Š” mapToObj() ์‚ฌ์šฉ
IntStream์„ Steram๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ๋Š” boxed() ์‚ฌ์šฉ

//mapToObj() ์‚ฌ์šฉํ•ด์„œ IntStream์„ Stream<String>์œผ๋กœ ๋ณ€ํ™˜

//๋กœ๋˜๋ฒˆํ˜ธ ์ƒ์„ฑ๊ธฐ
IntStream intStream = new Random().ints(1,46);  // 1~45์‚ฌ์ด์˜ ์ •์ˆ˜
Stream<String> lottoStream = intStream.distinct().limit(6).sorted()
                                .mapToObj(i->i+",");    // ์ •์ˆ˜๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
lottoStream.forEach(System.out::print); // 12,14,23,29,45

CharSequence์— ์ •์˜๋œ chars()๋Š” String์ด๋‚˜ StringBuffer์— ์ €์žฅ๋œ ๋ฌธ์ž๋“ค์„ IntStream์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

IntStream charStream = "12345".chars();
int charSum = charStream.map(ch-> ch-'0').sum() // charSum=15

Stream -> IntStream ๋ณ€ํ™˜ ์‹œ, mapToInt(Integer::parseInt) ์‚ฌ์šฉ
Stream -> IntStream ๋ณ€ํ™˜ ์‹œ, mapToInt(Integer::intValue) ์‚ฌ์šฉ

4.3. Stream๋ฅผ Stream๋กœ ๋ณ€ํ™˜

flatMap() ์‚ฌ์šฉ

// ์š”์†Œ๊ฐ€ ๋ฌธ์ž์—ด ๋ฐฐ์—ด(String[])์ธ ์ŠคํŠธ๋ฆผ
Stream<String[]> strArrStrm = Stream.of(
                              new String[]{"abc", "def", "ghi"},
                              new String[]{"ABC", "DEF"m "JKLMN"}
                              );

// Stream<String[]>์„ map(Arrays::stream)์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๊ฒฐ๊ณผ๋Š” Stream<String>์ด ์•„๋‹Œ Stream<Stream<String>>.
// ์ฆ‰ ์ŠคํŠธ๋ฆผ์˜ ์ŠคํŠธ๋ฆผ์ด๋‹ค.
Stream<Stream<String>> strStrStrm = strArrStrm.map(Arrays::stream);

//map() ๋Œ€์‹  flatMap() ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ ๋ฐฐ์—ด์ด ํ•˜๋‚˜์˜ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ ๋œ๋‹ค.
Stream<String> strStrm = strArrStrm.flatMap(Arrays::stream);

5. Optional

Optional์€ ์ง€๋„ค๋ฆญ ํด๋ž˜์Šค๋กœ ‘Tํƒ€์ž…์˜ ๊ฐ์ฒด’๋ฅผ ๊ฐ์‹ธ๋Š” ๋ž˜ํผ ํด๋ž˜์Šค๋‹ค.
๊ทธ๋ž˜์„œ Optionalํƒ€์ž…์˜ ๊ฐ์ฒด์—๋Š” ๋ชจ๋“  ํƒ€์ž…์˜ ์ฐธ์กฐ๋ณ€์ˆ˜๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ๋‹ค.

์ตœ์ข… ์—ฐ์‚ฐ์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋ƒฅ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ Optional ๊ฐ์ฒด์— ๋‹ด์•„์„œ ๋ฐ˜ํ™˜ํ•˜๋ฉด,
๋ฐ˜ํ™˜๋œ ๊ฒฐ๊ณผ๊ฐ€ null์ธ์ง€ ๋งค๋ฒˆ if๋ฌธ์œผ๋กœ ์ฒดํฌํ•˜๋Š” ๋Œ€์‹  Optional์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ„๋‹จํžˆ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

5.1. Optional ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ธฐ

of() ๋˜๋Š” ofNullable() ์‚ฌ์šฉ

//์ฐธ์กฐ๋ณ€์ˆ˜์˜ ๊ฐ’์ด null์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฉด of()๋Œ€์‹  ofNullable() ์‚ฌ์šฉ
Optional<String> optVal = Optional.of(null); //NullPointerException ๋ฐœ์ƒ
Optional<String> optVal = Optional.ofNullable(null);  // OK

//Optional<T> ํƒ€์ž…์˜ ์ฐธ์กฐ๋ณ€์ˆ˜๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ empty() ์‚ฌ์šฉ
//null๋กœ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ empty()๋กœ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์ง
Optional<String> optVal = null;
Optional<String> optVal = Optional.empty();  // ๋นˆ ๊ฐ์ฒด๋กœ ์ดˆ๊ธฐํ™”

5.2. Optional ๊ฐ์ฒด์˜ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ

get(), orElse()

Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get();   // optVal์— ์ €์žฅ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜. null์ด๋ฉด ์˜ˆ์™ธ๋ฐœ์ƒ
String str2 = optVal.orElse("");  // optVal์— ์ €์žฅ๋œ ๊ฐ’์ด null์ด๋ฉด ""๋ฅผ ๋ฐ˜ํ™˜
String str3 = optVal.orElseGet(String::new);  // null์„ ๋Œ€์ฒดํ•  ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋žŒ๋‹ค์‹ ์ง€์ •
String str4 = optVal.orElseThrow(NullPointerException::new);  // null์ผ ๋•Œ ์ง€์ •๋œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ

isPresent()๋Š” Optional ๊ฐ์ฒด์˜ ๊ฐ’์ด null์ด๋ฉด false๋ฅผ, ์•„๋‹ˆ๋ฉด true ๋ฐ˜ํ™˜
ifPresent(Consumer block)๋Š” ๊ฐ’์ด ์žˆ์œผ๋ฉด ์ฃผ์–ด์ง„ ๋žŒ๋‹ค์‹ ์‹คํ–‰, ์—†์œผ๋ฉด ์•„๋ฌด์ผ ์•ˆํ•จ

//์กฐ๊ฑด๋ฌธ
if(str!=null) {
  System.out.println(str);
}

//์œ„์˜ ์กฐ๊ฑด๋ฌธ์„ isPresent()๋กœ ๊ตฌํ˜„
if(Optional.ofNullable(str).isPresent())  {
  System.out.println(str);
}

//์œ„์˜ ์กฐ๊ฑด๋ฌธ์„ ifPresent()๋กœ ๊ตฌํ˜„
Optional.ofNullable(str).ifPresent(System.out::println);

6. ์ŠคํŠธ๋ฆผ์˜ ์ตœ์ข… ์—ฐ์‚ฐ

์ตœ์ข… ์—ฐ์‚ฐ์€ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์†Œ๋ชจํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ ๋‹ค. ๊ทธ๋ž˜์„œ ์ตœ์ข… ์—ฐ์‚ฐํ›„์—๋Š” ์ŠคํŠธ๋ฆผ์ด ๋‹ซํ˜€์„œ ๋”์ด ์ƒ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

6.1. forEach()

๋ฐ˜ํ™˜ ํƒ€์ž…์ด void์ด๋ฏ€๋กœ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์šฉ๋„๋กœ ๋งŽ์ด ์‚ฌ์šฉ

6.2. ์กฐ๊ฑด๊ฒ€์‚ฌ

allMatch(), anyMatch(), noneMatch(), findFirst(), findAny()

boolean allMatch (Predicate<? super T> predicate)
boolean anyMatch (Predicate<? super T> predicate)
boolean noneMatch (Predicate<? super T> predicate)

// ํ•™์ƒ์„ฑ์  ์ŠคํŠธ๋ฆผ์—์„œ ์ด์ ์ด ๋‚™์ œ์  ์ดํ•˜์ธ ํ•™์ƒ ํ™•์ธ
boolean noFailed = stuStream.anyMatch(s->s.getTotalScore()<=100);

// ์ŠคํŠธ๋ฆผ ์š”์†Œ ์ค‘ ์กฐ๊ฑด์— ์ผ์น˜ํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ๊ฒƒ ๋ฐ˜ํ™˜
Optional<Student> stu = stuStream.filter(s->s.getTotalScore()<=100).findFirst();

// ๋ณ‘๋ ฌ์ŠคํŠธ๋ฆผ์˜ ๊ฒฝ์šฐ findFirst() ๋Œ€์‹  findAny() ์‚ฌ์šฉ
Optional<Student> stu = parallelStream.filter(s->s.getTotalScore()<=100).findAny();

6.3. ํ†ต๊ณ„

count(), sum(), average(), max(), min()

// ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์ด ์•„๋‹Œ ๊ฒฝ์šฐ ํ†ต๊ณ„ ๊ด€๋ จ ๋ฉ”์„œ๋“œ๋Š” ์•„๋ž˜ 3๊ฐœ ๋ฟ์ด๋‹ค.
// ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์˜ min(), max()์™€ ๋‹ฌ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ Comparator๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
long count()
Optional<T> max(Comparator<? super T> comparator)
Optional<T> min(Comparator<? super T> comparator)

6.4. reduce()

reduce()๋Š” ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ค„์—ฌ๋‚˜๊ฐ€๋ฉด์„œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ตœ์ข…๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
๊ทธ๋ž˜์„œ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž…์ด BinaryOperator ์ด๋‹ค.
์ฒ˜์Œ ๋‘ ์š”์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์—ฐ์‚ฐํ•œ ๊ฒฐ๊ณผ๋กœ ๊ทธ ๋‹ค์Œ ์š”์†Œ์™€ ์—ฐ์‚ฐํ•œ๋‹ค.
๋ชจ๋“  ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์†Œ๋ชจํ•˜๊ฒŒ ๋˜๋ฉด ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Optional<T> reduce(BinaryOperator<T> accumulator)

reduce()์˜ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์€ ์ดˆ๊ธฐ๊ฐ’๊ณผ ์–ด๋–ค ์—ฐ์‚ฐ(BinaryOperator)์œผ๋กœ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ค„์—ฌ๋‚˜๊ฐˆ ๊ฒƒ์ธ์ง€๋งŒ ๊ฒฐ์ •ํ•˜๋ฉด ๋œ๋‹ค.

7. collect()

์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ์ตœ์ข… ์—ฐ์‚ฐ
collect()๊ฐ€ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ธฐ ์œ„ํ•œ ์ˆ˜์ง‘ ๋ฐฉ๋ฒ•์ด ์ •์˜๋œ ๊ฒƒ์ด collector.
collector๋Š” Collector์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ.

collect() ์ŠคํŠธ๋ฆผ์˜ ์ตœ์ข… ์—ฐ์‚ฐ. ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ collector๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
Collector ์ธํ„ฐํŽ˜์ด์Šค. collector๋Š” ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค.
Collectors ํด๋ž˜์Šค. static ๋ฉ”์„œ๋“œ๋กœ ๋ฏธ๋ฆฌ ์ž‘์„ฑ๋œ collector๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

collect()์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž…์€ Collector์ธ๋ฐ. ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ Collector๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ๋œป.
๊ทธ๋ฆฌ๊ณ  collect()๋Š” ์ด ๊ฐ์ฒด์— ๊ตฌํ˜„๋œ ๋ฐฉ๋ฒ•๋Œ€๋กœ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ์ˆ˜์ง‘ํ•œ๋‹ค.

7.1. ์ŠคํŠธ๋ฆผ์„ ์ปฌ๋ ‰์…˜๊ณผ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜

toList(), toSet(), toMap(), toCollection(), toArray()

// ์ŠคํŠธ๋ฆผ์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ์ปฌ๋ ‰์…˜์— ์ˆ˜์ง‘ํ•˜๋ ค๋ฉด, Collectorsํด๋ž˜์Šค์˜ toList()์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.  
List<String> names = stuStream.map(Student::getName)
                              .collect(Collectors.toList());

// List๋‚˜ Set์ด ์•„๋‹Œ ํŠน์ • ์ปฌ๋ ‰์…˜์„ ์ง€์ •ํ•˜๋ ค๋ฉด,
// toCollection()์— ํ•ด๋‹น ์ปฌ๋ ‰์…˜์˜ ์ƒ์„ฑ์ž ์ฐธ์กฐ๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.
ArrayList<String> list = names.stream()
                              .collect(Collectors.toCollection(ArrayList::new));
                              
// Map์€ ๊ฐ์ฒด์˜ ์–ด๋–ค ํ•„๋“œ๋ฅผ ํ‚ค์™€ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.
// ์š”์†Œ์˜ ํƒ€์ž…์ด Person์ธ ์ŠคํŠธ๋ฆผ์—์„œ ์‚ฌ๋žŒ์˜ ์ฃผ๋ฏผ๋ฒˆํ˜ธ(regId)๋ฅผ ํ‚ค๋กœ ํ•˜๊ณ , ๊ฐ’์œผ๋กœ Person ๊ฐ์ฒด๋ฅผ ๊ทธ๋Œ€๋กœ ์ €์žฅ
Map<String, Person> map = personStream.collect(Collectors.toMap(p->p.getRegId(), p->p)

// ์ŠคํŠธ๋ฆผ์— ์ €์žฅ๋œ ์š”์†Œ๋“ค์„ T[] ํƒ€์ž…์˜ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ํ•˜๋ ค๋ฉด toArray() ์‚ฌ์šฉ
// ๋‹จ, ํ•ด๋‹น ํƒ€์ž…์˜ ์ƒ์„ฑ์ž ์ฐธ์กฐ๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ง€์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค. ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฐ˜ํ™˜๋˜๋Š” ๋ฐฐ์—ด์˜ ํƒ€์ž…์€ Object[]
Student[] stuNames = studentStream.toArray(Student[]::new);   // OK
Student[] stuNames = studentStream.toArray();                 // ์—๋Ÿฌ
Object[] stuNames = studentStream.toArray();                  // OK

7.2. ํ†ต๊ณ„

counting(), summingInt(), averagingInt(), maxBy(), minBy()

7.3. ๋ฌธ์ž์—ด ๊ฒฐํ•ฉ

joining()
์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ String์ด๋‚˜ StringBuffer์ฒ˜๋Ÿผ CharSequence์˜ ์ž์†์ธ ๊ฒฝ์šฐ์—๋งŒ ๊ฒฐํ•ฉ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ
์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๊ฐ€ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ ๊ฒฝ์šฐ์—๋Š” ๋จผ์ € map()์„ ์ด์šฉํ•ด์„œ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

String studentNames = stuStream.map(Student::getName).collect(joining());
String studentNames = stuStream.map(Student::getName).collect(joining(","));
String studentNames = stuStream.map(Student::getName).collect(joining(",", "[", "]"));

// ๋งŒ์•ฝ map()์—†์ด ์ŠคํŠธ๋ฆผ์— ๋ฐ”๋กœ joining()ํ•˜๋ฉด, ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ์— toString()์„ ํ˜ธ์ถœํ•œ ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐํ•ฉํ•œ๋‹ค.
String studentInfo = stuStream.collect(joining(","));

7.4. ๊ทธ๋ฃนํ™”์™€ ๋ถ„ํ• 

groupingBy(), partitioningBy()
๊ทธ๋ฃนํ™”๋Š” ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ํŠน์ • ๊ธฐ์ค€์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•˜๋Š” ๊ฒƒ
๋ถ„ํ• ์€ ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ ๋‘ ๊ฐ€์ง€, ์ง€์ •๋œ ์กฐ๊ฑด์— ์ผ์น˜ํ•˜๋Š” ๊ทธ๋ฃน๊ณผ ์•„๋‹Œ ๊ทธ๋ฃน์œผ๋กœ ๋ถ„ํ• ํ•˜๋Š” ๊ฒƒ
groupingBy()๋Š” ์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋ฅผ Function์œผ๋กœ, partitioningBy()๋Š” Predicate๋กœ ๋ถ„๋ฅ˜
๋ณดํ†ต ์ŠคํŠธ๋ฆผ์˜ ๋‘ ๊ฐœ์˜ ๊ทธ๋ฃน์œผ๋กœ ๋‚˜๋ˆ ์•ผ ํ•˜๋ฉด partitioningBy()์“ฐ๋Š” ๊ฒƒ์ด ๋” ๋น ๋ฅด๊ณ , ๊ทธ ์™ธ์—๋Š” groupingBy()๋ฅผ ์“ฐ๋ฉด ๋œ๋‹ค.
๊ทธ๋ฃนํ™”์™€ ๋ถ„ํ• ์˜ ๊ฒฐ๊ณผ๋Š” Map์— ๋‹ด๊ฒจ ๋ฐ˜ํ™˜๋œ๋‹ค.

7.5. partitioningBy()์— ์˜ํ•œ ๋ถ„๋ฅ˜

// 1. ๊ธฐ๋ณธ ๋ถ„ํ• 
Map<Boolean, List<Student>> stuBySex 
              = stuStream.collect(partitioningBy(Student::isMale));  // ํ•™์ƒ๋“ค์„ ์„ฑ๋ณ„๋กœ ๋ถ„ํ• 
List<Student> maleStudent = stuBySex.get(true);   // Map์—์„œ ๋‚จํ•™์ƒ ๋ชฉ๋ก์„ ์–ป๋Š”๋‹ค.
List<Student> femaleStudent = stuBySex.get(false);  // ์—ฌํ•™์ƒ ๋ชฉ๋ก

// 2. ๊ธฐ๋ณธ ๋ถ„ํ•  + ํ†ต๊ณ„ ์ •๋ณด
Map<Boolean, Long> stuNumBySex = stuStream.collect(partitioningBy(Student::isMale, counting()));
System.out.println(stuNumBySex.get(true));  // 8 (๋‚จํ•™์ƒ์ˆ˜)
System.out.println(stuNumBySex.get(false));  // 10 (์—ฌํ•™์ƒ์ˆ˜)


// ๋‚จํ•™์ƒ 1๋“ฑ ๊ตฌํ•˜๊ธฐ, mapBy()์˜ ๋ฐ˜ํ™˜ํƒ€์ž…์€ Optional<Student>
Map<Boolean, Optional<Student>> topScoreBySex
                = stuStream.collect(partitioningBy(Student::isMale, maxBy(comparingInt(Student::getScore))));
System.out.println(topScoreBySex.get(true));  // Optional{[๋‚จ์ผ๋“ฑ, ๋‚จ, 1, 1, 300]}


// mapBy()์˜ ๋ฐ˜ํ™˜ํƒ€์ž…์ด Optional<Student>๊ฐ€ ์•„๋‹Œ Student๋ฅผ ๋ฐ˜ํ™˜ ๊ฒฐ๊ณผ๋กœ ์–ป์œผ๋ ค๋ฉด,  
// collectiongAndThen()๊ณผ Optional::get ํ•จ๊ป˜ ์‚ฌ์šฉ
Map<Boolean, student> topScoreBySex 
            = stuStream.collect(
                        partitioningBy(
                            Student::isMale, collectingAndThen(
                                              maxBy(comparingInt(Student::getScore))
                                              , Optional::get)));
                                              
// 3. ์ด์ค‘ ๋ถ„ํ• 
Map<Boolean, Map<Boolean, List<Student>>> failedStuBySex 
                         = stuStream.collect(
                                      partitioningBy(Student::isMale, partitioningBy(s->s.getScore()<150)));
List<Student> failedMaleStu = failedStuBySex.get(true).get(true);

7.6. groupingBy()์— ์˜ํ•œ ๋ถ„๋ฅ˜

groupingBy()๋กœ ๊ทธ๋ฃนํ™”ํ•˜๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ List์— ๋‹ด๋Š”๋‹ค.

// 1. ํ•™์ƒ ์ŠคํŠธ๋ฆผ์„ ๋ฐ˜ ๋ณ„๋กœ ๊ทธ๋ฃน์ง€์–ด Map์— ์ €์žฅ 
Map<Integer, List<Student>> stuByBan 
                  = stuStream.collect(groupingBy(Student::getBan, toList()));   //toList()์ƒ๋žต๊ฐ€๋Šฅ

Map<Integer, HashSet<Student>> stuByHak 
                  = stuStream.collect(groupingBy(Student::getHak, toCollection(HashSet::new)));


// 2. ํ•™์ƒ ์ŠคํŠธ๋ฆผ์„ ์„ฑ์ ์˜ ๋“ฑ๊ธ‰(Student.Level)์œผ๋กœ ๊ทธ๋ฃนํ™”
Map<Student.Level, Long> stuByLevel
            = stuStream.collect(
                        groupingBy(s-> { if(s.getScore()>=200)      return Student.Level.HIGH;
                                         else if(s.getScore()>=100) return Student.Level.MID;
                                         else                       return Student.Level.LOW;
                                       }, counting()));


// 3. groupingBy() ๋‹ค์ค‘ ์‚ฌ์šฉํ•˜๊ธฐ.
// ํ•™๋…„๋ณ„๋กœ ๊ทธ๋ฃนํ™”ํ•˜๊ณ  ๋‹ค์‹œ ๋ฐ˜๋ณ„๋กœ ๊ทธ๋ฃนํ™”
Map<Integer, Map<Integer, List<Student>>> stuByHakAndBan
            = stuStream.collect(groupingBy(Student::getHak,
                                groupingBy(Student::getBan)));
                                

// 4. ๊ฐ ๋ฐ˜๋ณ„ 1๋“ฑ ์ถ”์ถœ
Map<Integer, Map<Integer, Student>> topStuByHakAndBan
            = stuStream.collect(groupingBy(Student::getHak,
                                groupingBy(Student::getBan, 
                                           collectingAndThen(
                                                        maxBy(comparingInt(Student::getScore)),
                                                                                      Optional::get))));


// 5. ํ•™๋…„๋ณ„, ๋ฐ˜๋ณ„ ๊ทธ๋ฃนํ™”ํ•œ ํ›„์— ์„ฑ์ ๊ทธ๋ฃน์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ Set์— ์ €์žฅ
Map<Integer, Map<Integer, Set<Student.Level>>> stuByHakAndBan
            = stuStream.collect(groupingBy(Student::getHak,
                                groupingBy(Student::getBan,
                                           mapping(s-> {
                                                        if(s.getScore()>=200)      return Student.Level.HIGH;
                                                        else if(s.getScore()>=100) return Student.Level.MID;
                                                        else                       return Student.Level.LOW;
                                                       } , toSet()))));

8. ์ŠคํŠธ๋ฆผ ๋ณ€ํ™˜ ์ •๋ฆฌ

from to ๋ณ€ํ™˜๋ฉ”์„œ๋“œ
1. ์ŠคํŠธ๋ฆผ -> ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ
Stream IntStream mapToInt (ToIntFunction mapper)
LongStream mapToLong (ToLongFunction mapper)
DoubleStream mapToDouble (ToDoubleFunction mapper)
2. ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ -> ์ŠคํŠธ๋ฆผ
IntStream Stream
LongStream Stream boxed()
DoubleStream Stream
Stream mapToObj (DoubleFunction mapper)
3. ์ŠคํŠธ๋ฆผ์˜ ์ŠคํŠธ๋ฆผ -> ์ŠคํŠธ๋ฆผ
Stream> Stream flatMap (Function mapper)
Stream IntStream flatMapToInt (Function mapper)
Stream LongStream flatMapToLong (Function mapper)
Stream DoubleStream flatMapToDouble (Function mapper)
4. ์ปฌ๋ ‰์…˜ -> ์ŠคํŠธ๋ฆผ
Collection, List, Set Stream stream()