【问题标题】:How to run a function on each element in a list in JAVA?如何在 JAVA 列表中的每个元素上运行函数?
【发布时间】:2017-11-04 01:09:00
【问题描述】:

我想知道是否可以使用流来调用列表中每个元素的函数并修改它的值。

假设我有一个整数列表:List<Integer> list = Arrays.asList(1,2,3);

还有一种将每个数字加一的方法:

public static Integer plusOne(Integer n){
    return n + 1;
}

这是我用来在每个元素上调用函数并修改原始列表的:

for (int i = 0; i < list.size(); i++){
    list.set(i, plusOne(list.get(i)));
}

但是,我想知道是否有更短的方法可以使用流来编写它?

我尝试使用:

list.stream()
    .forEach(e -> plusOne(e));

但它似乎并没有改变原始列表,也没有返回任何东西......如果使用流无法更改原始列表,我怎样才能至少获得列表的新修改副本?

顺便说一下,在调用列表上的函数后,输出应该是(2,3,4)的列表。

【问题讨论】:

  • 改用map
  • 改变你在流中迭代的集合似乎是一个非常糟糕的主意。您最好使用更新后的值生成一个新的List
  • 流的众多好处之一是它不鼓励改变数据结构。

标签: java arrays list functional-programming java-stream


【解决方案1】:

不需要流。你可以使用List.replaceAll:

list.replaceAll(MyApplication::plusOne);

【讨论】:

    【解决方案2】:

    您需要将mapcollect 一起使用,因为单独使用stream 不会修改现有列表,例如:

    public static int plusOne(int i){
        return i + 1;
    }
    
    public static void main(String[] args) throws Exception{
        List<Integer> list = Arrays.asList(1,2,3);
        List<Integer> updated = list.stream()
                .map(i -> plusOne(i))
                .collect(Collectors.toList());
        System.out.println(updated);
    }
    

    甚至,

    List<Integer> updated = list.stream()
                .map(i -> i + 1)
                .collect(Collectors.toList());
        System.out.println(updated);
    

    【讨论】:

    • 非常感谢 Darshan Mehta!
    【解决方案3】:

    您可以在通过以下习语进行流式传输时改变您的List

    请注意,这是绝对可怕的代码,完全构成可怕的做法

    仅用于实验。

    List<Integer> list = Arrays.asList(1,2,3);
    // cannot have a re-assignable index here as it requires being effectively final
    int[] i = {0};
    // iterating list
    list.forEach(
        // for each element...
        (e) -> 
        // setting list at current index and incrementing index, with current value + 1
        list.set(i[0]++, e + 1)
    );
    System.out.println(list);
    

    输出

    [2, 3, 4]
    

    备注

    • 这将适用于上述习惯用法,因为没有对流进行并行处理,并且索引数组容器可以预见地发生变化。
    • 推荐的做法是通过流式传输生成新的List,将值映射到它们的增量并收集为List(请参阅Darshan 的答案的第二部分以获取一个简单的示例)

    【讨论】:

    • 非常感谢梅纳!我想问一下i[0]++ 部分,我以前从未见过这样的索引用法,你知道我在哪里可以找到有关这种方式的信息以及为什么普通的 int 不起作用?
    • @Engineer,不客气。基于数组的索引(与简单的int 相对)发生的情况是,在 lambda 中,来自方法本地范围的变量(以及其他)必须有效地被引用为 final。但是,您显然不能多次重新分配final int。但是你可以改变一个int[]。或AtomicInteger
    • @Engineer 请注意,以上内容更接近于“客厅技巧”而不是常见做法,这是我向您展示的解决方案的另一个危险信号 - 切勿在生产中使用它!
    【解决方案4】:

    虽然有一些方法可以随时修改输入列表,但我们已经学会避免这样做,因为它会导致问题。

    您可以很容易地将现有列表(在当前范围内)替换为新列表:

     list = list.stream().map(...).collect(Collectors.toList());
    

    这很好,很干净。您的旧列表仍然存在(直到潜在的垃圾收集)。任何其他对它的引用都没有变化——这是健康的。如果没有更多对其的引用,则将其作为垃圾回收。

    【讨论】:

    • 非常感谢 Slim!所以你是说创建 Streams 的目的是不必修改原始对象的值?
    • 好吧,你从来没有不得不修改原始对象的值。但是流会鼓励你不喜欢的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 2020-12-10
    • 2022-01-11
    • 2011-03-23
    • 1970-01-01
    • 2021-04-09
    相关资源
    最近更新 更多