JiwonDev

#11 Stream API

by JiwonDev

์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ์—๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 1. Stream ์ƒ์„ฑ 2. ์ค‘๊ฐœ์—ฐ์‚ฐ 3. ์ตœ์ข…์—ฐ์‚ฐ(Terminal Op) ์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.

 

# Stream API ํŠน์ง•

  • Java์˜ ๋ชจ๋“  Collection์€ ๋‚ด๋ถ€์— stream()์ด๋ผ๋Š” ๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ๊ฐ€ ์กด์žฌํ•œ๋‹ค. ์ปฌ๋ ‰์…˜์ด ์•„๋‹Œ ๊ฐ’๋“ค์€ Stream.of(~) ๋ฉ”์„œ๋“œ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ค‘๊ฐœ์—ฐ์‚ฐ ๋ฉ”์„œ๋“œ๋Š” ์ตœ์ข…์—ฐ์‚ฐ์ด ๋‚˜์˜ค๊ธฐ ์ „๊นŒ์ง€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค. ๋˜ํ•œ ์ค‘๊ฐœ์—ฐ์‚ฐ ๋ฉ”์„œ๋“œ๋Š” Stream์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ Stream.funcA(~).funcB(~).funcC(~) ๊ฐ™์ด ๋ฉ”์†Œ๋“œ ์ฒด์ด๋‹์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ํ•œ๋ฒˆ ์‚ฌ์šฉํ•œ ์ŠคํŠธ๋ฆผ์€ ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜ ์—†๋‹ค. ์ฆ‰ ์ตœ์ข…์—ฐ์‚ฐ ์ดํ›„ stream API ์—ฐ์‚ฐ์„ ๋‹ค์‹œ ํ•  ์ˆ˜ ์—†๋‹ค.
  • Stream API๋Š” ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ ์ข‹๊ฒŒ ์„ค๊ณ„๋˜์–ด์žˆ๋‹ค.
    ๋ฌผ๋ก  ํ•„์š”์— ๋”ฐ๋ผ (value) -> {~} ์œผ๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋„ ๋œ๋‹ค.

# 0. Boxing ์ด๋ž€

Wrapper Class๋“ค์„ ์ด์šฉํ•ด์„œ ๊ธฐ๋ณธํƒ€์ž…๋“ค(Primitive Type)๋ฅผ ๊ฐ์ฒด(Reference Type)์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์„ ๋ฐ•์‹ฑ(Boxing), ๋ฐ˜๋Œ€๋กœ ๊ธฐ๋ณธ ํƒ€์ž…์œผ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๊ฒƒ์„ ์–ธ๋ฐ•์‹ฑ(unBoxing)์ด๋ผ ํ•œ๋‹ค. ์ฐธ๊ณ ๋กœ ์ž๋ฐ” ์ดˆ๊ธฐํ™” ๋ฌธ๋ฒ•์—์„œ ์˜คํ† ๋ฐ•์‹ฑ์„ ์ง€์›ํ•ด์ค€๋‹ค.

Integer num = new Integer(17); // ๋ฐ•์‹ฑ
int n = num.intValue();        // ์–ธ๋ฐ•์‹ฑ

// ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•ด๋„ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฐ•์‹ฑ/์–ธ๋ฐ•์‹ฑ์ด ์ด๋ฃจ์–ด์ง„๋‹ค.
Character ch = 'X'; // Character ch = new Character('X'); : ์˜คํ† ๋ฐ•์‹ฑ
char c = ch;        // char c = ch.charValue();           : ์˜คํ† ์–ธ๋ฐ•์‹ฑ

  • ๋ฐ•์‹ฑ์€ ๊ธฐ๋ณธํƒ€์ž…์„ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ถ”๊ฐ€๊ธฐ๋Šฅ์„ ๋„ฃ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ทธ๋งŒํผ ๊ธฐ๋ณธํƒ€์ž…๋งŒ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ๋ณด๋‹ค ๋น„์šฉ์ด ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฐœ์ƒํ•จ์„ ์œ ์˜ํ•ด์•ผ ํ•œ๋‹ค.
  • Stream์—์„œ๋„ ๋งˆ์ฐฌ๊ธฐ์ง€๋กœ Stream<Integer>์™€ ๊ฐ™์ด ๊ธฐ๋ณธํ˜•์„ ๋ฐ•์‹ฑํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ์ €ํ•˜์‹œํ‚ค๊ธฐ์— ๊ธฐ๋ณธํ˜• ํŠนํ™” ์ŠคํŠธ๋ฆผ (IntStream, LongStream, DoubleStream ๋“ฑ)์„ ๋”ฐ๋กœ ์ œ๊ณตํ•ด์ค€๋‹ค.
  • IntStream๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์˜ .boxed() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐ•์‹ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
// ๊ธฐ๋ณธํ˜•(Primitive Type) ์ŠคํŠธ๋ฆผ์„ ๋ฐ•์‹ฑํ•˜๋Š” ๋ฐฉ๋ฒ•.
Stream<Integer> boxedIntStream = IntStream.range(1, 5).boxed();
  • ๋ฐ˜๋Œ€๋กœ Stream์„ ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ฐ”๊พธ๊ณ ์ž ํ•œ๋‹ค๋ฉด .mapToInt() .mapToLong() ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
// .mapToType() ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅํ•˜๋‹ค.
// ์ด ๋ฉ”์„œ๋“œ๋Š” ํƒ€์ž…์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ ์ด์™ธ์—๋Š” .map()๊ณผ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ํ•œ๋‹ค.
Stream.of(1.0, 2.0, 3.0) // Stream<Double> ์ƒ์„ฑ
.mapToInt(Double::intValue) // IntStream์œผ๋กœ ๋ณ€ํ™˜

// .mapToObj() ๋ฅผ ์ด์šฉํ•˜๋ฉด .boxed()์ฒ˜๋Ÿผ IntStream -> Stream<Intger> ๋กœ ๋ณ€ํ™˜๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.
IntStream.range(1, 4).mapToObj(i -> "a" + i)

 


