Input Handling

About

In Java, there are several ways to take input from the user. Here are some common methods.

Using Scanner Class

About

The Scanner class is a standard class in Java used for taking input from the user. It's part of the java.util package.

  • Supports reading input from various sources, including:

    • System.in (keyboard/console input)

    • Files (File object)

    • Strings (String object)

  • Provides methods to parse primitive data types (int, double, etc.) and strings.

  • Scanner class in Java is not thread-safe. It is not synchronized, which means that if multiple threads access a Scanner instance concurrently, and at least one thread modifies it (e.g., advances the scanner's position or modifies its delimiter), it can lead to undefined behavior.

How to Use Scanner Safely in Multithreaded Applications?

Synchronize External Access: Wrap the Scanner usage in a synchronized block or method to prevent concurrent access:

synchronized(scanner) {
    String line = scanner.nextLine();
}

Thread-Local Scanner Instances: Use a separate Scanner instance for each thread to avoid shared state:

ThreadLocal<Scanner> threadLocalScanner = ThreadLocal.withInitial(() -> new Scanner(System.in));

Example

Among all the methods mentioned here, using the Scanner class is probably the most commonly used method for taking input from the user in Java, especially for beginner-level programming and simple console-based applications.

Limitations of Scanner

  • Not suitable for reading large files efficiently.

  • Limited control over complex parsing; use libraries like Apache Commons CSV or Jackson for structured data.

Using BufferedReader Class

About

The BufferedReader class, along with InputStreamReader, can be used to read input from the user. This method is useful when dealing with input streams. It's part of the java.io package. It is especially useful when working with large input data as it buffers the data to improve reading performance.

  • Reads data in chunks to minimize I/O operations.

  • Works well for reading text files or console input.

  • Can read data line by line or character by character.

  • Supports marking and resetting, which allows re-reading portions of the input stream.

Example

Best Practices

  1. Always Close Resources: Use a try-with-resources block to automatically close the BufferedReader:

  2. Buffer Size: Use the default buffer size unless specific performance tuning is required.

  3. Avoid Mixing with Scanner: Avoid mixing BufferedReader and Scanner in the same program for reading input to prevent unexpected behavior.

  4. Check readLine() for Null: Always check for null to handle the end of the stream gracefully.

Using Command Line Arguments

We can also pass input arguments directly when running the program from the command line. These arguments can be accessed through the args parameter in the main method.

Using Console Class (Java 6 and later)

The Console class provides methods for reading input and writing output to the console. It is useful for simple console-based applications. It is a java.io package. Note that we need to execute the code via console.

Which one to use?

BufferedReader vs Scanner

Feature

BufferedReader

Scanner

Primary Purpose

Efficiently reads text from a character-based input stream in chunks.

Reads and parses input into tokens or specific data types.

Performance

Faster for reading large files due to internal buffering.

Slower for large files due to token parsing overhead.

Data Type Support

Reads text as String or characters; manual parsing required for primitives.

Built-in support for parsing primitives (int, double, etc.).

Input Sources

Can read from files, strings, or streams.

Can read from files, strings, streams, and system input (System.in).

Default Delimiter

Not applicable; reads lines or characters directly.

Whitespace (\s) by default, customizable via useDelimiter().

Line-by-Line Reading

Supports reading lines via readLine().

Possible but not as intuitive; requires custom logic.

Character-by-Character Reading

Supports reading characters using read().

Not supported directly.

End of Input Handling

Returns null for readLine() and -1 for read().

Uses hasNext() and related methods to check availability.

Parsing Capabilities

Requires manual parsing for primitives (e.g., using Integer.parseInt()).

Provides methods like nextInt(), nextDouble(), etc., for parsing.

Error Handling

Throws IOException for input/output errors.

Throws InputMismatchException for type mismatches.

Mark and Reset

Supports marking and resetting positions in the stream.

Does not support marking and resetting.

Custom Delimiters

Not applicable; processes lines/characters directly.

Supports custom delimiters via useDelimiter().

Thread Safety

Not thread-safe; external synchronization required.

Not thread-safe; external synchronization required.

Complex Input Handling

Best suited for reading raw text or large files efficiently.

Better for simple interactive inputs or structured data with tokens.

Binary File Support

Cannot handle binary files; use BufferedInputStream instead.

Not suitable for binary files.

Resource Management

Requires manual closure or use of try-with-resources.

Requires manual closure or use of try-with-resources.

Use Case

Reading and processing large text files, logs, or configuration files.

Interactive console input or small structured file parsing.

  • Use BufferedReader:

    • When working with large files where performance is critical.

    • When we need to read text line by line or character by character.

    • When parsing is not required, or we prefer manual control over parsing.

  • Use Scanner:

    • For interactive input from the console.

    • When parsing numbers, tokens, or specific data types directly is required.

    • For quick, small-scale parsing of structured input like CSVs or space-separated values.

Last updated