是否有一种简洁的方法来迭代Java 8中的索引流?

时间:2021-10-12 12:02:41

Is there a concise way to iterate over a stream whilst having access to the index in the stream?

在访问流中的索引时,是否有一种简单的方法来迭代流?

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());

which seems rather disappointing compared to the LINQ example given there

和这里给出的LINQ例子相比,这看起来有些令人失望?

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

Is there a more concise way?

有没有更简洁的方式?

Further it seems the zip has either moved or been removed...

此外,似乎拉链已经移动或被移走了……

17 个解决方案

#1


291  

The cleanest way is to start from a stream of indices:

最干净的方法是从一连串的指数开始:

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());

The resulting list contains "Erik" only.

结果列表只包含“Erik”。


One alternative which looks more familiar when you are used to for loops would be to maintain an ad hoc counter using a mutable object, for example an AtomicInteger:

当您习惯于for循环时,一个看起来更熟悉的替代方法是使用可变对象(例如一个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());

Note that using the latter method on a parallel stream could break as the items would not necesarily be processed "in order".

注意,在并行流上使用后一种方法可能会中断,因为这些项不会被“按顺序”处理。

#2


54  

The Java 8 streams API lacks the features of getting the index of a stream element as well as the ability to zip streams together. This is unfortunate, as it makes certain applications (like the LINQ challenges) more difficult than they would be otherwise.

Java 8 streams API缺乏获取流元素索引的特性,也缺乏将流压缩到一起的能力。这是不幸的,因为它使某些应用程序(如LINQ挑战)比其他应用程序更困难。

There are often workarounds, however. Usually this can be done by "driving" the stream with an integer range, and taking advantage of the fact that the original elements are often in an array or in a collection accessible by index. For example, the Challenge 2 problem can be solved this way:

然而,也有很多变通方法。通常,这可以通过“驱动”流的整数范围来完成,并利用了原始元素通常在数组中或在索引中可访问的集合中的事实。例如,挑战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());

As I mentioned above, this takes advantage of the fact that the data source (the names array) is directly indexable. If it weren't, this technique wouldn't work.

正如我前面提到的,这利用了一个事实,即数据源(名称数组)是可直接索引的。如果不是,这种技术就不会起作用。

I'll admit that this doesn't satisfy the intent of Challenge 2. Nonetheless it does solve the problem reasonably effectively.

我承认这不能满足挑战2的意图。尽管如此,它确实有效地解决了这个问题。

EDIT

编辑

My previous code example used flatMap to fuse the filter and map operations, but this was cumbersome and provided no advantage. I've updated the example per the comment from Holger.

我以前的代码示例使用平面映射来融合过滤器和映射操作,但这很麻烦,没有提供任何优势。我根据Holger的评论更新了这个例子。

#3


21  

I've used the following solution in my project. I think it is better than using mutable objects or integer ranges.

我在我的项目中使用了以下的解决方案。我认为它比使用可变对象或整数范围要好。

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));
    }
}

#4


14  

Since guava 21, you can use

因为guava 21,你可以使用。

Streams.mapWithIndex()

Example (from official doc):

示例(来自官方文档):

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

#5


13  

In addition to protonpack, jOOλ's Seq provides this functionality (and by extension libraries that build on it like cyclops-react, I am the author of this library).

除了protonpack,jOOλSeq提供了这种功能(通过扩展库构建像cyclops-react,我这库)的作者。

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

Seq also supports just Seq.of(names) and will build a JDK Stream under the covers.

Seq还支持Seq.of(名称),并将在覆盖下构建一个JDK流。

The simple-react equivalent would similarly look like

简单反应的当量也类似。

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

The simple-react version is more tailored for asynchronous / concurrent processing.

简单反应版本更适合于异步/并发处理。

#6


11  

Just for completeness here's the solution involving my StreamEx library:

为了完整起见,这里是我的StreamEx库的解决方案:

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

Here we create an EntryStream<Integer, String> which extends Stream<Entry<Integer, String>> and adds some specific operations like filterKeyValue or values. Also toList() shortcut is used.