# 1. ์ŠคํŠธ๋ฆผ ์ƒ์„ฑํ•˜๊ธฐ

# ๋ฐฐ์—ด (Array) Stream

๋ฐฐ์—ด์€ ๋‚ด๋ถ€์— stream ๋ฉ”์„œ๋“œ๊ฐ€ ์—†๋‹ค. ๋ณดํ†ต Arrays ์•ˆ์— ์žˆ๋Š” Arrays.stream() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

String[] arr = new String[]{"a", "b", "c"};

Stream<String> stream = Arrays.stream(arr);

Stream<String> streamOfArrayPart = 
  Arrays.stream(arr, 1, 3); // 1~2 ์š”์†Œ [b, c]

 


# Collection Stream

์ปฌ๋ ‰์…˜์˜ ๊ฒฝ์šฐ ๋‚ด๋ถ€์— Stream ๋ฉ”์„œ๋“œ๊ฐ€ ์กด์žฌํ•˜๋ฏ€๋กœ, ์†์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream(); // ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์ŠคํŠธ๋ฆผ

 


# Empty Stream (null ๊ฐ’ ๋Œ€์ฒด)

๋นˆ ์ŠคํŠธ๋ฆผ์ด ํ•„์š”ํ•  ๊ฒฝ์šฐ Stream.empty()๋ฅผ ์ด์šฉํ•˜์—ฌ ๋นˆ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Š” null ๊ฐ’ ์‚ฌ์šฉ์„ ๋Œ€์‹ ํ•ด์ค€๋‹ค.

// ์ปฌ๋ ‰์…˜์— ๊ฐ’์ด ๋น„์—ˆ๋‹ค๊ณ  ์ŠคํŠธ๋ฆผ ์—ฐ์‚ฐ์˜ ๊ฐ’์„ null๋กœ ์ฃผ๋Š” ๊ฑด ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
list.isEmpty() ? Stream.empty() : list.stream();

 


# ๊ธฐ๋ณธ ํƒ€์ž…ํ˜• Stream

๊ธฐ๋ณธํƒ€์ž…์˜ ์—ฐ์†๋œ ๊ฐ’๋“ค์„ Stream์œผ๋กœ ๋งŒ๋“ค ๋•, ๊ฐ๊ฐ์˜ Stream ๊ฐ์ฒด์•ˆ์—์žˆ๋Š” range๋ฅผ ์ด์šฉํ•ด์„œ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

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

// Randomํ•œ ์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด
DoubleStream doubles = new Random().doubles(3); // ๋‚œ์ˆ˜ 3๊ฐœ ์ƒ์„ฑ

๋ฌผ๋ก  ํŠน์ • ๊ฐ’์ด ํ•„์š”ํ•˜๋‹ค๋ฉด Stream.of() ๋ฅผ ์ด์šฉํ•ด์„œ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.

public static void main(String args[]) {
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); // 1,2,3,4,5 ์ƒ์„ฑ
    
    List<Integer> numbers = stream.map(x -> x + 1)
            .filter(x -> x % 2 == 0)
            .collect(Collectors.toList());

    numbers.stream().forEach(System.out::println); // 2, 4, 6
}

 


# ๋ฌธ์ž Stream

๋ณดํ†ต์€ String ์ž์ฒด๋ฅผ ์ŠคํŠธ๋ฆผ์— ๋„ฃ์–ด ์‚ฌ์šฉํ•˜์ง€๋งŒ, ๋ฌธ์ž์—ด ์ž์ฒด๋ฅผ ๋ฌธ์ž(char)๋กœ ๋‚˜๋ˆ„์–ด ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด str.chars()๋ฅผ ์ด์šฉํ•˜๋ฉด ๋œ๋‹ค.

// Char ๊ฐ’์„ IntStream์— ๋„ฃ๊ฒŒ๋˜๋ฉด ํ•ด๋‹น ๋ฌธ์ž์˜ ์ฝ”๋“œํ‘œ๋กœ ์ „ํ™˜๋œ๋‹ค.
IntStream charsStream = 
  "Stream".chars(); // [83, 116, 114, 101, 97, 109]
  
// ๋ณดํ†ต์€ ๊ตณ์ด Char๋กœ ๋ณ€ํ™˜ํ•˜์ง€์•Š๊ณ , String ๊ฐ’ ์ž์ฒด๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค.
Stream<String> stringStream = 
Pattern.compile("- ").splitAsStream("Eric- Elena- Java"); // ["Eric", "Elena", "Java"]

 


# ์ŠคํŠธ๋ฆผ ์—ฐ๊ฒฐํ•˜๊ธฐ (Stream.concat)

์—ฌ๋Ÿฌ๊ฐœ๋กœ ๋‚˜๋ˆ„์–ด์ ธ์žˆ๋Š” ์ŠคํŠธ๋ฆผ์„ Stream.concat ์œผ๋กœ ์—ฐ๊ฒฐ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Stream<String> stream1 = Stream.of("Java", "Scala", "Groovy");
Stream<String> stream2 = Stream.of("Python", "Go", "Swift");
Stream<String> concat = Stream.concat(stream1, stream2);
// [Java, Scala, Groovy, Python, Go, Swift]

 


# Stream.builder (์ŠคํŠธ๋ฆผ์— ์ง์ ‘ ๊ฐ’ ์ถ”๊ฐ€)

๋‹ค๋ฅธ ๊ฐ’์„ ์—ฐ๊ฒฐํ•˜๊ฑฐ๋‚˜ ๋ณ€ํ™˜ํ•˜์ง€์•Š๊ณ  ์ง์ ‘ ํ•ด๋‹น Stream์— ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Stream.builder()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ํ•ด๋‹น ์ŠคํŠธ๋ฆผ์—์„œ builder() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ํ›„, ์›ํ•˜๋Š” ์ž‘์—…์„ ํ•˜๊ณ  ๋งˆ์ง€๋ง‰์— .build() ํ•ด์ฃผ๋ฉด ์™„๋ฃŒ๋œ๋‹ค.

