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

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

有更简洁的方式吗?

此外,似乎拉链已经移动或被拆除…


当前回答

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

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

当然,正如您在问题中暗示的那样,您总是可以将Stream<V>转换为Collection<V>,例如List<V>,在其中您将可以访问索引。

其他回答

正如jean-baptiste-yunès所说,如果您的流是基于java列表的,那么使用AtomicInteger及其incrementAndGet方法是一个很好的解决问题的方法,并且返回的整数与原始列表中的索引对应,只要您不使用并行流。

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

然而,经常会有变通办法。通常,这可以通过使用整数范围“驱动”流来实现,并利用原始元素通常位于一个数组或一个可以通过索引访问的集合中这一事实。例如,挑战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的目的。尽管如此,它还是相当有效地解决了这个问题。

EDIT

我前面的代码示例使用flatMap来融合过滤器和映射操作,但这很麻烦,而且没有任何好处。我已经根据Holger的评论更新了示例。

如果你试图获得一个基于谓词的索引,试试这个:

如果你只关心第一个索引:

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

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

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

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

除了protonpack之外,jOOλ的Seq也提供了这种功能(通过在其上构建的扩展库,如cyclops-react,我是这个库的作者)。

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

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

这个问题(流方式获取第一个元素匹配布尔值的索引)已将当前问题标记为重复,所以我无法回答它;我在这里回答。

下面是获得匹配索引的通用解决方案,不需要外部库。

如果你有一个清单。

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

像这样叫它:

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

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

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