这里,我们创建了一个EntryStream ,它扩展了Stream >,并添加了一些特定的操作,如filterKeyValue或values。还使用toList()快捷方式。 ,>

#7


4  

There isn't a way to iterate over a Stream whilst having access to the index because a Stream is unlike any Collection. A Stream is merely a pipeline for carrying data from one place to another, as stated in the documentation:

在访问索引时,没有一种方法可以遍历流,因为流不像任何集合。流仅仅是将数据从一个地方传输到另一个地方的管道,正如文档中所述:

No storage. A stream is not a data structure that stores elements; instead, they carry values from a source (which could be a data structure, a generator, an IO channel, etc) through a pipeline of computational operations.

没有存储。流不是存储元素的数据结构;相反,它们通过计算操作的管道从源(可以是数据结构、生成器、IO通道等)中携带值。

Of course, as you appear to be hinting at in your question, you could always convert your Stream<V> to a Collection<V>, such as a List<V>, in which you will have access to the indexes.

当然,当您似乎在暗示您的问题时,您总是可以将流 转换为一个集合 ,如列表 ,其中您将可以访问索引。

#8


3  

With https://github.com/poetix/protonpack u can do that zip:

使用https://github.com/poetix/protonpack u可以做这个zip:

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

List<String> nameList;
Stream<Integer> indices = IntStream.range(0, names.length).boxed(); 

nameList = StreamUtils.zip(indices, stream(names),SimpleEntry::new)
        .filter(e -> e.getValue().length() <= e.getKey()).map(Entry::getValue).collect(toList());                   

System.out.println(nameList);

#9


3  

If you don't mind using a third-party library, Eclipse Collections has zipWithIndex and forEachWithIndex available for use across many types. Here's a set of solutions to this challenge for both JDK types and Eclipse Collections types using zipWithIndex.

如果您不介意使用第三方库,Eclipse系列将有zipWithIndex和forEachWithIndex,可以用于多种类型。下面是使用zipWithIndex对JDK类型和Eclipse集合类型的挑战的一组解决方案。

String[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
ImmutableList<String> expected = Lists.immutable.with("Erik");
Predicate<Pair<String, Integer>> predicate =
    pair -> pair.getOne().length() <= pair.getTwo() + 1;

// JDK Types
List<String> strings1 = ArrayIterate.zipWithIndex(names)
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings1);

List<String> list = Arrays.asList(names);
List<String> strings2 = ListAdapter.adapt(list)
    .zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings2);

// Eclipse Collections types
MutableList<String> mutableNames = Lists.mutable.with(names);
MutableList<String> strings3 = mutableNames.zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings3);

ImmutableList<String> immutableNames = Lists.immutable.with(names);
ImmutableList<String> strings4 = immutableNames.zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings4);

MutableList<String> strings5 = mutableNames.asLazy()
    .zipWithIndex()
    .collectIf(predicate, Pair::getOne, Lists.mutable.empty());
Assert.assertEquals(expected, strings5);

Here's a solution using forEachWithIndex instead.

这里有一个使用forEachWithIndex的解决方案。

MutableList<String> mutableNames =
    Lists.mutable.with("Sam", "Pamela", "Dave", "Pascal", "Erik");
ImmutableList<String> expected = Lists.immutable.with("Erik");

List<String> actual = Lists.mutable.empty();
mutableNames.forEachWithIndex((name, index) -> {
        if (name.length() <= index + 1)
            actual.add(name);
    });
Assert.assertEquals(expected, actual);

If you change the lambdas to anonymous inner classes above, then all of these code examples will work in Java 5 - 7 as well.

如果您将lambdas更改为上面的匿名内部类,那么所有这些代码示例都将在Java 5 - 7中工作。

Note: I am a committer for Eclipse Collections

注意:我是Eclipse集合的提交者。

#10


3  

With a List you can try

你可以试着列出一个清单。

List<String> strings = new ArrayList<>(Arrays.asList("First", "Second", "Third", "Fourth", "Fifth")); // An example list of Strings
strings.stream() // Turn the list into a Stream
    .collect(HashMap::new, (h, o) -> h.put(h.size(), o), (h, o) -> {}) // Create a map of the index to the object
        .forEach((i, o) -> { // Now we can use a BiConsumer forEach!
            System.out.println(String.format("%d => %s", i, o));
        });

