development

스트림을 재사용 할 수있는 방법이 있습니까?

big-blog 2021. 1. 9. 11:24
반응형

스트림을 재사용 할 수있는 방법이 있습니까?


새로운 Java 8 기능을 배우고 있으며 스트림 ( java.util.stream.Stream)과 수집기 를 실험하면서 스트림을 두 번 사용할 수 없다는 것을 깨달았습니다.

재사용 할 수있는 방법이 있습니까?


스트림을 재사용하는 효과를 얻으 myStreamSupplier.get()려면 공급자에서 스트림 표현식을 래핑하고 새로운 것을 원할 때마다 호출 할 수 있습니다. 예를 들면 :

Supplier<Stream<String>> sup = () -> someList.stream();
List<String> nonEmptyStrings = sup.get().filter(s -> !s.isEmpty()).collect(Collectors.toList());
Set<String> uniqueStrings = sup.get().collect(Collectors.toSet());

로부터 문서 :

스트림은 한 번만 작동해야합니다 (중간 또는 터미널 스트림 작업 호출).

스트림 구현은 스트림이 재사용되고 있음을 감지하면 IllegalStateException을 발생시킬 수 있습니다.

따라서 대답은 '아니요'입니다. 스트림은 재사용 할 수 없습니다.


다른 사람들이 말했듯이 "당신은 할 수 없습니다".

그러나 summaryStatistics()많은 기본 작업에 편리함을 기억하는 것이 유용 합니다.

그래서 대신 :

List<Person> personList = getPersons();

personList.stream().mapToInt(p -> p.getAge()).average().getAsDouble();
personList.stream().mapToInt(p -> p.getAge()).min().getAsInt();
personList.stream().mapToInt(p -> p.getAge()).max().getAsInt();

다음을 수행 할 수 있습니다.

// Can also be DoubleSummaryStatistics from mapToDouble()

IntSummaryStatistics stats = personList.stream()
                                       .mapToInt(p-> p.getAge())
                                       .summaryStatistics();

stats.getAverage();
stats.getMin();
stats.getMax();

Stream의 전체 아이디어는 일회성이라는 것입니다. 이렇게하면 중간 저장소없이 다시 시작할 수없는 소스 (예 : 네트워크 연결에서 회선 읽기)를 만들 수 있습니다. 그러나 스트림 콘텐츠를 재사용하려면 중간 컬렉션에 덤프하여 "하드 카피"를 얻을 수 있습니다.

Stream<MyType> stream = // get the stream from somewhere
List<MyType> list = stream.collect(Collectors.toList()); // materialize the stream contents
list.stream().doSomething // create a new stream from the list
list.stream().doSomethingElse // create one more stream from the list

스트림을 구체화하지 않으려면 경우에 따라 동일한 스트림으로 한 번에 여러 작업을 수행 할 수있는 방법이 있습니다. 예를 들어, 당신은 참조 할 수 있습니다 또는 세부 사항에 대한 질문입니다.


생각해 보면 스트림을 "재사용"하려는 의지는 멋진 인라인 작업으로 원하는 결과를 수행하려는 의지 일뿐입니다. 그래서, 기본적으로 우리가 여기서 말하는 것은 터미널 연산을 작성한 후 처리를 계속하기 위해 무엇을 할 수 있습니까?

1) 터미널 작업이 컬렉션을 반환하면 모든 컬렉션을 스트림으로 되돌릴 수 있기 때문에 문제가 즉시 해결됩니다 (JDK 8).

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .collect(Collectors.toList())
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

2) 터미널 작업 이 Optional 클래스에 대한 JDK 9 개선 사항을 사용 하여 optional을 반환하는 경우 Optional 결과를 스트림으로 전환하고 원하는 멋진 인라인 작업을 얻을 수 있습니다.

List<Integer> l=Arrays.asList(5,10,14);
        l.stream()
            .filter(nth-> nth>5)
            .findAny()
            .stream()
            .filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));

3) 터미널 작업이 다른 것을 반환하는 경우 이러한 결과를 처리하기 위해 스트림을 고려해야한다고 생각합니다.

List<Integer> l=Arrays.asList(5,10,14);
        boolean allEven=l.stream()
            .filter(nth-> nth>5)
            .allMatch(nth-> nth%2==0);
        if(allEven){
            ...
        }

As others have noted the stream object itself cannot be reused.

But one way to get the effect of reusing a stream is to extract the stream creation code to a function.

You can do this by creating a method or a function object which contains the stream creation code. You can then use it multiple times.

Example:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

    // The normal way to use a stream:
    List<String> result1 = list.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    // The stream operation can be extracted to a local function to
    // be reused on multiple sources:
    Function<List<Integer>, List<String>> listOperation = l -> l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());

    List<String> result2 = listOperation.apply(list);
    List<String> result3 = listOperation.apply(Arrays.asList(1, 2, 3));

    // Or the stream operation can be extracted to a static method,
    // if it doesn't refer to any local variables:
    List<String> result4 = streamMethod(list);

    // The stream operation can also have Stream as argument and return value,
    // so that it can be used as a component of a longer stream pipeline:
    Function<Stream<Integer>, Stream<String>> streamOperation = s -> s
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i);

    List<String> result5 = streamOperation.apply(list.stream().map(i -> i * 2))
        .filter(s -> s.length() < 7)
        .sorted()
        .collect(toCollection(LinkedList::new));
}

public static List<String> streamMethod(List<Integer> l) {
    return l.stream()
        .filter(i -> i % 2 == 1)
        .map(i -> i * i)
        .limit(10)
        .map(i -> "i :" + i)
        .collect(toList());
}

If, on the other hand, you already have a stream object which you want to iterate over multiple times, then you must save the content of the stream in some collection object.

You can then get multiple streams with the same content from than collection.

Example:

public void test(Stream<Integer> stream) {
    // Create a copy of the stream elements
    List<Integer> streamCopy = stream.collect(toList());

    // Use the copy to get multiple streams
    List<Integer> result1 = streamCopy.stream() ...
    List<Integer> result2 = streamCopy.stream() ...
}

The Functional Java library provides its own streams that do what you are asking for, i.e. they're memoized and lazy. You can use its conversion methods to convert between Java SDK objects and FJ objects, e.g. Java8.JavaStream_Stream(stream) will return a reusable FJ stream given a JDK 8 stream.

ReferenceURL : https://stackoverflow.com/questions/36255007/is-there-any-way-to-reuse-a-stream

반응형