How do I use Files.mismatch() to compare files?

In Java, java.nio.file.Files.mismatch(Path, Path) is a powerful method introduced in Java 12 that allows you to compare the contents of two files efficiently. It returns the position of the first byte where the two files differ, or -1L if they are identical.

How to use Files.mismatch

Here is a basic example of how to implement it:

package org.kodejava.nio;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class FileCompare {
    public static void main(String[] args) {
        Path path1 = Path.of("file1.txt");
        Path path2 = Path.of("file2.txt");

        try {
            long mismatch = Files.mismatch(path1, path2);

            if (mismatch == -1L) {
                System.out.println("Files are identical.");
            } else {
                System.out.println("Files differ at byte position: " + mismatch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Key Behaviors to Keep in Mind:

  1. Return Values:
    • -1L: The files are identical (same size and same content).
    • A non-negative value: The index of the first byte that differs.
    • File Size Mismatch: If one file is a prefix of the other, it returns the size of the smaller file as the mismatch point.
  2. Performance: Files.mismatch is generally faster than manual byte-by-byte comparison because it uses optimized internal buffers.
  3. Same Path: If you pass the exact same Path object (or two paths that point to the same file via Files.isSameFile), it returns -1L immediately without reading the content.
  4. Exceptions: It throws an IOException if there’s an error reading the files or if one of the paths does not exist.

How do I use String.indent() to format output text?

The String.indent(int n) method in Java is a useful tool for formatting multi-line strings by adjusting the amount of leading space (indentation) on each line. Introduced in Java 12, it performs the following for the specified n indentation:

  1. Positive n: Adds n spaces to the beginning of each line in the string.
  2. Negative n: Removes up to n leading spaces from each line.
  3. Zero n: Leaves the string unchanged (but trims blank lines at the start/end).

This is particularly useful for formatting text in structured views, like logs, JSON, XML, or pretty-printed output.

Example: Using String.indent to Format Output

Here is how you can use the String.indent() method:

package org.kodejava.lang;

public class StringIndentExample {
    public static void main(String[] args) {
        // Original multi-line string (no indentation)
        String text = "Line 1\nLine 2\nLine 3";

        // Adding an indent of 4 spaces
        String indented = text.indent(4);
        System.out.println("Indented by 4 spaces:\n" + indented);

        // Removing 2 leading spaces (negative indent)
        String negativeIndent = indented.indent(-2);
        System.out.println("Indented with -2 (removing spaces):\n" + negativeIndent);

        // Applying indent to empty lines
        String withEmptyLines = "Line 1\n\nLine 3";
        String indentedWithEmptyLines = withEmptyLines.indent(4);
        System.out.println("Handling empty lines:\n" + indentedWithEmptyLines);
    }
}

Explanation of Code:

  1. Adding Indentation: The first call to .indent(4) adds 4 spaces to each line.
  2. Removing Indentation: .indent(-2) deducts 2 spaces from the start of each line (if spaces exist).
  3. Empty Lines: When dealing with blank lines, indent maintains the level of indentation for such lines, adding or removing spaces as needed.

Output:

Indented by 4 spaces:
    Line 1
    Line 2
    Line 3

Indented with -2 (removing spaces):
  Line 1
  Line 2
  Line 3

Handling empty lines:
    Line 1

    Line 3

Notes:

  • Empty lines and their indentation are preserved.
  • Lines with no leading spaces are unaffected by negative indentation.
  • Leading and trailing full blank lines are removed.

When to Use:

  • To format multiline strings cleanly.
  • To produce human-readable or formatted output in tools or commands (e.g., logging, text processing).

How do I use Collectors::teeing in Streams?

In Java, Collectors::teeing is a feature introduced in Java 12 that allows you to collect elements of a stream using two different collectors and then combine the results using a BiFunction.

Syntax:

static <T, R1, R2, R> Collector<T, ?, R> teeing(Collector<? super T, ?, R1> downstream1,
                                                Collector<? super T, ?, R2> downstream2,
                                                BiFunction<? super R1, ? super R2, R> merger)

Concept:

  1. downstream1: The first collector for processing input elements.
  2. downstream2: The second collector for processing input elements.
  3. merger: A BiFunction that merges the results of the two collectors.

The teeing collector is useful when you want to process a stream in two different ways simultaneously and combine the results.


Example 1: Calculate the Average and Sum of a Stream

Here’s how you can calculate both the sum and average of a list of integers simultaneously:

package org.kodejava.util.stream;

import java.util.List;
import java.util.stream.Collectors;

public class TeeingExample {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5);

        var result = numbers.stream()
                .collect(Collectors.teeing(
                        Collectors.summingInt(i -> i),             // Collector 1: Sum
                        Collectors.averagingInt(i -> i),           // Collector 2: Average
                        (sum, avg) -> "Sum: " + sum + ", Avg: " + avg // Merger
                ));

        System.out.println(result); // Output: Sum: 15, Avg: 3.0
    }
}

Example 2: Get Statistics (Min and Max) from a Stream

You can create a single step operation to compute the minimum and maximum values of a stream:

package org.kodejava.util.stream;

import java.util.List;
import java.util.stream.Collectors;

public class TeeingStatsExample {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(3, 5, 7, 2, 8);

        var stats = numbers.stream()
                .collect(Collectors.teeing(
                        Collectors.minBy(Integer::compareTo),     // Collector 1: Get Min
                        Collectors.maxBy(Integer::compareTo),     // Collector 2: Get Max
                        (min, max) -> new int[]{min.orElse(-1), max.orElse(-1)} // Merge into an array
                ));

        System.out.println("Min: " + stats[0] + ", Max: " + stats[1]);
        // Output: Min: 2, Max: 8
    }
}

How It Works:

  • Two collectors (downstream1 and downstream2) collect the stream elements independently. For example, the first collector might compute the sum, while the second computes the average.
  • Once the stream has been fully processed, the results from both collectors are passed to the merger, which applies a transformation or combination of the two results.

Example 3: Concatenate Strings and Count Elements Simultaneously

Here’s how you can process a stream of strings to count the number of elements and also concatenate them into a single string:

package org.kodejava.util.stream;

import java.util.List;
import java.util.stream.Collectors;

public class TeeingStringExample {
    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob", "Charlie");

        var result = names.stream()
                .collect(Collectors.teeing(
                        Collectors.joining(", "),        // Collector 1: Concatenate strings
                        Collectors.counting(),           // Collector 2: Count elements
                        (joined, count) -> joined + " (Total: " + count + ")" // Merge
                ));

        System.out.println(result);  // Output: Alice, Bob, Charlie (Total: 3)
    }
}

Key Points:

  1. Stream Processing: The stream elements are processed only once but collected using two different collectors.
  2. Merger Function: The merger combines both results into a final result of your choice.
  3. Utility: Collectors::teeing is very useful when you need to perform dual aggregations in one pass over the data.

Now you’re ready to use Collectors::teeing for combining results in your streams!