使用Java 8中断/停止forEachRemaining

时间:2014-04-11 10:11:38

标签: java iterator mahout java-8

请考虑以下代码段:

private List<User> getUsers() throws TasteException {
        final int MAX_USERS = 100;
        List<User> userList = new ArrayList<>(MAX_USERS);
        dataModel.getUserIDs().forEachRemaining(userId -> {
            if (userList.size() == 100) {
                // stop
            }
            userList.add(new User(userId));
        });
        return userList;
    }

breakreturn无效。我该怎么办?

5 个答案:

答案 0 :(得分:5)

提前停止迭代的唯一方法是抛出异常。不建议使用控制流的异常,所以我会使用Stream.limit,.map和.collect:

private List<User> getUsers() throws TasteException {
  final int MAX_USERS = 100;
  return dataModel.getUserIDs()
                  .stream()
                  .limit(MAX_USERS)
                  .map(userId -> new User(userId))
                  .collect(Collectors.toList());
}

如果无法更改getUserIDs以返回Collection,您可以先转换为Spliterator:

private List<User> getUsers() throws TasteException {
  final int MAX_USERS = 10;
  return StreamSupport.stream(Spliterators.spliteratorUnknownSize(dataModel.getUserIDs(), 0), false)
                      .limit(MAX_USERS)
                      .map(userId -> new User(userId))
                      .collect(Collectors.toList());
}

答案 1 :(得分:2)

考虑正确使用流,似乎你想要:

dataModel.getUserIDs().stream()
    .limit(100)
    .forEach(userId -> userList.add(new User(userId)));

这将获得前100个项目的流并对它们执行操作。由于我不知道dataModel.getUserIDs()的签名,我无法给出更详细的答案。

答案 2 :(得分:2)

在进行任何密集操作之前,您可以'模拟'break;在foreach lambda主体的开头添加一个if进行布尔检查

请注意,我使用final Map<Boolean>来保存boolean标志found,因为这是在lambda外声明一个布尔'变量'的方法(你知道,它必须是' final')但是能够在循环中设置它的值

boolean containsSecret(Iterable<String> values) {
    final Map<String, Boolean> scopedLambdaBooleans = new HashMap<String, Boolean>();
    scopedLambdaBooleans.put("found", false);
    values.forEach(s -> {
        if (scopedLambdaBooleans.get("found")) {
            return; //just the overhead of a boolean if but a continue before any iteration is very near to the break; performance
        }
        //Logger.getAnonymousLogger().info(s);
        if (secret.equals(s)) {
            scopedLambdaBooleans.put("found", true);
        }
    });
    return scopedLambdaBooleans.get("found");
}

开销只是在任何迭代开始时的布尔if检查

如果您因添加if check而感到内疚,您可以补偿摆脱内部if

boolean containsSecretNoInternalIf(Iterable<String> values) {
    final Map<String, Boolean> scopedLambdaBooleans = new HashMap<String, Boolean>();
    scopedLambdaBooleans.put("found", false);
    values.forEach(s -> {
        if (scopedLambdaBooleans.get("found")) {
            return; //just the overhead of a boolean if but a continue before any iteration is very near to the break; performance
        }
        Logger.getAnonymousLogger().info(s);
        scopedLambdaBooleans.put("found", secret.equals(s));

    });
    return scopedLambdaBooleans.get("found");
}

无论如何,在发现之前的任何迭代中都在内存中写入一个布尔“位”,我不知道是否真的比'if'更好(如果在使用一点内存的.equals上检查'执行自己或直接cpu寄存器?嗯...在jvm上我们应该认为它像'堆栈与堆'并且是的,我认为现代jvm与JIT编译器堆栈有优化直接使用cpu寄存器)

答案 3 :(得分:1)

控制流程(中断,提前返回) - 在上面的forEach示例中,通过放置“return”可以实现传统的继续操作。 lambda中的陈述。但是,没有办法打破循环或从lambda中返回包含该方法的结果的值。例如:

final String secret = "foo";
boolean containsSecret(Iterable<String> values) {
    values.forEach(s -> {
         if (secret.equals(s)) {
            ??? // want to end the loop and return true, but can't
        }
    });
}

点击此处查看更多链接:http://www.techempower.com/blog/2013/03/26/everything-about-java-8/

答案 4 :(得分:0)

为了从Scanner实例的forEachRemaining中中断/退出,请在满足退出条件后如下关闭控制台输入。

scanner.forEachRemaining(s -> {
    if(s.equals("exit")) {
         try {
             System.in.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
     parkingLot.execute(s.trim().split(" "));
 });