Java 为什么“.hasNext()”使用“BufferedReader”中的元素?
问题陈述 在我在wsl Ubuntu上执行的一个编译的.jar文件中,我运行命令:task reportToGetUrgencyOfAllTasks,它返回一个包含以下列的任务列表:id、uuid、紧迫性。我可以检查列表,它被打印到终端,它由1793个任务和1798行组成,一个标题和一些关于需要同步的无关消息。。当我使用reader.lines.count计算对象数时,它返回1798并按预期使用这些行 如果我使用以下内容创建while循环:Java 为什么“.hasNext()”使用“BufferedReader”中的元素?,java,iterator,next,Java,Iterator,Next,问题陈述 在我在wsl Ubuntu上执行的一个编译的.jar文件中,我运行命令:task reportToGetUrgencyOfAllTasks,它返回一个包含以下列的任务列表:id、uuid、紧迫性。我可以检查列表,它被打印到终端,它由1793个任务和1798行组成,一个标题和一些关于需要同步的无关消息。。当我使用reader.lines.count计算对象数时,它返回1798并按预期使用这些行 如果我使用以下内容创建while循环: long counter = 0; while (re
long counter = 0;
while (reader.lines().iterator().hasNext()) {
counter++;
System.out.println("counter=" + counter);
}
它列出了数字1到1798,这很奇怪,因为我认为.hasNext没有使用项目,而是只检查下一个元素是否存在,而没有从流中获取它。如此处所示:。我期望有一个无限的数字循环,因为迭代器将永远停留在第一个元素,而不是下一个元素
因此,如果我想从阅读器中获取所有元素,并将它们放入ArrayList,其中包含:
ArrayList<String> capturedCommandOutput = new ArrayList<String>();
while (reader.lines().iterator().hasNext()) {
counter++;
capturedCommandOutput.add(reader.lines().iterator().next());
}
生成命令的方法是:
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<String> iter = reader.lines().iterator();
long counter = 0;
while (iter.hasNext()) {
counter++;
System.out.println("counter=" + counter);
capturedCommandOutput.add(iter.next());
}
由于每个迭代器都是从行返回的不同流生成的,因此可以对每个流调用终端操作迭代器,但是当您调用迭代器的方法hashNext或next时,迭代器会使用来自其单一数据源的数据—BufferedReader
正如Javadoc的行所说:
在执行终端流操作期间,不得对读卡器进行操作。否则,终端流操作的结果是未定义的
迭代器是一个终端操作,只要您对它返回的迭代器进行迭代,您仍然没有完成终端操作。因此,在完成迭代器之前,不应该对读取器进行操作。第二次调用行视为在读卡器上操作。您创建了多个迭代器-一个在循环的条件下,然后在循环的每个迭代中再创建一个迭代器
应该是:
Iterator<String> iter = reader.lines().iterator();
long counter = 0;
while (iter.hasNext()) {
counter++;
System.out.println("counter=" + counter);
capturedCommandOutput.add(iter.next());
}
由于每个迭代器都是从行返回的不同流生成的,因此可以对每个流调用终端操作迭代器,但是当您调用迭代器的方法hashNext或next时,迭代器会使用来自其单一数据源的数据—BufferedReader
正如Javadoc的行所说:
在执行终端流操作期间,不得对读卡器进行操作。否则,终端流操作的结果是未定义的
迭代器是一个终端操作,只要您对它返回的迭代器进行迭代,您仍然没有完成终端操作。因此,在完成迭代器之前,不应该对读取器进行操作。第二次调用行被视为在读卡器上操作。您正在一次又一次地从流中创建流和迭代器 每次调用reader.lines都会创建一个新的流,这是必须的,因为流是不可重用的 在流上调用迭代器是一种终端操作 因此,您要做的是在读取器中的其余元素上创建一个流,然后在其上创建一个迭代器 迭代器契约并没有说它不会使用流中的任何元素。它说的是它不使用迭代器本身的任何元素。也就是说,如果您使用
Iterator<String> iter = reader.lines().iterator();
如果你调用iter.hasNext,你可以期望读卡器中可用的第一行是你从iter读取的元素,而不是从同一个读卡器上的任何其他迭代器读取的元素
实现这一点的一种方法是在第一次调用hasNext时从流中读取一个元素,并使其保持缓冲状态。一旦调用next,它将为您提供缓冲元素。这维护了迭代器契约,但它仍然从读取器中读取一行
现在,如果您创建另一个流和另一个迭代器,它将只使用下一行,依此类推
您应该在读卡器上只调用一次行,并且在生成的流上只调用一次迭代器-或者您应该使用保守的方法使用readLine,直到返回null。您正在一次又一次地从流创建流和迭代器 每次调用reader.lines都会创建一个新的流,这是必须的,因为流是不可重用的 在流上调用迭代器是一种终端操作 因此,您要做的是在读取器中的其余元素上创建一个流,然后在其上创建一个迭代器 迭代器契约并没有说它不会使用流中的任何元素。它说的是它不使用迭代器本身的任何元素。也就是说,如果您使用
Iterator<String> iter = reader.lines().iterator();
如果你调用iter.hasNext,你总是可以期望读卡器中可用的第一行是元素y
你从国际热核聚变实验堆(iter)中读取,而不是从同一读取器上的任何其他迭代器中读取
实现这一点的一种方法是在第一次调用hasNext时从流中读取一个元素,并使其保持缓冲状态。一旦调用next,它将为您提供缓冲元素。这维护了迭代器契约,但它仍然从读取器中读取一行
现在,如果您创建另一个流和另一个迭代器,它将只使用下一行,依此类推
你应该在读卡器上只调用一次线路,您应该只对结果流调用一次迭代器-或者您应该使用保守的方法使用readLine,直到返回null。您可能希望在while条件和循环体中使用相同的迭代器。您可能希望在while条件和循环体中使用相同的迭代器。谢谢,为了正确理解它,reader.lines.iterator创建了一个迭代器对象,由于我使用了该行两次,它创建了该迭代器对象两次。我明白这就是问题所在,但我不明白这是如何导致读者对元素的双重消费的。我假设reader.lines.iterator.hasNext至少将读取器中的第一个元素带入新的迭代器对象,这会减少读取器中的元素,然后当我在while循环中创建新的迭代器对象时,它再次从reader?@a.t.获取元素,因为reader.lines返回一个流,迭代器是该流上的终端操作。由于通过reader.lines.iterator创建的每个迭代器都使用来自同一BufferedReader源的数据,因此,对不同迭代器的交替调用,无论是调用next还是hasNext,都会使用来自读取器的一行数据。注意Javadoc中的行表示-在执行终端流操作期间,不得对读取器进行操作。否则,终端流操作的结果是未定义的。谢谢,为了正确理解它,reader.lines.iterator创建了一个迭代器对象,由于我使用了该行两次,它创建了该迭代器对象两次。我明白这就是问题所在,但我不明白这是如何导致读者对元素的双重消费的。我假设reader.lines.iterator.hasNext至少将读取器中的第一个元素带入新的迭代器对象,这会减少读取器中的元素,然后当我在while循环中创建新的迭代器对象时,它再次从reader?@a.t.获取元素,因为reader.lines返回一个流,迭代器是该流上的终端操作。由于通过reader.lines.iterator创建的每个迭代器都使用来自同一BufferedReader源的数据,因此,对不同迭代器的交替调用,无论是调用next还是hasNext,都会使用来自读取器的一行数据。注意Javadoc中的行表示-在执行终端流操作期间,不得对读取器进行操作。否则,终端流操作的结果是未定义的。