Output:

输出:

0 => First
1 => Second
2 => Third
3 => Fourth
4 => Fifth

#11


2  

I found the solutions here when the Stream is created of list or array (and you know the size). But what if Stream is with unknown size? In this case try this variant:

当流创建列表或数组时,我找到了解决方案(您知道大小)。但是如果流的大小未知呢?在这种情况下尝试这种变体:

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);
            }
        };
    }
}

Usage:

用法:

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());
    });
}

#12


1  

Here is code by AbacusUtil

这是AbacusUtil的代码。

Stream.of(names).indexed()
      .filter(e -> e.value().length() <= e.index())
      .map(Indexed::value).toList();

Disclosure: I'm the developer of AbacusUtil.

披露:我AbacusUtil的开发者。

#13


1  

If you happen to use Vavr(formerly known as Javaslang), you can leverage the dedicated method:

如果您碰巧使用Vavr(以前称为Javaslang),您可以利用专用方法:

Stream.of("A", "B", "C")
  .zipWithIndex();

If we print out the content, we will see something interesting:

如果我们把内容打印出来,我们会看到一些有趣的东西:

Stream((A, 0), ?)

This is because Streams are lazy and we have no clue about next items in the stream.

这是因为流是懒惰的,我们不知道流中的下一个项目。

#14


0  

You can create a static inner class to encapsulate the indexer as I needed to do in example below:

您可以创建一个静态的内部类来封装索引器,如下面的示例所示:

static class Indexer {
    int i = 0;
}

public static String getRegex() {
    EnumSet<MeasureUnit> range = EnumSet.allOf(MeasureUnit.class);
    StringBuilder sb = new StringBuilder();
    Indexer indexer = new Indexer();
    range.stream().forEach(
            measureUnit -> {
                sb.append(measureUnit.acronym);
                if (indexer.i < range.size() - 1)
                    sb.append("|");

                indexer.i++;
            }
    );
    return sb.toString();
}

#15


0  

This question (Stream Way to get index of first element matching boolean) has marked the current question as a duplicate, so I can not answer it there; I am answering it here.

这个问题(获取第一个元素匹配布尔的索引的流方法)已经将当前的问题标记为一个副本,所以我不能在那里回答它;我在这里回答。

Here is a generic solution to get the matching index that does not require an external library.

这里有一个通用的解决方案,可以获得不需要外部库的匹配索引。

If you have a list.

如果你有一个列表。

public static <T> int indexOf(List<T> items, Predicate<T> matches) {
        return IntStream.range(0, items.size())
                .filter(index -> matches.test(items.get(index)))
                .findFirst().orElse(-1);
}

And call it like this:

这样称呼它:

int index = indexOf(myList, item->item.getId()==100);

And if using a collection, try this one.

如果使用一个集合,试试这个。

   public static <T> int indexOf(Collection<T> items, Predicate<T> matches) {
        int index = -1;
        Iterator<T> it = items.iterator();
        while (it.hasNext()) {
            index++;
            if (matches.test(it.next())) {
                return index;
            }
        }
        return -1;
    }

#16


0  

One possible way is to index each element on the flow:

一种可能的方法是索引流中的每个元素:

AtomicInteger index = new AtomicInteger();
Stream.of(names)
  .map(e->new Object() { String n=e; public i=index.getAndIncrement(); })
  .filter(o->o.n.length()<=o.i) // or do whatever you want with pairs...
  .forEach(o->System.out.println("idx:"+o.i+" nam:"+o.n));

Using an anonymous class along a stream is not well-used while being very useful.

在流中使用匿名类不是很有用,但是非常有用。

#17


0  

If you are trying to get an index based on a predicate, try this:

如果您试图根据谓词获取索引,请尝试以下方法:

If you only care about the first index:

如果你只关心第一个指标:

OptionalInt index = IntStream.range(0, list.size())
    .filter(i -> list.get(i) == 3)
    .findFirst();

Or if you want to find multiple indexes:

