java - 为什么`.hasNext()` 会消耗`BufferedReader` 中的元素?
问题描述
问题陈述
在我在 wsl Ubuntu 上执行的编译.jar
文件中,我运行命令:task reportToGetUrgencyOfAllTasks
它返回一个包含列的任务列表:id, uuid, urgency
。我可以检查列表,它被打印到终端,它包含 1793 个任务和 1798 行(一个标题和一些关于需要同步的不相关消息)。当我用它计算对象的数量时,reader.lines().count()
它会返回 1798 并按预期使用这些行。
如果我创建一个while循环:
long counter = 0;
while (reader.lines().iterator().hasNext()) {
counter++;
System.out.println("counter=" + counter);
}
它列出了数字 1 到 1798,这很奇怪,因为我认为它.hasNext()
没有消耗项目,而只是检查下一个元素是否存在,而不是从流中取出它。(如此处建议:https ://hajsoftutorial.com/iterator-hasnext-and-next/ )。我期望一个无限循环的数字,因为迭代器会在hasNext()
.
那么,如果我想从阅读器中获取所有元素并将它们放入 ArrayList 中:
ArrayList<String> capturedCommandOutput = new ArrayList<String>();
while (reader.lines().iterator().hasNext()) {
counter++;
capturedCommandOutput.add(reader.lines().iterator().next());
}
它跳过每隔一行。
问题
为什么hasNext()
在这种情况下使用迭代器中的元素?
完整代码 对于 MWE,需要在 WSL Ubuntu 上安装 taskwarrior,但是,我认为这是一个 Java 问题,因为我可以看到阅读器确实包含所有行/信息,因此为了完整性:运行命令的完整方法是:
package customSortServer;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Map;
import java.util.StringJoiner;
public class RunCommandsLongOutput2 {
/**
* This executes the commands in terminal. Additionally it sets an environment
* variable (not necessary for your particular solution) Additionally it sets a
* working path (not necessary for your particular solution)
*
* @param commandData
* @param ansYes
* @throws Exception
*/
public static ArrayList<String> executeCommands(Command command, Boolean ansYes) {
ArrayList<String> capturedCommandOutput = new ArrayList<String>();
File workingDirectory = new File(command.getWorkingDirectory());
// create a ProcessBuilder to execute the commands in
ProcessBuilder processBuilder = new ProcessBuilder(command.getCommandLines());
// this is set an environment variable for the command (if needed)
if (command.isSetEnvVar()) {
processBuilder = setEnvironmentVariable(processBuilder, command);
}
// this can be used to set the working directory for the command
if (command.isSetWorkingPath()) {
processBuilder.directory(workingDirectory);
}
// execute the actual commands
try {
Process process = processBuilder.start();
System.out.println("Started");
if (command.isGetOutput()) {
// capture the output stream of the command
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
// System.out.println(reader.lines().count());
// long counter = 0;
while (reader.lines().iterator().hasNext()) {
capturedCommandOutput.add(reader.lines().iterator().next());
}
// while (reader.lines().iterator().hasNext()) {
// counter++;
// System.out.println("line=" + reader.lines().iterator().next());
// }
}
// connect the output of your command to any new input.
// e.g. if you get prompted for `yes`
new Thread(new SyncPipe(process.getErrorStream(), System.err)).start();
new Thread(new SyncPipe(process.getInputStream(), System.out)).start();
PrintWriter stdin = new PrintWriter(process.getOutputStream());
// This is not necessary but can be used to answer yes to being prompted
if (ansYes) {
stdin.println("yes");
}
// write any other commands you want here
stdin.close();
// If the command execution led to an error, returnCode!=0, or not (=0).
int returnCode = process.waitFor();
System.out.println("Return code = " + returnCode);
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// return output if required:
return capturedCommandOutput;
}
/**
* source: https://stackoverflow.com/questions/7369664/using-export-in-java
*
* @param processBuilder
* @param varName
* @param varContent
* @return
*/
private static ProcessBuilder setEnvironmentVariable(ProcessBuilder processBuilder, Command command) {
Map<String, String> env = processBuilder.environment();
env.put(command.getEnvVarName(), command.getEnvVarContent());
processBuilder.environment().put(command.getEnvVarName(), command.getEnvVarContent());
return processBuilder;
}
}
生成命令的方法是:
public static void createCommandToGetUrgencyList(HardCoded hardCoded) {
// create copy command
Command command = new Command();
String[] commandLines = new String[2];
commandLines[0] = "task";
commandLines[1] = hardCoded.getGetUrgencyReportName();
command.setCommandLines(commandLines);
command.setEnvVarContent("/var/taskd");
command.setEnvVarName("TASKDDATA");
command.setWorkingPath("/usr/share/taskd/pki");
command.setGetOutput(true);
// execute command to copy file
ArrayList<String> urgencyList = RunCommandsLongOutput2.executeCommands(command, false);
System.out.println("The urgency list has length = "+urgencyList.size());
for (int i = 0; i < urgencyList.size(); i++) {
System.out.println("The output of the command =" + urgencyList.get(i));
}
}
解决方案
您创建多个Iterator
s - 在循环的条件中创建一个,然后在循环的每次迭代中再创建一个。
它应该是:
Iterator<String> iter = reader.lines().iterator();
long counter = 0;
while (iter.hasNext()) {
counter++;
System.out.println("counter=" + counter);
capturedCommandOutput.add(iter.next());
}
由于每一个Iterator
都是从不同的Stream<String>
(由 返回lines()
)生成的,因此可以iterator()
在每个 s 上调用终端操作 ( ) ,Stream
但是当您调用s的方法Iterator
(hashNext()
或数据 - .next()
Iterator
BufferedReader
正如 Javadoclines()
所说:
终端流操作执行过程中不得对阅读器进行操作。否则,终端流操作的结果是不确定的。
iterator()
是一个终端操作,只要你迭代Iterator
它返回的,你仍然没有完成终端操作。因此,在完成Iterator
. 第二次调用lines()
算作对阅读器的操作。
推荐阅读
- c# - 在第一个 winform 页面中设置标签文本以显示在第二个表单页面上
- python - cwd 被添加到路径
- javascript - setState 不适用于多个 onClick 函数
- javascript - 需要使用带有 try、catch 和 throw 异常的 javascript。我想用它来检查一个文本框以确保它是一个数字而不是一个字母
- java - 试图创建一个计时器,但增量方法遇到了一些问题
- python - Python解压缩文件排除特定文件
- php - 如何使用运行 Xdebug 的 PHPUnit 加快测试速度
- vue.js - 如何找到 vue2-google-maps 自动完成结果的索引?
- javascript - 为什么我的计算器程序不起作用?JavaScript 和 HTML 相关
- python - RuntimeWarning:Numpy 遇到溢出