Stream<String> builderStream = 
  Stream.<String>builder()
    .add("Eric").add("Elena").add("Java")
    .build(); // [Eric, Elena, Java]

 

 


# Stream.generate (ํ•จ์ˆ˜๋กœ ์ŠคํŠธ๋ฆผ ๊ฐ’ ์ƒ์„ฑ)

Stream.generate(~)๋ฅผ ์ด์šฉํ•˜๋ฉด ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๊ฐ’์„ ์ƒ์„ฑํ•ด ๋‚ผ ์ˆ˜ ์žˆ๋‹ค. ๋ฌผ๋ก  ๊ทธ๋ƒฅ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐœ์ˆ˜๊ฐ€ ๋ฌดํ•œ์ธ(infinite) ์ŠคํŠธ๋ฆผ์ด ๋งŒ๋“ค์–ด์ง€๊ธฐ์— .limit(n)๋‚˜ .skip(n)์„ ์ด์šฉํ•ด ๊ฐœ์ˆ˜๋ฅผ ์ œํ•œํ•˜์—ฌ ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

  • .limit(n) ์ฒ˜์Œ๋ถ€ํ„ฐ n๊ฐœ ๊นŒ์ง€์˜ ๊ฐ’๋งŒ ์‚ฌ์šฉํ•œ๋‹ค.
  • .skip(n) ๊ฐ’์„ ์•ž์—์„œ๋ถ€ํ„ฐ n๊ฐœ ๋›ฐ์–ด๋„˜๊ณ  ๋‚˜๋จธ์ง€ ๊ฐ’์„ ์‚ฌ์šฉํ•œ๋‹ค.
// java.util.function์— ์žˆ๋Š” Supplier๋ฅผ ์ด์šฉํ•ด๋„ ๋œ๋‹ค. ๋ฉ”์„œ๋“œ ์›ํ˜•๋„ ๊ทธ๋Ÿฌํ•จ. 
public static<T> Stream<T> generate(Supplier<T> s) { ... }


Stream<String> generatedStream =
        Stream.generate(() -> "A").limit(4); // ["A", "A", "A", "A"]
public static void main(String args[]) {
    Supplier<String> supplyHi = () -> "Hi";

    Stream<String> generatedStream =
            Stream.generate(supplyHi).limit(4);

    String str = generatedStream.collect(Collectors.joining(" "));
    System.out.println(str); // Hi Hi Hi Hi
}

 


# Stream.iterate()

generate์™€ ๋น„์Šทํ•œ๋ฐ, ๋‹ค๋ฅธ ์ ์€ ์ดˆ๊ธฐ๊ฐ’์„ ์ฃผ๊ณ  ๋ฉ”์„œ๋“œ๋ฅผ ์ดํ„ฐ๋ ˆ์ดํ„ฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•œ๋‹ค.

Stream<Integer> iteratedStream = 
  Stream.iterate(30, n -> n + 2).limit(5); // [30, 32, 34, 36, 38]

 


# ํŒŒ์ผ Stream

์ž˜ ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š์ง€๋งŒ, ํŒŒ์ผ ๊ฐ์ฒด๋ฅผ ๋ฌธ์ž์—ด ์ŠคํŠธ๋ฆผ์œผ๋กœ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

// ํŒŒ์ผ์„ String ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€๊ฒฝ
Stream<String> lineStream = 
  Files.lines(Paths.get("file.txt"), 
              Charset.forName("UTF-8"));

 


# ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ

Stream ๋Œ€์‹  parallelStream์„ ์ด์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. parallelStream์€ ์“ฐ๋ ˆ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€์ ์œผ๋กœ Fork/Join framework๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

// ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์ƒ์„ฑ
Stream<Product> parallelStream = productList.parallelStream();

// ๋ณ‘๋ ฌ ์—ฌ๋ถ€ ํ™•์ธ
boolean isParallel = parallelStream.isParallel();

// ์˜ˆ์ œ
public static void main(String[] args) {		
    List<String> list = Arrays.asList("AAA","BBB","CCC","DDD","EEE");
		 
    //์ˆœ์ฐจ์ฒ˜๋ฆฌ
    Stream<String> stream = list.stream();
    stream.forEach(System.out::println);
    
    //๋ณ‘๋ ฌ์ฒ˜๋ฆฌ
    Stream<String> parallelStream = list.parallelStream();
    parallelStream.forEach(System.out::println);
}

 


# 2. ์ŠคํŠธ๋ฆผ ๊ฐ€๊ณตํ•˜๊ธฐ (์ค‘๊ฐœ์—ฐ์‚ฐ)

# filter

ํ˜„์žฌ ์š”์†Œ๋ฅผ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๊ฑธ๋Ÿฌ๋‚ด๋Š” ์ž‘์—…์„ ํ•ด์ค€๋‹ค.

Stream<T> filter(Predicate<? super T> predicate);
List<String> names = Arrays.asList("Eric", "Elena", "Java");
Stream<String> stream = 
  names.stream()
  .filter(name -> name.contains("a"));
// ์ด๋ฆ„์— a๊ฐ€ ํฌํ•จ๋˜๋Š” ๋‹จ์–ด๋งŒ ๋‚จ๊ฒŒ๋จ. ex) [Elena, Java]

 

 


# map

๊ฐ๊ฐ์˜ ์š”์†Œ๋ฅผ input์œผ๋กœ ๋ฐ›์•„์„œ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜ํ•จ. ์ด๋Ÿฌํ•œ ์ž‘์—…์„ Mapping์ด๋ผ๊ณ  ๋ถ€๋ฅด๊ธฐ๋„ ํ•œ๋‹ค.

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
List<String> names = Arrays.asList("Eric", "Elena", "Java");
Stream<String> stream = 
  names.stream()
  .map(String::toUpperCase); // [ERIC, ELENA, JAVA]

