Java 为什么“.hasNext()”使用“BufferedReader”中的元素?

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

问题陈述

在我在wsl Ubuntu上执行的一个编译的.jar文件中,我运行命令:task reportToGetUrgencyOfAllTasks,它返回一个包含以下列的任务列表:id、uuid、紧迫性。我可以检查列表,它被打印到终端,它由1793个任务和1798行组成,一个标题和一些关于需要同步的无关消息。。当我使用reader.lines.count计算对象数时,它返回1798并按预期使用这些行

如果我使用以下内容创建while循环:

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中的行表示-在执行终端流操作期间,不得对读取器进行操作。否则,终端流操作的结果是未定义的。