이 연재글은 Java 알아보기의 3번째 글입니다.

개발에서 자주 사용하는 Java stream 예제 코드 정리

// 배열 스트림 
String[] strArr = new String[]{"one", "two", "three", "four", "five"};
Stream<String> stream = Arrays.stream(strArr);
assertThat(stream.collect(Collectors.toList()), contains("one", "two", "three", "four", "five"));

// 부분 스트림
Stream<String> partStream = Arrays.stream(strArr, 1, 2);
assertThat(partStream.collect(Collectors.toList()), contains("two"));

// 빈 스트림
Stream<String> emptyStream = Stream.empty();
assertEquals(0L, emptyStream.count());

// 컬렉션 스트림
List<String> list = Arrays.asList("one", "two", "three", "four", "five");
Stream<String> listStream = list.stream();
assertThat(listStream.collect(Collectors.toList()), contains("one", "two", "three", "four", "five"));

// 병렬 스트림
Stream<String> parallelStream = list.parallelStream();
boolean isParallel = parallelStream.isParallel();
assertTrue(isParallel);
IntStream intParallelStream = IntStream.range(1, 10).parallel();
// 병렬 처리로 인해 순서대로 생성되지 않음
assertThat(intParallelStream.boxed().collect(Collectors.toList()), containsInAnyOrder(1, 2, 3, 4, 5, 6, 7, 8, 9));
System.out.println();

// 병렬 스트림 재생성
intParallelStream = IntStream.range(1, 10).parallel();

// 시퀀셜 스트림으로 변경
intParallelStream = intParallelStream.sequential();
// 시퀀셜 처리로 인해 순서대로 숫자생성됨.
assertThat(intParallelStream.boxed().collect(Collectors.toList()), contains(1, 2, 3, 4, 5, 6, 7, 8, 9));

// 빌더 패턴을 사용하여 스트림 생성
Stream<String> builderStream = Stream.<String>builder().add("one").add("two").add("three").build();
assertThat(builderStream.collect(Collectors.toList()), contains("one", "two", "three"));

// 스트림 generate - 무한으로 생성되므로 limit이 필요
Stream<String> generateStream = Stream.generate(() -> "gen").limit(5);
assertThat(generateStream.collect(Collectors.toList()), contains("gen", "gen", "gen", "gen", "gen"));

// 스트림 iterate - 무한으로 생성되므로 limit이 필요
Stream<Integer> iterateStream = Stream.iterate(10, n -> n + 2).limit(5);
assertThat(iterateStream.collect(Collectors.toList()), contains(10, 12, 14, 16, 18));

// 기본 타입 스트림
IntStream intStream = IntStream.range(1, 5);
assertThat(intStream.boxed().collect(Collectors.toList()), contains(1, 2, 3, 4));

LongStream longStream = LongStream.rangeClosed(1, 5);
assertThat(longStream.boxed().collect(Collectors.toList()), contains(1L, 2L, 3L, 4L, 5L));

DoubleStream doubleStream = new Random().doubles(10);

// Boxing : IntStream -> Stream
Stream<Integer> boxedIntStream = IntStream.range(1, 5).boxed();
assertThat(boxedIntStream.collect(Collectors.toList()), contains(1, 2, 3, 4));

// Char stream
IntStream charsStream = "Stream".chars();
assertThat(charsStream.boxed().collect(Collectors.toList()), contains(83, 116, 114, 101, 97, 109));

// RegEx - 문자열을 정규표현식을 적용하여 스트림으로 반환
Stream<String> stringStream = Pattern.compile(", ").splitAsStream("one, two, three");
assertThat(stringStream.collect(Collectors.toList()), contains("one", "two", "three"));

// File -> Stream
try {
    Stream<String> lineStream = Files.lines(Paths.get("file.txt"), Charset.forName("UTF-8"));
} catch (Exception e) {}

// 두개의 Stream 합치기(병합)
List<String> listOne = Stream.of("one", "two", "three").collect(Collectors.toList());
List<String> listTwo = Stream.of("four", "five", "six").collect(Collectors.toList());
Stream<String> concatStream = Stream.concat(listOne.stream(), listTwo.stream());
assertThat(concatStream.collect(Collectors.toList()), contains("one", "two", "three", "four", "five", "six"));

// 필터링
concatStream = Stream.concat(listOne.stream(), listTwo.stream());
Stream<String> fiterStream = concatStream.filter(num -> num.contains("three"));
assertThat(fiterStream.collect(Collectors.toList()), contains("three"));

// 맵핑 - 특정값으로 변환
concatStream = Stream.concat(listOne.stream(), listTwo.stream());
Stream<String> uppperStream = concatStream.map(String::toUpperCase);
assertThat(uppperStream.collect(Collectors.toList()), contains("ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX"));

// 중첩된 리스트를 단일 리스트로 변환 - flatMap
List<List<String>> overlapList = Arrays.asList(Arrays.asList("one", "two"), Arrays.asList("three", "four"));
Stream<String> flatStream = overlapList.stream().flatMap(Collection::stream);
assertThat(flatStream.collect(Collectors.toList()), contains("one", "two", "three", "four"));

// 정렬
List<Integer> sortedList = IntStream.of(14, 11, 20, 39, 23)
        .sorted()
        .boxed()
        .collect(Collectors.toList());
assertThat(sortedList, contains(11, 14, 20, 23, 39));

List<String> lang = Arrays.asList("Java", "Scala", "Groovy", "Python", "Go", "Swift");

// 정렬하여 리스트로 반환
List<String> sortedLists = lang.stream().sorted().collect(Collectors.toList());
assertThat(sortedLists, contains("Go", "Groovy", "Java", "Python", "Scala", "Swift"));