// ์ด๋Ÿฐ ์‹์œผ๋กœ ๊ฐ์ฒด ์•ˆ์˜ ํŠน์ • ํ•„๋“œ๋ฅผ ๊บผ๋‚ด์˜ฌ ์ˆ˜๋„ ์žˆ์Œ.
Stream<Integer> stream = 
  productList.stream()
  .map(Product::getAmount);
// [23, 14, 13, 23, 13]

 


# flatMap

์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๋ฉด, ์š”์†Œ๊ฐ€ ์ค‘์ฒฉ๋˜์–ด์žˆ์œผ๋ฉด ํ•œ๋‹จ๊ณ„ ํ’€์–ด์ฃผ์–ด ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๋Š” map์ด๋‹ค.

๋ฐฐ์—ด์˜ ์š”์†Œ๊ฐ€ ์•„๋‹Œ ๋ฐฐ์—ด ์ž์ฒด๋ฅผ ์ž…๋ ฅ๊ฐ’์œผ๋กœ ๋ฐ›๋Š”๊ฒฝ์šฐ [ [1,2,3], [4,5,6] ] ์ฒ˜๋Ÿผ ์ค‘์ฒฉ๋˜๋Š” ์ŠคํŠธ๋ฆผ์ด ๋งŒ๋“ค์–ด์ง„๋‹ค.

์ด๋ฅผ ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ž‘์—…ํ•˜๊ฒŒ ๋˜๋ฉด [1, 2, 3, 4, 5, 6]์ด ์•„๋‹ˆ๋ผ [1,2,3] [4,5,6] ์œผ๋กœ ์ŠคํŠธ๋ฆผ ์š”์†Œ๊ฐ€ ๊ฒฐ์ •๋˜์–ด ์ŠคํŠธ๋ฆผ ๋ฐ˜ํ™˜๊ฐ’์ด ์ค‘์ฒฉ๋˜์–ด ๋‹ค์Œ ์ž‘์—…์„ ํ•˜๊ธฐ ๊นŒ๋‹ค๋กœ์›Œ์ง€๋Š”๋ฐ, flatMap์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ˜ํ™˜๊ฐ’์ด ์ค‘์ฒฉ๋˜์–ด์žˆ์œผ๋ฉด ์ค‘์ฒฉ์„ 1๋‹จ๊ณ„ ์ œ๊ฑฐํ•ด์ค€๋‹ค.

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Stream<String[]> strStream = Stream.of(
    new String[] {"a", "b", "c"}, 
    new String[] {"d", "e", "f"}
    );
    

// ์œ„์˜ ๊ฐ’์„ ๊ทธ๋ƒฅ map์„ ์‚ฌ์šฉํ•˜๋ฉด 2์ค‘ Stream์ด ๋ฐ˜ํ™˜๋จ 
// ์š”์†Œ๊ฐ€ ์ด 2๊ฐœ์ž„. ["a", "b", "c"], ["d", "e", "f"]
// Arrays.stream( ["a", "b", "c"] )...
Stream<Stream<String>> stream = strStream.map(Arrays::stream);

// flatMap์„ ์‚ฌ์šฉํ•˜๋ฉด 1์ค‘ Stream์œผ๋กœ ์ฐจ์›์„ ๋‚ฎ์ถœ ์ˆ˜ ์žˆ์Œ
// ์š”์†Œ๊ฐ€ ์ค‘์ฒฉ๋˜์–ด์žˆ๋‹ค๋ฉด ํ•œ๋‹จ๊ณ„ ํ’€์–ด์คŒ. ์š”์†Œ๊ฐ€ ์ด 6๊ฐœ์ž„ ["a","b","c","d","e","f"] 
// Arrays.stream( "a" )...
Stream<String> stream = strStream.flatMap(Arrays::stream);

Map vs FlatMap ์ƒ์„ธ์„ค๋ช…

๋”๋ณด๊ธฐ

Stream<String[]> ์ฒ˜๋Ÿผ ์ž…๋ ฅ๊ฐ’ ์ž์ฒด๊ฐ€ ๋ฆฌ์ŠคํŠธ๋กœ ์ฃผ์–ด์งˆ ๊ฒฝ์šฐ

Map => ์š”์†Œ 2๊ฐœ [1,2], [1,1] => [ [1,2], [1,1] ] ๋ฐ˜ํ™˜

FlatMap => ์š”์†Œ๊ฐ€ ์ค‘์ฒฉ๋˜์–ด์žˆ์œผ๋ฉด ํ•œ๋ฒˆ ์ œ๊ฑฐ ํ›„ ์‚ฌ์šฉ, ์š”์†Œ 4๊ฐœ [ [1, 2], [1, 1] ] => [1,2,1,1] ๋ฐ˜ํ™˜

 

* ์ฐธ๊ณ ๋กœ Stream<String[]>์„ ํ•ด์ฃผ์—ˆ๋‹คํ•ด์„œ ๋ฐฐ์—ด์•ˆ์— ์žˆ๋Š” ์š”์†Œ๊นŒ์ง€ ์ŠคํŠธ๋ฆผํ™” ๋œ ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ์ฃผ์†Œ๊ฐ’์ด ์ŠคํŠธ๋ฆผํ™” ๋œ ๊ฒƒ์ด๋ฏ€๋กœ ์•ˆ์— ๋“ค์–ด์žˆ๋Š” ์š”์†Œ๋“ค์€ ์›๋ณธ ๊ทธ๋Œ€๋กœ์ด๊ธฐ์— ์•ˆ์— ์žˆ๋Š” ๊ฐ’๋“ค์„ Arrays.Stream์œผ๋กœ ๋ณต์‚ฌํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.

Stream<String[]>์€ ๊ฐ€๋ฅดํ‚ค๋Š” ์ฃผ์†Œ๊ฐ’์„ ๋ณต์‚ฌํ•œ๊ฒƒ์ด๋‹ค.

 

