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

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

有更简洁的方式吗?

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


当前回答

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的评论更新了示例。

其他回答

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

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

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

String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
String completeString
         =  IntStream.range(0,namesArray.length)
           .mapToObj(i -> namesArray[i]) // Converting each array element into Object
           .map(String::valueOf) // Converting object to String again
           .collect(Collectors.joining(",")); // getting a Concat String of all values
        System.out.println(completeString);

山姆,帕梅拉,戴夫,帕斯卡,埃里克

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

IntStream.range(0,namesArray.length)
               .mapToObj(i -> namesArray[i]) // Converting each array element into Object
               .map(String::valueOf) // Converting object to String again
               .forEach(s -> {
                //You can do various operation on each element here
                System.out.println(s);
               }); // getting a Concat String of all 

收集清单:

String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
 List<String> namesList
                =  IntStream.range(0,namesArray.length)
                .mapToObj(i -> namesArray[i]) // Converting each array element into Object
                .map(String::valueOf) // Converting object to String again
                .collect(Collectors.toList()); // collecting elements in List
        System.out.println(listWithIndex);

你不一定需要地图 这是最接近LINQ示例的lambda:

int[] idx = new int[] { 0 };
Stream.of(names)
    .filter(name -> name.length() <= idx[0]++)
    .collect(Collectors.toList());

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的评论更新了示例。

你可以使用IntStream.iterate()来获取索引:

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

这只适用于Java 9以上的Java 8,你可以使用这个:

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