或者如果你想找到多个索引:

IntStream.range(0, list.size())
   .filter(i -> list.get(i) == 3)
   .collect(Collectors.toList());

Add .orElse(-1); in case you want to return a value if it doesn't find it.

添加.orElse(1);如果你想返回一个值,如果它没有找到。

#1


291  

The cleanest way is to start from a stream of indices:

最干净的方法是从一连串的指数开始:

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());

The resulting list contains "Erik" only.

结果列表只包含“Erik”。


One alternative which looks more familiar when you are used to for loops would be to maintain an ad hoc counter using a mutable object, for example an AtomicInteger:

当您习惯于for循环时,一个看起来更熟悉的替代方法是使用可变对象(例如一个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());

Note that using the latter method on a parallel stream could break as the items would not necesarily be processed "in order".

注意,在并行流上使用后一种方法可能会中断,因为这些项不会被“按顺序”处理。

#2


54  

The Java 8 streams API lacks the features of getting the index of a stream element as well as the ability to zip streams together. This is unfortunate, as it makes certain applications (like the LINQ challenges) more difficult than they would be otherwise.

Java 8 streams API缺乏获取流元素索引的特性,也缺乏将流压缩到一起的能力。这是不幸的,因为它使某些应用程序(如LINQ挑战)比其他应用程序更困难。

There are often workarounds, however. Usually this can be done by "driving" the stream with an integer range, and taking advantage of the fact that the original elements are often in an array or in a collection accessible by index. For example, the Challenge 2 problem can be solved this way:

然而,也有很多变通方法。通常,这可以通过“驱动”流的整数范围来完成,并利用了原始元素通常在数组中或在索引中可访问的集合中的事实。例如,挑战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());

As I mentioned above, this takes advantage of the fact that the data source (the names array) is directly indexable. If it weren't, this technique wouldn't work.

正如我前面提到的,这利用了一个事实,即数据源(名称数组)是可直接索引的。如果不是,这种技术就不会起作用。

I'll admit that this doesn't satisfy the intent of Challenge 2. Nonetheless it does solve the problem reasonably effectively.

我承认这不能满足挑战2的意图。尽管如此,它确实有效地解决了这个问题。

EDIT

编辑

My previous code example used flatMap to fuse the filter and map operations, but this was cumbersome and provided no advantage. I've updated the example per the comment from Holger.

我以前的代码示例使用平面映射来融合过滤器和映射操作,但这很麻烦,没有提供任何优势。我根据Holger的评论更新了这个例子。

#3


21  

I've used the following solution in my project. I think it is better than using mutable objects or integer ranges.

我在我的项目中使用了以下的解决方案。我认为它比使用可变对象或整数范围要好。

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));
    }
}

#4


14  

Since guava 21, you can use

因为guava 21,你可以使用。

Streams.mapWithIndex()

Example (from official doc):

示例(来自官方文档):

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

#5


13  

In addition to protonpack, jOOλ's Seq provides this functionality (and by extension libraries that build on it like cyclops-react, I am the author of this library).

除了protonpack,jOOλSeq提供了这种功能(通过扩展库构建像cyclops-react,我这库)的作者。

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

Seq also supports just Seq.of(names) and will build a JDK Stream under the covers.

Seq还支持Seq.of(名称),并将在覆盖下构建一个JDK流。

The simple-react equivalent would similarly look like

简单反应的当量也类似。

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

The simple-react version is more tailored for asynchronous / concurrent processing.

简单反应版本更适合于异步/并发处理。

#6


11  

Just for completeness here's the solution involving my StreamEx library:

为了完整起见,这里是我的StreamEx库的解决方案:

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

Here we create an EntryStream<Integer, String> which extends Stream<Entry<Integer, String>> and adds some specific operations like filterKeyValue or values. Also toList() shortcut is used.

这里,我们创建了一个EntryStream ,它扩展了Stream >,并添加了一些特定的操作,如filterKeyValue或values。还使用toList()快捷方式。 ,>

#7


4  

There isn't a way to iterate over a Stream whilst having access to the index because a Stream is unlike any Collection. A Stream is merely a pipeline for carrying data from one place to another, as stated in the documentation:

