2014-07-11 183 views
12

我试图找出如何从一个lambda表达式返回的方法值内返回从方法的值:lambda表达式

public int findMissingNumber(Collection<Integer> ints) { 
    Single<Integer> start = new Single<>(1); 
    ints.stream().mapToInt(Integer::valueOf).parallel().forEach(i -> { 
     if (i != start.setValue(start.getValue() + 1)) { 
      //return here 
     } 
    }); 
    return -1; 
} 

然而,似乎使用return关键字在lambda表达式将显式返回到lambda函数本身。是否有某种方法可以打破或强制整个方法的回报?

+1

你想返回什么?如果您要返回满足某些条件的流的成员,请使用过滤器。 – Eran

回答

15

是否有某种方式来打破或强制返回整个方法?

不,至少,除非你抛出异常。

基本上,这不是forEach的意思。你可以写一个方法,它接受一个函数返回null为“继续”和非为“停止,并做出结果”...但该方法不是forEach

您使用lambda表达式的事实在这里确实很偶然。想象一下,你只是打电话forEach,并传递一些参数 - 不是真的奇怪,如果这个调用使得你的findMissingNumber方法返回(没有例外),没有findMissingNumber方法本身具有返回语句?

+0

我想我使用'forEach'和流开始的主要原因是使用'parallel'(因为这是通过一个大集合)。你将能够扩展如何做一个方法/函数来通过像这样的流? – Rogue

+4

@Rogue:那么,你可能很有可能用'filter()'和'findFirst()'来构建它 - 让你的'filter'谓词只返回“匹配”的项目,然后找到那个流的第一个元素。另一方面,你的lambda表达式目前有一个副作用('start.setValue()'),这不是一个很好的并行起点... –

+0

这似乎使它足够干净,尽管我认为它有点儿令人沮丧的是,没有办法通过第一次发现来停止(抛出一个异常似乎也没有逃脱lambda)。谢谢! – Rogue

11

(是的XY problem的这一个实例?)

的问题是如何从一个lambda返回forEach内。 Jon Skeet提供了一些关于findFirst的有用信息,同时也告诫有关并行lambda运行中的副作用 - 两个优点。

但是关于原来的问题,我还在想:你想要解决什么问题?

该示例中的方法名称findMissingNumber是暗示性的。该方法将一组数字作为参数,并在递增计数器的同时对其进行迭代。它在发现不匹配时返回,或者如果没有不匹配,则返回-1。由于计数器会在处理ints集合中的每个值时递增一次,因此该集合似乎是按顺序排列的,除非缺少一个数字。

如果是这样,参数应该是List而不是Collection。 (在这里犯了一个大的假设),在此假设下,一个可以重写使用lambda表达式中的代码,像这样流:

static OptionalInt findMissingNumber(List<Integer> ints) { 
    return 
     IntStream.rangeClosed(1, ints.size()) 
      .filter(i -> i != ints.get(i-1)) 
      .findFirst(); 
} 

反而增加计数器的,我们用IntStream.range产生预计将在列表中的值。然后,我们依靠随机访问列表中的get值,从列表中的预期位置开始。我们筛选不匹配并返回第一个,如果有的话。这避免了突变,所以应该并行正确运行。 (请注意,这不很好并行如果列表不是随机存取,虽然)。

返回值是一个OptionalInt如果没有匹配被发现。这比使用诸如-1的标记值指示“未找到”条件更明确。