Java 8中是否有一种简明的方式在流中迭代索引?

517

有没有一种简洁的方法在迭代流时访问流中的索引?

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

有更简洁的方法吗?

另外,似乎该zip文件已经移动或被删除...


3
intRange() 是什么?我在 Java 8 中还没有遇到过这个方法。 (翻译者注:原文中的 "accross" 应为 "across",已进行修正。) - Rohit Jain
@RohitJain 可能是 IntStream.rangeClosed(x, y) - assylias
2
作为旁注,我认为使用List<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());更好地完成挑战4。 - assylias
4
是的,zip 已经被移除了,还有实验性质的双值流,也称为 BiStreamMapStream。主要问题在于,为了有效地执行此操作,Java 确实需要一个结构类型的二元组(或元组)类型。由于缺乏这样一个类型,很容易创建一个通用的 Pair 或 Tuple 类 - 这已经做过很多次了 - 但它们都会被擦除到相同的类型。 - Stuart Marks
4
通用的Pair或Tuple类的另一个问题是,它需要将所有的基本类型进行装箱。 - Stuart Marks
显示剩余2条评论
26个回答

0
如果列表是唯一的,我们可以利用 indexOf 方法。
List<String> names = Arrays.asList("Sam", "Pamela", "Dave", "Pascal", "Erik");

    names.forEach(name ->{
        System.out.println((names.indexOf(name) + 1) + ": " + name);
    });

2
不建议这样做,它的运行时间复杂度为O(N^2) - OneCricketeer

0
正如jean-baptiste-yunès所说,如果您的流基于Java List,则使用AtomicInteger及其incrementAndGet方法是解决问题的非常好的方法,并且返回的整数与原始List中的索引相对应,只要您不使用并行流即可。

0

以下是标准Java的解决方案:

一行解决方案:

Arrays.stream("zero,one,two,three,four".split(","))
        .map(new Function<String, Map.Entry<Integer, String>>() {
            int index;

            @Override
            public Map.Entry<Integer, String> apply(String s) {
                return Map.entry(index++, s);
            }
        })
        .forEach(System.out::println);

并且使用实用方法可以获得更可读的解决方案:

static <T> Function<T, Map.Entry<Integer, T>> mapWithIntIndex() {
    return new Function<T, Map.Entry<Integer, T>>() {
        int index;

        @Override
        public Map.Entry<Integer, T> apply(T t) {
            return Map.entry(index++, t);
        }
    };
}

...
Arrays.stream("zero,one,two,three,four".split(","))
        .map(mapWithIntIndex())
        .forEach(System.out::println);

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

输出:Sam,Pamela,Dave,Pascal,Erik

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

以上问题的解决方案预计是一个包含一个元素 ErikList - Kaplan
我已经添加了一个示例来收集列表中的数据。 - Arpan Saini

0
这个问题(流式获取第一个布尔值匹配元素索引)被标记为重复,所以我不能在那里回答它; 我在这里回答。

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

如果您有一个列表。

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

0
有没有一种简洁的方法可以在迭代流时访问流中的索引?
如果您将“迭代”视为终端操作(例如forEach),那么是的,有一种简单的方法可以访问索引:
Stream<String> names = Stream.of("Sam","Pamela", "Dave", "Pascal", "Erik");
List<String> filtered = new ArrayList<>();

names.reduce(0L, (index, item) -> {
    log.info("Iteration over items [index: {}, item: {}]", index, item);
    if (item.length() < index) {
        filtered.add(item);
    }
    return index + 1;
}, Long::sum);

这仅适用于顺序流。

并行流必须首先转换为顺序流:

names.sequential().reduce(0L, (index, item) -> {
    log.info("Iteration over items [index: {}, item: {}]", index, item);
    if (item.length() < index) {
        filtered.add(item);
    }
    return index + 1;
}, Long::sum);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接