在访问索引时,没有一种方法可以遍历流,因为流不像任何集合。流仅仅是将数据从一个地方传输到另一个地方的管道,正如文档中所述:

No storage. A stream is not a data structure that stores elements; instead, they carry values from a source (which could be a data structure, a generator, an IO channel, etc) through a pipeline of computational operations.

没有存储。流不是存储元素的数据结构;相反,它们通过计算操作的管道从源(可以是数据结构、生成器、IO通道等)中携带值。

Of course, as you appear to be hinting at in your question, you could always convert your Stream<V> to a Collection<V>, such as a List<V>, in which you will have access to the indexes.

当然,当您似乎在暗示您的问题时,您总是可以将流 转换为一个集合 ,如列表 ,其中您将可以访问索引。

#8


3  

With https://github.com/poetix/protonpack u can do that zip:

使用https://github.com/poetix/protonpack u可以做这个zip:

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

List<String> nameList;
Stream<Integer> indices = IntStream.range(0, names.length).boxed(); 

nameList = StreamUtils.zip(indices, stream(names),SimpleEntry::new)
        .filter(e -> e.getValue().length() <= e.getKey()).map(Entry::getValue).collect(toList());                   

System.out.println(nameList);

#9


3  

If you don't mind using a third-party library, Eclipse Collections has zipWithIndex and forEachWithIndex available for use across many types. Here's a set of solutions to this challenge for both JDK types and Eclipse Collections types using zipWithIndex.

如果您不介意使用第三方库,Eclipse系列将有zipWithIndex和forEachWithIndex,可以用于多种类型。下面是使用zipWithIndex对JDK类型和Eclipse集合类型的挑战的一组解决方案。

String[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
ImmutableList<String> expected = Lists.immutable.with("Erik");
Predicate<Pair<String, Integer>> predicate =
    pair -> pair.getOne().length() <= pair.getTwo() + 1;

// JDK Types
List<String> strings1 = ArrayIterate.zipWithIndex(names)
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings1);

List<String> list = Arrays.asList(names);
List<String> strings2 = ListAdapter.adapt(list)
    .zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings2);

// Eclipse Collections types
MutableList<String> mutableNames = Lists.mutable.with(names);
MutableList<String> strings3 = mutableNames.zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings3);

ImmutableList<String> immutableNames = Lists.immutable.with(names);
ImmutableList<String> strings4 = immutableNames.zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings4);

MutableList<String> strings5 = mutableNames.asLazy()
    .zipWithIndex()
    .collectIf(predicate, Pair::getOne, Lists.mutable.empty());
Assert.assertEquals(expected, strings5);

Here's a solution using forEachWithIndex instead.

这里有一个使用forEachWithIndex的解决方案。

MutableList<String> mutableNames =
    Lists.mutable.with("Sam", "Pamela", "Dave", "Pascal", "Erik");
ImmutableList<String> expected = Lists.immutable.with("Erik");

List<String> actual = Lists.mutable.empty();
mutableNames.forEachWithIndex((name, index) -> {
        if (name.length() <= index + 1)
            actual.add(name);
    });
Assert.assertEquals(expected, actual);

If you change the lambdas to anonymous inner classes above, then all of these code examples will work in Java 5 - 7 as well.

如果您将lambdas更改为上面的匿名内部类,那么所有这些代码示例都将在Java 5 - 7中工作。

Note: I am a committer for Eclipse Collections

注意:我是Eclipse集合的提交者。

#10


3  

With a List you can try

你可以试着列出一个清单。

List<String> strings = new ArrayList<>(Arrays.asList("First", "Second", "Third", "Fourth", "Fifth")); // An example list of Strings
strings.stream() // Turn the list into a Stream
    .collect(HashMap::new, (h, o) -> h.put(h.size(), o), (h, o) -> {}) // Create a map of the index to the object
        .forEach((i, o) -> { // Now we can use a BiConsumer forEach!
            System.out.println(String.format("%d => %s", i, o));
        });

Output:

输出:

0 => First
1 => Second
2 => Third
3 => Fourth
4 => Fifth

#11


2  