๋‚ด๋ถ€์˜ ์š”์†Œ๋“ค์—๊ฒŒ Arrays.Stream( ์š”์†Œ ) ๋ฅผ ํ†ตํ•ด ์ด๋ ‡๊ฒŒ ๋ฐ”๊พธ์–ด์ฃผ์–ด์•ผํ•œ๋‹ค.

์˜ˆ์ œ

String[] arrayOfWords = {"Hello", "World"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);

streamOfWords.map(s->s.split("")) // ๋ฌธ์ž์—ด์„ Array๋กœ ๋ฐ”๊ฟˆ.
    .map(Arrays::stream) // ํ•˜๋‚˜์˜ ๋ฐฐ์—ด [H,e,l,l,o]์„ Stream<String[]> ์œผ๋กœ ๋ฐ”๊ฟˆ. 
    .distinct() // ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ์—ˆ์œผ๋‚˜ ๋ฐฐ์—ด์ด ์ค‘์ฒฉ๋˜์–ด ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ.
    .collect(Collectors.toList());
    // [ ["H","e","l","l","o"], ["W","o","r","l","d"] ]
๊ฒฐ๊ณผ๋ฌผ : [ [H,e,l,l,o], [W,o,r,l,d] ]

1. map(s->s.split)์— ์˜ํ•ด "HELLO"๊ฐ€ [H,E,L,L,O]๋กœ ๋ถ„๋ฆฌ๋œ๋‹ค.
2. ํ˜„์žฌ ์ŠคํŠธ๋ฆผ์€ ์ค‘์ฒฉ๋˜์–ด์žˆ์œผ๋ฏ€๋กœ distinct()๋Š” ์•„๋ฌด๋Ÿฐ ์—ญํ• ์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
3. ์ฆ‰ ๊ฒฐ๊ณผ๋ฌผ๋„ ๊ทธ๋Œ€๋กœ ์ค‘์ฒฉ๋œ [ [H,E,L,L,O], [W,O,R,L,D] ] ๋ฐฐ์—ด์ด ๋ฆฌํ„ด๋œ๋‹ค.

 

์ด๋ฅผ FlatMap์„ ์ด์šฉํ•ด์ฃผ๋ฉด ์‰ฝ๊ฒŒ ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ๋‹ค.

String[] arrayOfWords = {"Hello", "World"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);

streamOfWords.map(s->s.split("")) // ๋ฌธ์ž์—ด์„ Array๋กœ ๋ฐ”๊ฟˆ
    .flatMap(Arrays::stream) //๊ฐ๊ฐ์˜ ์š”์†Œ๊ฐ€ ์ค‘์ฒฉ์ด ์ œ๊ฑฐ๋˜๊ณ , Stream<String>์ด ์ ์šฉ๋จ
    .distinct() // ์ค‘๋ณต์ด ์ œ๊ฑฐ๋จ
    .collect(Collectors.toList());
    // ["H","e","l","o","W","r","d"]

1. map(s->s.split(""))์˜ ๊ฒฐ๊ณผ๋กœ ๋‚˜์˜จ ์ด์ค‘ ๋ฐฐ์—ด [ [H,E,L,L,O] [W,O,R,L,D] ]๋ฅผ FlatMap์— ๋„˜๊ธด๋‹ค.

2. FlatMap์€ [H,E,L,L,O] , [W,O,R,L,D]๋กœ ๋ฐฐ์—ด 2๊ฐœ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›๋Š”๋‹ค. FlatMap์€ ์š”์†Œ ๋งˆ๋‹ค ์ค‘์ฒฉ์„ ์ œ๊ฑฐํ•˜์—ฌ H,E,L,L,O,W,O,R,L,D๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›๊ณ  ์ด๋ฅผ Arrays.stream(~)์„ ์ด์šฉํ•˜์—ฌ ๊ฐ๊ฐ์˜ ๋‹จ์–ด๋ฅผ Stream<String>์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

 

๋‹น์—ฐํ•œ๊ฑฐ์ง€๋งŒ, flatMap๋„ map๊ณผ ๋™์ผํ•˜๊ฒŒ ์‘์šฉ๊ฐ€๋Šฅํ•˜๋‹ค.

// students ๋Š” Student ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด๊ฐ€์žˆ๋Š” list์ด๋‹ค.
students.stream() 
  .flatMapToInt(student -> // ํ•™์ƒ๊ฐ์ฒด๋ฅผ [๊ตญ์–ด์ ์ˆ˜,์˜์–ด์ ์ˆ˜,์ˆ˜ํ•™์ ์ˆ˜] ๋กœ ๋งŒ๋“ ๋‹ค.
                IntStream.of(student.getKor(), 
                             student.getEng(), 
                             student.getMath()))
// ๊ทธ๋ƒฅ map์„ ์‚ฌ์šฉํ•˜๋ฉด students stream์˜ ํ•œ ์š”์†Œ์— "[๊ตญ์–ด,์˜์–ด,์ˆ˜ํ•™]" ์ด๋ ‡๊ฒŒ ์ €์žฅ๋œ๋‹ค.
// flatMap์„ ์ด์šฉํ•˜์—ฌ "๊ตญ์–ด,์˜์–ด,์ˆ˜ํ•™" ์œผ๋กœ ์ค‘์ฒฉ๋˜์–ด์žˆ๋Š” ๋ฐฐ์—ด์„ ํ’€์–ด์ฃผ์–ด์•ผ average()๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
  .average().ifPresent(avg -> 
                       System.out.println(Math.round(avg * 10)/10.0)); // ๊ฐ ํ•™์ƒ์˜ ํ‰๊ท ์ ์ˆ˜.

 


# sorted

๊ฐ’์„ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•œ๋‹ค. ํ•„์š”ํ•˜๋‹ค๋ฉด sorted(~)์— ๋น„๊ต ํ•จ์ˆ˜(Comparator)๋ฅผ ์ค˜ ์›ํ•˜๋Š” ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
// ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ
intList.stream().sorted()

// ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ 
intList.stream().sorted(Comparator.reverseOrder())

// ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ๋‚ด๋ฆผ์ฐจ์ˆœ ๊ตฌํ˜„๊ฐ€๋Šฅ.
strList.stream().sorted(Comparator.comparing(String::length).reversed())

// ๋ฉ”์„œ๋“œ ๋ ˆํผ๋Ÿฐ์Šค ์‚ฌ์šฉ (Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ๊ฐ์ฒด๋งŒ ๊ฐ€๋Šฅ)
intList.stream().sorted(Integer::compareTo)

// ๋žŒ๋‹ค ์‚ฌ์šฉ (Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ๊ฐ์ฒด๋งŒ ๊ฐ€๋Šฅ)
intList.stream().sorted((a, b) -> a.compareTo(b))

// ์ต๋ช… ํด๋ž˜์Šค๋กœ Comparator ์ง์ ‘ ๊ตฌํ˜„
// compareTo()๋Š” intํ˜•์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. a ๋ณด๋‹ค (ํฌ๋‹ค 1), (๊ฐ™๋‹ค 0), (์ž‘๋‹ค -1)
intList.stream().sorted(new Comparator<Integer>() {
    @Override
    public int compare(Integer a, Integer b) {
        return a.compareTo(b);
    }
})
List<String> lang = 
  Arrays.asList("Java", "Scala", "Groovy", "Python", "Go", "Swift");

lang.stream()
  .sorted()
  .collect(Collectors.toList());
// [Go, Groovy, Java, Python, Scala, Swift]

lang.stream()
  .sorted(Comparator.reverseOrder())
  .collect(Collectors.toList());
// [Swift, Scala, Python, Java, Groovy, Go]

 


# ์ดํ„ฐ๋ ˆ์ดํ„ฐ (peek)

map์€ ์ŠคํŠธ๋ฆผ ์š”์†Œ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•œ๋‹ค. ๋งŒ์•ฝ ์ˆ˜์ •์ด ํ•„์š”์—†๊ณ  ์ฝ๊ธฐ๋งŒ ํ•œ๋‹ค๋ฉด peek์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

peek๋Š” Stream์„ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ๋งŒ์•ฝ ์ถœ๋ ฅํ•  ๊ฐ’์ด ์—†์–ด์„œ ๋๋‚ด๊ณ  ์‹ถ๋‹ค๋ฉด forEach (map๊ณผ ๋™์ผ) ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. 

Stream<T> peek(Consumer<? super T> action);
int sum = IntStream.of(1, 3, 5, 7, 9)
  .peek(System.out::println) // ์ŠคํŠธ๋ฆผ ์š”์†Œ๋“ค์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๋‹ค. ์ฝ๊ธฐ๋งŒ ํ•œ๋‹ค.
  .sum();

 


# Optional API (null ๊ฐ’ ์ฒ˜๋ฆฌ, boxing)

2021.07.12 - [Backend/Java] - #12. Optional API (*Null๊ฐ’ ์ฒ˜๋ฆฌ)


#3. ์ŠคํŠธ๋ฆผ ๊ฒฐ๊ณผ ๋งŒ๋“ค๊ธฐ (์ตœ์ข…์—ฐ์‚ฐ)

# ๋‹จ์ผ ๊ฐ’ (Calculating)

์ŠคํŠธ๋ฆผ์˜ ์š”์†Œ๋“ค์„ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ๋งŒ๋“ ๋‹ค. .count() .sum() .min() .max() ๋“ฑ ๊ธฐ๋ณธํ˜• ํƒ€์ž…์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ ๋‹ค.

long count = IntStream.of(1, 3, 5, 7, 9).count();
long sum = LongStream.of(1, 3, 5, 7, 9).sum();

count๋‚˜ sum์€ ์š”์†Œ๊ฐ€ ์—†์œผ๋ฉด 0์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋˜์ง€๋งŒ, min, max, average์ฒ˜๋Ÿผ ๊ฒฐ๊ณผ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ Null๊ฐ’ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด Optional ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

OptionalInt min = IntStream.of(1, 3, 5, 7, 9).min();
OptionalInt max = IntStream.of(1, 3, 5, 7, 9).max();

DoubleStream.of(1.1, 2.2, 3.3, 4.4, 5.5)
  .average()
  .ifPresent(System.out::println);

# Reduction (reduce)

์ง์ ‘ ์—ฐ์‚ฐ์„ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, reduce๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.

.reduce(์ดˆ๊นƒ๊ฐ’, ํ•จ์ˆ˜, Combiner)์ด๋‹ค. ์ฐธ๊ณ ๋กœ combiner๋Š” ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์„ ํ•˜๋‚˜๋กœ ํ•ฉ์น˜๋Š” reduce ์—ฐ์‚ฐ์ด๋‹ค.

// ์ธ์ž 1๊ฐœ (accumulator) ์ดˆ๊ธฐ๊ฐ’ ์—†์Œ. (์ฒซ๋ฒˆ์งธ๊ฐ’=์ดˆ๊ธฐ๊ฐ’)
Optional<T> reduce(BinaryOperator<T> accumulator);

// ์ธ์ž 2๊ฐœ (identity), ์ดˆ๊ธฐ๊ฐ’ ์žˆ์Œ.
T reduce(T identity, BinaryOperator<T> accumulator);

// 3๊ฐœ (combiner), ๋งˆ์ง€๋ง‰ ๊ฐ’์œผ๋กœ  ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋‚˜๋ˆ  ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น˜๋Š” ๋กœ์ง์„ ์ ๋Š”๋‹ค.
<U> U reduce(U identity, 
  BiFunction<U, ? super T, U> accumulator,
  BinaryOperator<U> combiner);
// ์ธ์ž๊ฐ€ ํ•˜๋‚˜์ธ ๊ฒฝ์šฐ (ํ•จ์ˆ˜)
OptionalInt reduced = 
  IntStream.range(1, 4) // [1, 2, 3]
  .reduce((a, b) -> Integer.sum(a, b)); // ๋žŒ๋‹ค์‹์œผ๋กœ ํ‘œํ˜„
  
