hasNext() and hasNextInt()
The methods of scanner will block for more input if there is no current token and the source ('System.in' in this case) hasn't reached end-of-stream yet. This will then result in it waiting for your input.
This is also explicitly mentioned in the documentation of hasNext()
(and the other blocking methods):
This method may block while waiting for input to scan.
This can also be seen in the actual implementation of hasNext()
:
public boolean hasNext() {
ensureOpen();
saveState();
modCount++;
while (!sourceClosed) {
if (hasTokenInBuffer()) {
return revertState(true);
}
readInput();
}
boolean result = hasTokenInBuffer();
return revertState(result);
}
As you can see, it will loop as long as the source isn't closed, and if there is no token in the buffer, it will call readInput()
to find the next token. This read will block if there is currently nothing to read from the source at this time, but it hasn't reached end-of-stream/end-of-file yet.
A method to overcome the blocking Behavior of hasNext()
To overcome the blocking behavior of hasNext()
, we can create our own method to handle the input, like readInput()
shown below. However, to get a line of text as input, simply use nextLine()
instead of readInput()
.
import java.util.Scanner;
public class ChkInput {
static Scanner input = new Scanner(System.in);
// Swallow all input data each time we read input
public static String[] readInput() {
String in = input.nextLine();
String[] data = in.split("[\\s]+");
return data;
}
public static void main(String[] args) throws Exception {
while(true) {
System.out.println("Input a number : ");
String[] data = readInput();
// handle the expected input as nextInt()
int num = Integer.parseInt( data[0] );
System.out.println("Number is " + num);
// If more data are available, do something before next loop
// Here if expecting more data but length == 1, you know data missing.
// so next loop get the data
if( data.length > 1) {
// example: just print them out.
System.out.println("More input data waiting : ");
for(int i = 1; i < data.length; i++) {
System.out.print(data[i] + ", ");
}
System.out.println();
}
}
}
}
Another Way to Handle Input with Better Control
The following is an example of implementing the idea mentioned above into a class named InputBuffer
.
// file: ChkInput.java
import java.util.Scanner;
// InputBuffer : An implementation of using buffer to hold input.
// With better control of hasNext() and flushing (clear data)
//
class InputBuffer {
Scanner input;
String[] buff;
int buffIndex;
// constructor
//----
public InputBuffer(Scanner scannerDev) throws Exception {
if(scannerDev == null) {
throw new Exception("Scanner is not defined");
}
input = scannerDev;
buff = new String[0];
buffIndex = 0;
}
// verify if any data available in buffer.
// if no data just proceed (no blocking).
//----
public boolean hasNext() {
return buffIndex < buff.length;
}
// read data as a token
//----
public String readInput() throws Exception{
if(buffIndex == buff.length) {
String in = input.nextLine().trim();
buff = in.split("[\\s]+");
buffIndex = 0;
}
String data = buff[ buffIndex ];
buffIndex++;
return data;
}
// read data as a line.
// if buffer filled with tokens and a subsequent
// readInputLine() follows a readInput(), all
// tokens rejoined using space (' ')
//----
public String readInputLine() throws Exception {
String data;
if(buffIndex == buff.length) {
data = input.nextLine();
} else {
data = "";
for(int i = buffIndex; i < buff.length; i++ ) {
data = data + buff[i];
if (i < buff.length) {
data = data + " ";
}
}
}
flush();
return data;
}
// clear remaining data in buffer
//----
public void flush() {
buff = new String[0];
buffIndex = 0;
}
// explicitly close the scanner device
//---
public void close() {
input.close();
}
}
// Test program
public class ChkInput {
public static void main(String[] args) throws Exception {
InputBuffer input = new InputBuffer(new Scanner(System.in));
while(true) {
System.out.println("Enter line of text (x to stop): ");
String name = input.readInputLine();
if (name.equals("x")) { break; }
System.out.println("Line = " + name);
System.out.println("Input a number : ");
String data = input.readInput();
// handle the expected input like nextInt();
int num = Integer.parseInt( data );
System.out.println("Number is " + num);
// hasNext() checks if there is another data available, do something before next loop
// if there is no data, return false and proceed to next loop. No blocking (waiting input) by hasNext()
if( input.hasNext()) {
System.out.println( "Get the next data : " + input.readInput() );
}
input.flush(); // clear all remaining data, start fresh
}
}
}