// 역순으로 정렬하여 리스트로 반환
List<String> sortedRLists = lang.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
assertThat(sortedRLists, contains("Swift", "Scala", "Python", "Java", "Groovy", "Go"));

// 데이터 길이로 정렬 후 리스트로 반환
List<String> sortedSizeLists = lang.stream().sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList());
assertThat(sortedSizeLists, contains("Go", "Java", "Scala", "Swift", "Groovy", "Python"));

// 데이터 길이로 역순 정렬 후 리스트로 반환
List<String> sortedSizeRLists = lang.stream().sorted((s1, s2) -> s2.length() - s1.length()).collect(Collectors.toList());
assertThat(sortedSizeRLists, contains("Groovy", "Python", "Scala", "Swift", "Java", "Go"));

// peek - 연산 중간에 데이터 확인
int sum = IntStream.of(1, 3, 5, 7, 9).peek(System.out::println).sum();
assertEquals(25, sum);

// Calculating - 데이터 건수, 합계 반환
assertEquals(5, IntStream.of(1, 3, 5, 7, 9).count());
assertEquals(25, IntStream.of(1, 3, 5, 7, 9).sum());

// 최소값 계산
OptionalInt min = IntStream.of(1, 3, 5, 7, 9).min();
assertEquals(1, min.getAsInt());

// 최대값 계산
OptionalInt max = IntStream.of(1, 3, 5, 7, 9).max();
assertEquals(9, max.getAsInt());

// 평균 계산
OptionalDouble average = DoubleStream.of(1.1, 2.2, 3.3, 4.4, 5.5).average();
assertEquals(3.3, average.getAsDouble());

// reduce - 컬렉션의 값 하나 하나를 꺼내 연산.
OptionalInt reduced = IntStream.range(1, 4).reduce(
        (a, b) -> {
            return Integer.sum(a, b);
        });
assertEquals(6, reduced.getAsInt());

// reduce - 위 연산과 동일하나 초기값 지정하여 연산.
int reduceTwoParam = IntStream.range(1, 4).reduce(10, Integer::sum);
assertEquals(16, reduceTwoParam);

Integer reducedParallel = Arrays.asList(1, 2, 3, 4).parallelStream()
        .reduce(10, Integer::sum, (a, b) -> {
            return a + b;
        });
assertEquals(50, reducedParallel.intValue());

// Collecting
List<Product> productList =
        Arrays.asList(
                new Product(23, "potatoes"),
                new Product(14, "orange"),
                new Product(13, "lemon"),
                new Product(23, "bread"),
                new Product(13, "suger"));
List<String> collectorCollection =
        productList.stream()
                .map(Product::getName)
                .collect(Collectors.toList());
assertThat(collectorCollection, contains("potatoes", "orange", "lemon", "bread", "suger"));

// Joining
String listToString =
        productList.stream()
                .map(Product::getName)
                .collect(Collectors.joining());
assertEquals("potatoesorangelemonbreadsuger", listToString);

String listToString2 =
        productList.stream()
                .map(Product::getName)
                .collect(Collectors.joining(", ", "<", ">"));
assertEquals("<potatoes, orange, lemon, bread, suger>", listToString2);

// Average
Double averageAmount =
        productList.stream()
                .collect(Collectors.averagingInt(Product::getAmount));
assertEquals(17.2, averageAmount, 0.1);

// Sum
Integer sumAmount =
        productList.stream()
                .collect(Collectors.summingInt(Product::getAmount));
assertEquals(Integer.valueOf(86), sumAmount);

Integer sumAmount2 = productList.stream().mapToInt(Product::getAmount).sum();
assertEquals(Integer.valueOf(86), sumAmount2);

// Summary
IntSummaryStatistics statistics =       productList.stream().collect(Collectors.summarizingInt(Product::getAmount));

assertEquals(17.2, statistics.getAverage(), 0.1);
assertEquals(5, statistics.getCount());
assertEquals(23, statistics.getMax());
assertEquals(13, statistics.getMin());
assertEquals(86, statistics.getSum());

// Grouping
Map<Integer, List<Product>> collectorMapOfLists =
        productList.stream().collect(Collectors.groupingBy(Product::getAmount));

assertEquals(2, collectorMapOfLists.get(13).size());
assertEquals(1, collectorMapOfLists.get(14).size());

// Partition
Map<Boolean, List<Product>> mapPartitioned =
        productList.stream().collect(Collectors.partitioningBy(el -> el.getAmount() > 15));

assertEquals(2, mapPartitioned.get(true).size());
assertEquals(3, mapPartitioned.get(false).size());

// unmodifiable Set 생성
Set<Product> unmodifiableSet =      productList.stream().collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet));

// Collector.of
Collector<Product, ?, LinkedList<Product>> toLinkedList =
        Collector.of(LinkedList::new, LinkedList::add,
                (first, second) -> {
                    first.addAll(second);
                    return first;
                }
        );
LinkedList<Product> linkedListOfPersons =
        productList.stream().collect(toLinkedList);

// matching
List<String> names = Arrays.asList("Eric", "Elena", "Java");
boolean anyMatch = names.stream().anyMatch(name -> name.contains("J"));
assertTrue(anyMatch);
boolean allMatch = names.stream().allMatch(name -> name.length() > 3);
assertTrue(allMatch);
boolean noneMatch = names.stream().noneMatch(name -> name.endsWith("s"));
assertTrue(noneMatch);

public static class Product {
    private int amount;
    private String name;

    public int getAmount() {
        return amount;
    }

    public void setAmount(int amount) {
        this.amount = amount;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Product(int amount, String name) {
        this.amount = amount;
        this.name = name;
    }
}
연재글 이동[이전글] Java Optional 사용법
[다음글] Java Collectors 알아보기