2017-06-02 137 views
1

我想知道是否有可能使用流来调用列表中的每个元素上的函数,并且还可以使用流调用其值以及其值。如何在JAVA的列表中的每个元素上运行一个函数?

比方说,我有一个整数列表: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)的列表。

+1

使用'map'代替 – Li357

+1

变异你通过流遍历集合似乎是一个非常糟糕的主意开始。您可能更适合用更新的值生成新的“List”。 – Mena

+3

流的许多优点之一是它不鼓励变异的数据结构。 – slim

回答

2

您需要使用map单独collectstream不修改现有的列表,如:

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

非常感谢Darshan Mehta! – Engineer

1

可以发生变异的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(见Darshan的回答的第二部分为一个简单的例子)
+0

非常感谢你!我想问一下'i [0] ++'部分,我从来没有见过像这样的索引的使用,你知道我在哪里可以找到有关这种方式的信息,以及为什么正常的int不起作用? – Engineer

+0

@Engineer不用客气。基于数组的索引(而不是简单的'int')发生的事情是,在lambda内部,方法的局部范围内的变量(等等)必须被有效地“最终”引用。但是,显然不能重新指定多个“final int”。但是你可以改变一个int []'。或者一个'AtomicInteger'。 – Mena

+0

@Engineer注意到上述内容更接近“客厅技巧”,而不是一般惯例,我向您展示的解决方案的另一面红旗 - 从不在生产中使用! – Mena

1

虽然有办法新List随时修改输入列表,我们已经学会了避免这样做,因为它会导致问题。

您可以用新的列表替换现有列表(在当前范围),很容易地:

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

这是非常干净的。您的旧名单仍然存在(直到潜在的垃圾收集)。任何其他引用它都没有改变 - 这是健康的。如果没有更多的引用,它是垃圾收集。

+0

非常感谢你苗条!所以你说Streams是为了_not_不得不修改原始对象的值而创建的? – Engineer

+0

好吧,你永远不会*修改原始对象的值。但是流是鼓励你不用的代码。 – slim

2

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

list.replaceAll(MyApplication::plusOne); 
相关问题