[java] Java 8에서 인덱스를 사용하여 스트림을 반복하는 간결한 방법이 있습니까?

스트림의 인덱스에 액세스하면서 스트림을 반복하는 간결한 방법이 있습니까?

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};

List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
        .filter(e -> e.getValue().length() <= e.getKey())
        .map(Entry::getValue)
        .collect(toList());

LINQ 예제에 비해 다소 실망스러워 보입니다.

string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();

더 간결한 방법이 있습니까?

또한 지퍼가 움직이거나 제거 된 것 같습니다 …



답변

가장 깨끗한 방법은 일련의 인덱스에서 시작하는 것입니다.

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0, names.length)
         .filter(i -> names[i].length() <= i)
         .mapToObj(i -> names[i])
         .collect(Collectors.toList());

결과 목록에는 “Erik”만 포함됩니다.


루프에 익숙 할 때 더 친숙해 보이는 한 가지 대안은 다음과 같이 변경 가능한 객체를 사용하여 임시 카운터를 유지하는 것입니다 AtomicInteger.

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
AtomicInteger index = new AtomicInteger();
List<String> list = Arrays.stream(names)
                          .filter(n -> n.length() <= index.incrementAndGet())
                          .collect(Collectors.toList());

병렬 스트림에서 후자의 방법사용하면 항목이 “순서대로”처리되지 않기 때문에 중단 될 수 있습니다 .


답변

Java 8 스트림 API에는 스트림 요소의 색인을 얻는 기능과 스트림을 함께 압축하는 기능이 없습니다. LINQ 과제와 같은 특정 응용 프로그램을 다른 방법보다 더 어렵게 만드는 것은 불행한 일입니다.

그러나 종종 해결 방법이 있습니다. 일반적으로 이것은 정수 범위의 스트림을 “구동”하고 원래 요소가 종종 배열 또는 색인으로 액세스 가능한 콜렉션에 있다는 사실을 이용하여 수행 할 수 있습니다. 예를 들어, Challenge 2 문제는 다음과 같이 해결할 수 있습니다.

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};

List<String> nameList =
    IntStream.range(0, names.length)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(toList());

위에서 언급했듯이, 이것은 데이터 소스 (이름 배열)가 직접 인덱싱 가능하다는 사실을 이용합니다. 그렇지 않은 경우이 기술은 작동하지 않습니다.

이것이 도전 2의 의도를 만족시키지 못한다는 것을 인정할 것이다. 그럼에도 불구하고 그것이 문제를 합리적으로 효과적으로 해결한다.

편집하다

필자의 이전 코드 예제 flatMap는 필터 및 맵 작업을 통합하는 데 사용 되었지만 번거롭고 이점이 없었습니다. Holger의 의견에 따라 예제를 업데이트했습니다.


답변

구아바 21부터 사용할 수 있습니다

Streams.mapWithIndex()

예 ( 공식 문서에서 ) :

Streams.mapWithIndex(
    Stream.of("a", "b", "c"),
    (str, index) -> str + ":" + index)
) // will return Stream.of("a:0", "b:1", "c:2")


답변

내 프로젝트에서 다음 솔루션을 사용했습니다. 가변 객체 또는 정수 범위를 사용하는 것보다 낫다고 생각합니다.

import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Objects.requireNonNull;


public class CollectionUtils {
    private CollectionUtils() { }

    /**
     * Converts an {@link java.util.Iterator} to {@link java.util.stream.Stream}.
     */
    public static <T> Stream<T> iterate(Iterator<? extends T> iterator) {
        int characteristics = Spliterator.ORDERED | Spliterator.IMMUTABLE;
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, characteristics), false);
    }

    /**
     * Zips the specified stream with its indices.
     */
    public static <T> Stream<Map.Entry<Integer, T>> zipWithIndex(Stream<? extends T> stream) {
        return iterate(new Iterator<Map.Entry<Integer, T>>() {
            private final Iterator<? extends T> streamIterator = stream.iterator();
            private int index = 0;

            @Override
            public boolean hasNext() {
                return streamIterator.hasNext();
            }

            @Override
            public Map.Entry<Integer, T> next() {
                return new AbstractMap.SimpleImmutableEntry<>(index++, streamIterator.next());
            }
        });
    }

    /**
     * Returns a stream consisting of the results of applying the given two-arguments function to the elements of this stream.
     * The first argument of the function is the element index and the second one - the element value. 
     */
    public static <T, R> Stream<R> mapWithIndex(Stream<? extends T> stream, BiFunction<Integer, ? super T, ? extends R> mapper) {
        return zipWithIndex(stream).map(entry -> mapper.apply(entry.getKey(), entry.getValue()));
    }

    public static void main(String[] args) {
        String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};

        System.out.println("Test zipWithIndex");
        zipWithIndex(Arrays.stream(names)).forEach(entry -> System.out.println(entry));

        System.out.println();
        System.out.println("Test mapWithIndex");
        mapWithIndex(Arrays.stream(names), (Integer index, String name) -> index+"="+name).forEach((String s) -> System.out.println(s));
    }
}


답변

protonpack뿐만 아니라, jOOλ의 서열은 이 기능을 제공 (그리고 같은 그 위에 빌드 것을 확장 라이브러리가 외눈 박이가-반응 ,이 라이브러리의 저자).

Seq.seq(Stream.of(names)).zipWithIndex()
                         .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
                         .toList();

Seq는 Seq.of (names) 만 지원하며 표지 아래에 JDK 스트림을 빌드합니다.

간단한 반응식과 비슷하게 보일 것입니다.

 LazyFutureStream.of(names)
                 .zipWithIndex()
                 .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
                 .toList();

단순 반응 버전은 비동기 / 동시 처리에보다 적합합니다.


답변

완성을 위해 StreamEx 라이브러리 와 관련된 솔루션이 있습니다.

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
EntryStream.of(names)
    .filterKeyValue((idx, str) -> str.length() <= idx+1)
    .values().toList();

여기서 우리 는 or 와 같은 특정 작업 EntryStream<Integer, String>을 확장 Stream<Entry<Integer, String>>하고 추가 하는를 만듭니다 . 또한 바로 가기가 사용됩니다.filterKeyValuevaluestoList()


답변

스트림이 목록이나 배열로 만들어 졌을 때 해결책을 찾았습니다 (크기를 알고 있습니다). 그러나 스트림 크기를 알 수없는 경우 어떻게해야합니까? 이 경우 다음 변형을 시도하십시오.

public class WithIndex<T> {
    private int index;
    private T value;

    WithIndex(int index, T value) {
        this.index = index;
        this.value = value;
    }

    public int index() {
        return index;
    }

    public T value() {
        return value;
    }

    @Override
    public String toString() {
        return value + "(" + index + ")";
    }

    public static <T> Function<T, WithIndex<T>> indexed() {
        return new Function<T, WithIndex<T>>() {
            int index = 0;
            @Override
            public WithIndex<T> apply(T t) {
                return new WithIndex<>(index++, t);
            }
        };
    }
}

용법:

public static void main(String[] args) {
    Stream<String> stream = Stream.of("a", "b", "c", "d", "e");
    stream.map(WithIndex.indexed()).forEachOrdered(e -> {
        System.out.println(e.index() + " -> " + e.value());
    });
}