I found the solutions here when the Stream is created of list or array (and you know the size). But what if Stream is with unknown size? In this case try this variant:

当流创建列表或数组时,我找到了解决方案(您知道大小)。但是如果流的大小未知呢?在这种情况下尝试这种变体:

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);
            }
        };
    }
}

Usage:

用法:

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());
    });
}

#12


1  

Here is code by AbacusUtil

这是AbacusUtil的代码。

Stream.of(names).indexed()
      .filter(e -> e.value().length() <= e.index())
      .map(Indexed::value).toList();

Disclosure: I'm the developer of AbacusUtil.

披露:我AbacusUtil的开发者。

#13


1  

If you happen to use Vavr(formerly known as Javaslang), you can leverage the dedicated method:

如果您碰巧使用Vavr(以前称为Javaslang),您可以利用专用方法:

Stream.of("A", "B", "C")
  .zipWithIndex();

If we print out the content, we will see something interesting:

如果我们把内容打印出来,我们会看到一些有趣的东西:

Stream((A, 0), ?)

This is because Streams are lazy and we have no clue about next items in the stream.

这是因为流是懒惰的,我们不知道流中的下一个项目。

#14


0  

You can create a static inner class to encapsulate the indexer as I needed to do in example below:

您可以创建一个静态的内部类来封装索引器,如下面的示例所示:

static class Indexer {
    int i = 0;
}

public static String getRegex() {
    EnumSet<MeasureUnit> range = EnumSet.allOf(MeasureUnit.class);
    StringBuilder sb = new StringBuilder();
    Indexer indexer = new Indexer();
    range.stream().forEach(
            measureUnit -> {
                sb.append(measureUnit.acronym);
                if (indexer.i < range.size() - 1)
                    sb.append("|");

                indexer.i++;
            }
    );
    return sb.toString();
}

#15


0  

This question (Stream Way to get index of first element matching boolean) has marked the current question as a duplicate, so I can not answer it there; I am answering it here.

这个问题(获取第一个元素匹配布尔的索引的流方法)已经将当前的问题标记为一个副本,所以我不能在那里回答它;我在这里回答。

Here is a generic solution to get the matching index that does not require an external library.

这里有一个通用的解决方案,可以获得不需要外部库的匹配索引。

If you have a list.

如果你有一个列表。

public static <T> int indexOf(List<T> items, Predicate<T> matches) {
        return IntStream.range(0, items.size())
                .filter(index -> matches.test(items.get(index)))
                .findFirst().orElse(-1);
}

And call it like this:

这样称呼它:

int index = indexOf(myList, item->item.getId()==100);

And if using a collection, try this one.

如果使用一个集合,试试这个。

   public static <T> int indexOf(Collection<T> items, Predicate<T> matches) {
        int index = -1;
        Iterator<T> it = items.iterator();
        while (it.hasNext()) {
            index++;
            if (matches.test(it.next())) {
                return index;
            }
        }
        return -1;
    }

#16


0  

One possible way is to index each element on the flow:

一种可能的方法是索引流中的每个元素:

AtomicInteger index = new AtomicInteger();
Stream.of(names)
  .map(e->new Object() { String n=e; public i=index.getAndIncrement(); })
  .filter(o->o.n.length()<=o.i) // or do whatever you want with pairs...
  .forEach(o->System.out.println("idx:"+o.i+" nam:"+o.n));

Using an anonymous class along a stream is not well-used while being very useful.

在流中使用匿名类不是很有用,但是非常有用。

#17


0  

If you are trying to get an index based on a predicate, try this:

如果您试图根据谓词获取索引,请尝试以下方法:

If you only care about the first index:

如果你只关心第一个指标:

OptionalInt index = IntStream.range(0, list.size())
    .filter(i -> list.get(i) == 3)
    .findFirst();

Or if you want to find multiple indexes:

或者如果你想找到多个索引:

IntStream.range(0, list.size())
   .filter(i -> list.get(i) == 3)
   .collect(Collectors.toList());

Add .orElse(-1); in case you want to return a value if it doesn't find it.

添加.orElse(1);如果你想返回一个值,如果它没有找到。