john smith john smith - 7 months ago 21
Java Question

Order letters in ascending order read in from a text file in Java

I have had help with this code, full credit to the creator

I'm trying to make a Java class that displays how many times a letter appears within a text file and display them in ascending order so if the text file contains

Hello World
, the output would be

D is shown 1 time
E is shown 1 time
H is shown 1 time
L is shown 3 times
O is shown 2 times
R is shown 1 time
W is shown 1 time


However, with the code that I have, it does display the letters and how many times they get displayed, but instead of them being grouped together like shown above, it gets displayed as if:

E is shown 1 time
H is shown 1 time
L is shown 2 times
O is shown 1 time
W is shown 1 time
L is shown 1 time
O is shown 1 time
R is shown 1 time
D is shown 1 time


The code (which is not mine as stated at the beginning) which I'm looking to modify so that its output is similar to the first example is

public class class1{

public static void main(String[] args) throws IOException {
try (Scanner in = new Scanner(new FileReader("example.txt"))) {
while (in.hasNext()) {
letterFrequency(in.next());
}
}
}

private static void letterFrequency(String input) {
Map<Character, Integer> counts = new HashMap<>();
for (char each : input.toCharArray()) {
if (Character.isLetter(each)) {
char upper = Character.toUpperCase(each);
counts.put(upper, counts.getOrDefault(upper, 0) + 1);
}
}
counts.entrySet().stream().forEach((entry) -> {
System.out.println(String.format(String.format("%s is shown %d "
+ "time%s", entry.getKey(), entry.getValue(),
entry.getValue() > 1 ? "s" : "")));
});
}
}

Answer

Your problem is you are processing one word at a time.

Your counting algorithm is OK (except for output order). Your problem is with the Scanner, which reads one word at a time.

Change the scanner to read in the whole file, passing that to your counting method. One (sneaky) way is to do this:

Scanner in = new Scanner(new FileReader("example.txt")).useDelimiter("\\A")

which sets the delimiter to the start of all input, which of course never happens (again) so it keeps reading to the end of the stream.

One interesting and convenient outcome of using "\\A" as the delimiter is that if the input is empty, hasNext() is false, so this change will also automatically handle empty files cleanly.

You can fix the order of the output of the letters just by using a TreeMap instead of a HashMap.

Comments