// ์ธ์ž๊ฐ€ 2๊ฐœ์ธ ๊ฒฝ์šฐ (์ดˆ๊นƒ๊ฐ’, ํ•จ์ˆ˜)
int reducedTwoParams = 
  IntStream.range(1, 4) // [1, 2, 3]
  .reduce(10, Integer::sum); // ๋ฉ”์†Œ๋“œ ๋ ˆํผ๋Ÿฐ์Šค๋กœ ํ‘œํ˜„
  
// ์ธ์ž๊ฐ€ 3๊ฐœ์ธ ๊ฒฝ์šฐ (์ดˆ๊นƒ๊ฐ’, ํ•จ์ˆ˜, Combiner)
Integer reducedParallel = Arrays.asList(1, 2, 3)
  .parallelStream() // ๋‹จ์ผ Stream์˜ ๊ฒฝ์šฐ Combiner๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.
  .reduce(10, Integer::sum,
          (result1, result2) -> { // Combiner. ๊ฐ๊ฐ์˜ ์Šค๋ ˆ๋“œ ๊ฒฐ๊ณผ๋ฅผ ํ•ฉ์น˜๋Š” reducer
            System.out.println("combiner was called");
            return result1 + result2;
          });
// ๋งˆ์ง€๋ง‰ ๋ณ‘๋ ฌ ์ŠคํŠธ๋ฆผ ์‹คํ–‰๊ฒฐ๊ณผ. ์Šค๋ ˆ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๋ผ๋ฉด ์•ž์—์„œ๋ถ€ํ„ฐ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.
combiner was called // result1 + result2
combiner was called // [์•ž์—์„œ ๊ณ„์‚ฐํ•œ result] + result2
36

# Collect (์ปฌ๋ ‰์…˜ ๋ฐ˜ํ™˜)

์›ํ•˜๋Š”๋ฐ๋กœ ๊ฐ€๊ณต์ด ๋๋‚œ Stream์„ ํ•˜๋‚˜์˜ ๊ฐ’์ด ์•„๋‹ˆ๋ผ Collection์œผ๋กœ ๋ฐ”๊พธ์–ด์ฃผ๋Š” ์ตœ์ข…์—ฐ์‚ฐ(Terminal) ๋ฉ”์„œ๋“œ์ด๋‹ค. ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” Collectors ํด๋ž˜์Šค์— ๋ชจ์—ฌ์žˆ๋‹ค.

Stream.of(1, 3, 3, 5, 5)
      .filter(i -> i > 2)
      .map(i -> i * 2)
      .map(i -> "#" + i)
      .collect(joining("-", "<=", "=>")) // "<=#6 - #6 - #10 - #10=>"

 Collectors.joining(๊ตฌ๋ถ„์ž, prefix, postfix)  : stream์˜ ์›์†Œ๋“ค์„ ํ•˜๋‚˜์˜ String์œผ๋กœ concat ํ•œ๋‹ค.

 Collectors.toList()  : stream์˜ ์›์†Œ๋“ค์„ List์— ๋‹ด์•„์„œ return ํ•œ๋‹ค

 Collectors.toSet()  : stream์˜ ์›์†Œ๋“ค์„ set์— ๋‹ด์•„์„œ return ํ•œ๋‹ค. ( * distinct() ๋ผ๋Š” ์ค‘๊ฐœ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, toList๋ฅผ ํ†ตํ•ด์„œ๋„ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋œ set์„ ๊ตฌํ˜„ ํ•  ์ˆ˜ ๋„ ์žˆ๋‹ค.)


# ๊ธฐ๋ณธ ์ž๋ฃŒํ˜• ์ปฌ๋ ‰์…˜ Collectors.method()

.sum(), .average() ์€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ˜๋ฉด, collect(Collectors.๋ฉ”์„œ๋“œ)๋Š” ์ปฌ๋ ‰์…˜์„ ๋ฐ˜ํ™˜๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

// ์ŠคํŠธ๋ฆผ์„ ํ†ตํ•ด int ์š”์†Œ๋“ค์„ ๋ฐ›์•„ ์ „์ฒด ํ‰๊ท ์„ Double ์ปฌ๋ ‰์…˜์œผ๋กœ ๋ฐ˜ํ™˜ํ•จ.
Double averageAmount = 
 productList.stream()
  .collect(Collectors.averagingInt(Product::getAmount));

// ์ŠคํŠธ๋ฆผ์„ ํ†ตํ•ด int ์š”์†Œ๋“ค์„ ๋ฐ›์•„ ์ „์ฒด ํ•ฉ์„ Integer ์ปฌ๋ ‰์…˜์œผ๋กœ ๋ฐ˜ํ™˜ํ•จ.
Integer summingAmount = 
 productList.stream()
  .collect(Collectors.summingInt(Product::getAmount));
 
 // SummaryStatistics ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด ์—ฌ๋Ÿฌ ํ†ต๊ณ„ ๊ฐ’์„ ํ•œ๋ฒˆ์— ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
 // IntSummaryStatistics {count=5, sum=86, min=13, average=17.200000, max=23}
 IntSummaryStatistics statistics = 
 productList.stream()
  .collect(Collectors.summarizingInt(Product::getAmount));
  
 statistics.getCount()
 statistics.getSum()
 statistics.getAverage()
 statistics.getMin()
 statistics.getMax()
// ๋ฌผ๋ก  ๊ธฐ๋ณธํ˜• ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ „ํ™˜ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.
Integer summingAmount = 
  productList.stream()
  .mapToInt(Product::getAmount) // ๊ธฐ๋ณธํ˜• int ์ŠคํŠธ๋ฆผ์œผ๋กœ ์ „ํ™˜
  .sum(); // sum ์‚ฌ์šฉ๊ฐ€๋Šฅ

 


# Collectors.groupingBy()

ํŠน์ • ์กฐ๊ฑด์œผ๋กœ ์š”์†Œ๋“ค์„ ๊ทธ๋ฃนํ™” ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” Map<Integer, List<>> ์™€ ๊ฐ™์€ ํ˜•ํƒœ์˜ ์ปฌ๋ ‰์…˜์œผ๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.

Map<Integer, List<Product>> collectorMapOfLists =
 productList.stream()
  .collect(Collectors.groupingBy(Product::getAmount));
 
 // Product๋กœ ์ด๋ฃจ์–ด์ง„ ๋ฆฌ์ŠคํŠธ๋ฅผ amount(int)๋กœ groupingBy ํ•œ ๊ฒฐ๊ณผ
 // Map์— key๋Š” intํ˜•, value๋Š” ๋ฆฌ์ŠคํŠธ๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค. ๋ฆฌ์ŠคํŠธ ์•ˆ์—๋Š” Product ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค.
     23=[Product{amount=23, name='potatoes'}, Product{amount=23, name='bread'}], 
     
     13=[Product{amount=13, name='lemon'}, Product{amount=13, name='sugar'}], 
     
     14=[Product{amount=14, name='orange'}]
 }

 

# Colletors.partitioningBy()

grounpingBy์™€ ๊ฑฐ์˜ ๋น„์Šทํ•œ๋ฐ, ํ•จ์ˆ˜ ๊ฐ’์ด ์•„๋‹Œ ์กฐ๊ฑด (Predicate)์„ key๊ฐ’์œผ๋กœ ์ด์šฉํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ๋ฅผ 2๊ฐœ๋กœ ๋‚˜๋ˆˆ๋‹ค.

Map<Boolean, List<Product>> mapPartitioned = 
  productList.stream()
  .collect(Collectors.partitioningBy(el -> el.getAmount() > 15));
  
{ // ๊ฒฐ๊ณผ๋ฌผ
  false=[ Product{amount=14, name='orange'}, 
          Product{amount=13, name='lemon'}, 
          Product{amount=13, name='sugar'} ], 
          
   true=[Product{amount=23, name='potato'}, 
          Product{amount=23, name='bread'} ]
}

# Collector.of()

์›ํ•˜๋Š” Collector๊ฐ€ ์—†๋‹ค๋ฉด .of()๋กœ ๋งŒ๋“ค๊ณ  Collector ๊ฐ์ฒด์— ๋‹ด์•„ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” .reduce()์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

public static<T, R> Collector<T, R, R> of(
  Supplier<R> supplier, // new collector ์ƒ์„ฑ
  BiConsumer<R, T> accumulator, // ๋‘ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ๊ณ„์‚ฐ
  BinaryOperator<R> combiner, // ๊ณ„์‚ฐํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ํ•จ์ˆ˜.
  Characteristics... characteristics) { ... }
Collector<Product, ?, LinkedList<Product>> toLinkedList = 
  Collector.of(LinkedList::new, //์ดˆ๊นƒ๊ฐ’ Collector 
               LinkedList::add, //ํ•จ์ˆ˜
               (first, second) -> { // Combiner, ์—ฌ๊ธฐ์—์„œ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ ์‚ฌ์šฉํ•œ๋‹ค.
                 first.addAll(second);
                 return first;
               });
               
// ์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„ Collector ํ•จ์ˆ˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
LinkedList<Product> linkedListOfPersons = 
  productList.stream()
  .collect(toLinkedList); // ๋‚ด๊ฐ€ ๋งŒ๋“  Collector ํ•จ์ˆ˜, toLinkedList

# Match (์กฐ๊ฑด ํ™•์ธ, boolean ๋ฐ˜ํ™˜)

Predicate ๋ฅผ ์ด์šฉํ•˜์—ฌ ํ•ด๋‹น ์กฐ๊ฑด์— ๋งŒ์กฑํ•˜๋Š” ์š”์†Œ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ ํ›„ boolean ๊ฐ’์„ ๋ฆฌํ„ดํ•œ๋‹ค.

๋งŒ์•ฝ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ค‘๊ฐ„์—ฐ์‚ฐ์ธ filter๋ฅผ ์ด์šฉํ•˜์ž.

  • ํ•˜๋‚˜๋ผ๋„ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์š”์†Œ๊ฐ€ ์žˆ๋Š”์ง€(.anyMatch)
  • ๋ชจ๋‘ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š”์ง€(.allMatch)
  • ๋ชจ๋‘ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ์•Š๋Š”์ง€(.noneMatch)
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
List<String> names = Arrays.asList("Eric", "Elena", "Java");

boolean anyMatch = names.stream()
  .anyMatch(name -> name.contains("a")); // true
  
boolean allMatch = names.stream() 
  .allMatch(name -> name.length() > 3); // true
  
boolean noneMatch = names.stream()
  .noneMatch(name -> name.endsWith("s")); // true

# forEach

์ž๋ฐ”์˜ for-Each ๋ฌธ์ด๋‚˜ map()๊ณผ ๋น„์Šทํ•˜๋‹ค. ๋‹ค๋งŒ Stream์„ ๋ฐ˜ํ™˜ํ•˜์ง€์•Š๊ณ  ์—ฐ์‚ฐ์„ ์ข…๋ฃŒํ•œ๋‹ค.

names.stream().forEach(System.out::println);

 

'๐ŸŒฑBackend > Java' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

#13 JSP (Java Server Pages)  (0) 2021.07.14
#12. Optional API (Null๊ฐ’ ์ฒ˜๋ฆฌ)  (0) 2021.07.12
#10 Modern Java (Java8~)  (0) 2021.07.12
#8 Java - 3 (์“ฐ๋ ˆ๋“œ, IO, ๋„คํŠธ์›Œํฌ)  (0) 2021.07.09
#7 Exception (์˜ˆ์™ธ์ฒ˜๋ฆฌ)  (0) 2021.07.07

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