Counting characters in a text file
I have a text file and I was curious about what character appears how often in the text.
Any review is appreciated.
public class CountLetters {
public static void main(String args) throws Exception {
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
The output will be for example:
a: 1202
b: 311
c: 603
d: 510
e: 2125
f: 373
g: 362
h: 718
i: 1313
j: 5
k: 74
l: 678
m: 332
n: 1129
o: 1173
p: 348
q: 40
r: 812
s: 1304
t: 1893
u: 415
v: 195
w: 314
x: 86
y: 209
z: 9
java
add a comment |
I have a text file and I was curious about what character appears how often in the text.
Any review is appreciated.
public class CountLetters {
public static void main(String args) throws Exception {
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
The output will be for example:
a: 1202
b: 311
c: 603
d: 510
e: 2125
f: 373
g: 362
h: 718
i: 1313
j: 5
k: 74
l: 678
m: 332
n: 1129
o: 1173
p: 348
q: 40
r: 812
s: 1304
t: 1893
u: 415
v: 195
w: 314
x: 86
y: 209
z: 9
java
add a comment |
I have a text file and I was curious about what character appears how often in the text.
Any review is appreciated.
public class CountLetters {
public static void main(String args) throws Exception {
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
The output will be for example:
a: 1202
b: 311
c: 603
d: 510
e: 2125
f: 373
g: 362
h: 718
i: 1313
j: 5
k: 74
l: 678
m: 332
n: 1129
o: 1173
p: 348
q: 40
r: 812
s: 1304
t: 1893
u: 415
v: 195
w: 314
x: 86
y: 209
z: 9
java
I have a text file and I was curious about what character appears how often in the text.
Any review is appreciated.
public class CountLetters {
public static void main(String args) throws Exception {
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
The output will be for example:
a: 1202
b: 311
c: 603
d: 510
e: 2125
f: 373
g: 362
h: 718
i: 1313
j: 5
k: 74
l: 678
m: 332
n: 1129
o: 1173
p: 348
q: 40
r: 812
s: 1304
t: 1893
u: 415
v: 195
w: 314
x: 86
y: 209
z: 9
java
java
edited Dec 3 '14 at 16:16
Jamal♦
30.3k11116226
30.3k11116226
asked Apr 20 '14 at 10:28
Koray Tugay
11541232
11541232
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
Resources:
You should start using try-with-resources
. This statment does some work for you with resources that implement AutoCloseable
. It closes these resources for you, so you don't have to worry about file-locks and remaining database connections:
File file = new File("C:/text.txt");
try(Scanner scanner = new Scanner(file, "utf-8")){
//your code here ;)
}
You also shouldn't throw Exception
in the main method of your program. This can be very confusing to users. Instead you main-method should handle all exceptions "gracefully" by being wrapped into a try-catch-block.
Conditionals:
if(!Character.isLetter(c)){
continue;
}
This is an early return statement for the purpose of following conditions, meaning you don't have to write else if
in your next condition.
Naming
hashMap
is not a good name. The map you use is not a Hash-Map, and treeMap
would also not explain what the map does, what it contains.
You might want to rename it to characterMap
all else equal, your naming is nice and consistent, and tells exactly what the variables do. You nicely follow camelCase
-conventions. Keep it up!
Summary:
Your code reads nicely and is easily understandable. You follow naming conventions and have descriptive and understandable variable names. You should work on your exception handling and the use of resources.
I would name itcharacters
instead ofcharacterMap
since that "map" doesn't add anything and instead removes some abstraction from your variable. It will cause a refactoring if you change the underlying datastructure.
– Jeroen Vannevel
Apr 20 '14 at 17:25
@Jeroen ypu have a point. But changing the underlying datastructure will cause a refactoring either way.
– Vogel612♦
Apr 20 '14 at 17:30
I would useStandardCharsets.UTF_8
rather than theString
. Otherwise +1
– Boris the Spider
Apr 20 '14 at 18:41
@BoristheSpider In this case it would requireStandardCharsets.UTF_8.toString()
not justStandardCharsets.UTF_8
.
– user40964
Apr 21 '14 at 9:24
add a comment |
My notes in code:
public class CountLetters {
// Throwing Exception is too general, you should throw IOException
public static void main(String args) throws Exception {
// It is better practice to define Map, instead of TreeMap
// The name of variable hashMap could be better, for example characterMap or characters
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
// 'continue' is unnecessary as last statement in a loop
// It is better to put following 'else if' and 'else' here and to remove negation in condition
// like this: if(Character.isLetter(c)){ if ( ... ) { ... } else { ... } }
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
// You should call scanner.close() here
// I would wrap this into if (!hashMap.isEmpty()) { ... }, but it is not really needed
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
I would rewrite the class like this:
public class CountLetters {
public static void main(String args) throws IOException {
Map<Character, Integer> characters = new TreeMap<Character, Integer>();
Scanner scanner = null;
try {
scanner = new Scanner(new File("C:/text.txt"),"utf-8");
while (scanner.hasNext()) {
char line = scanner.nextLine().toLowerCase().toCharArray();
for (Character character : line) {
if (Character.isLetter(character)){
if (characters.containsKey(character)) {
characters.put(character, characters.get(character) + 1);
} else {
characters.put(character, 1);
}
}
}
}
} finally {
if (scanner != null){
scanner.close();
}
}
if (!characters.isEmpty()){
for (Map.Entry<Character, Integer> entry : characters.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
}
It's great to see such a new user being so active! +1, and feel free to join us in our chat room sometime! :)
– syb0rg
Apr 20 '14 at 16:07
add a comment |
- I would change the map
hashMap
tocharacterCounterMap
.
Then I would initialized the map at first with characters, like
for(char c = 'a'; c <= 'z'; c++) {
characterCounterMap.put(c,0);
}
Then it will help you to shortening the if-else ladder, like
if(Character.isLetter(character)) {
characterCounterMap.put(character, characterCounterMap.get(character) + 1);
} // see no else
4
Caution!Character.isLetter()
is Unicode-aware, and supports more than just 'a'..'z'.
– 200_success
Apr 21 '14 at 8:37
add a comment |
All the points in Vogel612's answer should be taken into consideration. Your failure to close resources is your biggest issue.
My main aim with this answer is to show how how this should now be done with Java 8.
Your current method uses very traditional Java loops and conditions. Here is how the code should look with the Java 8 APIs:
public static void main(final String args) {
final Path file = Paths.get("C:/text.txt");
try (final Stream<String> lines = Files.lines(file)) {
final Map<Character, Integer> count = lines.
flatMap(line -> IntStream.range(0, line.length()).mapToObj(line::charAt)).
filter(Character::isLetter).
map(Character::toLowerCase).
collect(TreeMap::new, (m, c) -> m.merge(c, 1, Integer::sum), Map::putAll);
count.forEach((letter, c) -> System.out.println(letter + ": " + c));
} catch (IOException e) {
System.out.println("Failed to read file.");
e.printStackTrace(System.out);
}
}
This code has exactly the same function as your code but is significantly shorter - it leverages Java 8's new Stream
API combined with the all new lambdas.
The code uses Files.lines
to get a Stream<String>
consisting of each line. It then uses flatMap
to turn that into a Stream<Character>
by "flattening" a Stream<Stream<Character>>
which we get by creating an IntStream
of [0, line.length())
and calling line.charAt
for each element of the IntStream
. The char
is then autoboxed to a Character
.
We use the filter
method of Stream
to strip out things that aren't letters.
Now we use the new Map.merge
method with takes a key and a value and in addition a lambda that takes two values. If the key does not exist in the map then it is simply added with the given value. If it does exist in the map then the lambda is called with the existing value and the new value; the value returned from the lambda is then put into the map.
We use the collect
method of the Stream<Character>
to "reduce" the stream to a mutable collection, in this case a TreeMap
.
Finally we use the new forEach
method on Map
to print out the contents of the map.
As a demonstration of the power of Java 8, in order to sort the output by count rather than by the character (as you had in your post), simply change the print to:
count.entrySet().stream().
sorted((l, r) -> l.getValue().compareTo(r.getValue())).
forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
This is really a nice rewrite to Java 8.
– user40964
Apr 21 '14 at 9:27
Something I just noticed... what's the reason you're flatmapping over an IntStream that calls into charAt instead of usingArrays.stream
onto toCharArray?flatMap(line -> Arrays.stream(line.toCharArray())
should work just as well and is IMO cleaner...
– Vogel612♦
Sep 20 '16 at 8:42
1
@Vogel612 you may have a point - I remember having lots of issues withchar
andint
and boxing/unboxing; the only downside I can see from usingtoCharArray
is that it creates a newchar
- but creating the newInterStream
might well be even worse. Another issue I spotted, now that I'm looking at it, is that in the last part(l, r) -> l.getValue().compareTo(r.getValue())
could simply beMap.Entry.comparingByValue()
.
– Boris the Spider
Sep 20 '16 at 9:11
but is significantly shorter
I see that you consider this to be an advantage, but I do not believe shorter code is always better . To me it is usually more difficult to read, to runtime it does not matter at all anyway.. So where is the advantage?
– Koray Tugay
Feb 6 '18 at 19:43
@KorayTugay I believe it is only more difficult to use because you are not used to reading it. I think the above Java 8 code is more terse and expresses semantic meaning much better - you read what the intent of the code is rather than all the boilerplate around the code.
– Boris the Spider
Feb 6 '18 at 21:12
|
show 3 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f47704%2fcounting-characters-in-a-text-file%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Resources:
You should start using try-with-resources
. This statment does some work for you with resources that implement AutoCloseable
. It closes these resources for you, so you don't have to worry about file-locks and remaining database connections:
File file = new File("C:/text.txt");
try(Scanner scanner = new Scanner(file, "utf-8")){
//your code here ;)
}
You also shouldn't throw Exception
in the main method of your program. This can be very confusing to users. Instead you main-method should handle all exceptions "gracefully" by being wrapped into a try-catch-block.
Conditionals:
if(!Character.isLetter(c)){
continue;
}
This is an early return statement for the purpose of following conditions, meaning you don't have to write else if
in your next condition.
Naming
hashMap
is not a good name. The map you use is not a Hash-Map, and treeMap
would also not explain what the map does, what it contains.
You might want to rename it to characterMap
all else equal, your naming is nice and consistent, and tells exactly what the variables do. You nicely follow camelCase
-conventions. Keep it up!
Summary:
Your code reads nicely and is easily understandable. You follow naming conventions and have descriptive and understandable variable names. You should work on your exception handling and the use of resources.
I would name itcharacters
instead ofcharacterMap
since that "map" doesn't add anything and instead removes some abstraction from your variable. It will cause a refactoring if you change the underlying datastructure.
– Jeroen Vannevel
Apr 20 '14 at 17:25
@Jeroen ypu have a point. But changing the underlying datastructure will cause a refactoring either way.
– Vogel612♦
Apr 20 '14 at 17:30
I would useStandardCharsets.UTF_8
rather than theString
. Otherwise +1
– Boris the Spider
Apr 20 '14 at 18:41
@BoristheSpider In this case it would requireStandardCharsets.UTF_8.toString()
not justStandardCharsets.UTF_8
.
– user40964
Apr 21 '14 at 9:24
add a comment |
Resources:
You should start using try-with-resources
. This statment does some work for you with resources that implement AutoCloseable
. It closes these resources for you, so you don't have to worry about file-locks and remaining database connections:
File file = new File("C:/text.txt");
try(Scanner scanner = new Scanner(file, "utf-8")){
//your code here ;)
}
You also shouldn't throw Exception
in the main method of your program. This can be very confusing to users. Instead you main-method should handle all exceptions "gracefully" by being wrapped into a try-catch-block.
Conditionals:
if(!Character.isLetter(c)){
continue;
}
This is an early return statement for the purpose of following conditions, meaning you don't have to write else if
in your next condition.
Naming
hashMap
is not a good name. The map you use is not a Hash-Map, and treeMap
would also not explain what the map does, what it contains.
You might want to rename it to characterMap
all else equal, your naming is nice and consistent, and tells exactly what the variables do. You nicely follow camelCase
-conventions. Keep it up!
Summary:
Your code reads nicely and is easily understandable. You follow naming conventions and have descriptive and understandable variable names. You should work on your exception handling and the use of resources.
I would name itcharacters
instead ofcharacterMap
since that "map" doesn't add anything and instead removes some abstraction from your variable. It will cause a refactoring if you change the underlying datastructure.
– Jeroen Vannevel
Apr 20 '14 at 17:25
@Jeroen ypu have a point. But changing the underlying datastructure will cause a refactoring either way.
– Vogel612♦
Apr 20 '14 at 17:30
I would useStandardCharsets.UTF_8
rather than theString
. Otherwise +1
– Boris the Spider
Apr 20 '14 at 18:41
@BoristheSpider In this case it would requireStandardCharsets.UTF_8.toString()
not justStandardCharsets.UTF_8
.
– user40964
Apr 21 '14 at 9:24
add a comment |
Resources:
You should start using try-with-resources
. This statment does some work for you with resources that implement AutoCloseable
. It closes these resources for you, so you don't have to worry about file-locks and remaining database connections:
File file = new File("C:/text.txt");
try(Scanner scanner = new Scanner(file, "utf-8")){
//your code here ;)
}
You also shouldn't throw Exception
in the main method of your program. This can be very confusing to users. Instead you main-method should handle all exceptions "gracefully" by being wrapped into a try-catch-block.
Conditionals:
if(!Character.isLetter(c)){
continue;
}
This is an early return statement for the purpose of following conditions, meaning you don't have to write else if
in your next condition.
Naming
hashMap
is not a good name. The map you use is not a Hash-Map, and treeMap
would also not explain what the map does, what it contains.
You might want to rename it to characterMap
all else equal, your naming is nice and consistent, and tells exactly what the variables do. You nicely follow camelCase
-conventions. Keep it up!
Summary:
Your code reads nicely and is easily understandable. You follow naming conventions and have descriptive and understandable variable names. You should work on your exception handling and the use of resources.
Resources:
You should start using try-with-resources
. This statment does some work for you with resources that implement AutoCloseable
. It closes these resources for you, so you don't have to worry about file-locks and remaining database connections:
File file = new File("C:/text.txt");
try(Scanner scanner = new Scanner(file, "utf-8")){
//your code here ;)
}
You also shouldn't throw Exception
in the main method of your program. This can be very confusing to users. Instead you main-method should handle all exceptions "gracefully" by being wrapped into a try-catch-block.
Conditionals:
if(!Character.isLetter(c)){
continue;
}
This is an early return statement for the purpose of following conditions, meaning you don't have to write else if
in your next condition.
Naming
hashMap
is not a good name. The map you use is not a Hash-Map, and treeMap
would also not explain what the map does, what it contains.
You might want to rename it to characterMap
all else equal, your naming is nice and consistent, and tells exactly what the variables do. You nicely follow camelCase
-conventions. Keep it up!
Summary:
Your code reads nicely and is easily understandable. You follow naming conventions and have descriptive and understandable variable names. You should work on your exception handling and the use of resources.
edited Apr 20 '14 at 11:31
answered Apr 20 '14 at 11:13
Vogel612♦
21.3k346128
21.3k346128
I would name itcharacters
instead ofcharacterMap
since that "map" doesn't add anything and instead removes some abstraction from your variable. It will cause a refactoring if you change the underlying datastructure.
– Jeroen Vannevel
Apr 20 '14 at 17:25
@Jeroen ypu have a point. But changing the underlying datastructure will cause a refactoring either way.
– Vogel612♦
Apr 20 '14 at 17:30
I would useStandardCharsets.UTF_8
rather than theString
. Otherwise +1
– Boris the Spider
Apr 20 '14 at 18:41
@BoristheSpider In this case it would requireStandardCharsets.UTF_8.toString()
not justStandardCharsets.UTF_8
.
– user40964
Apr 21 '14 at 9:24
add a comment |
I would name itcharacters
instead ofcharacterMap
since that "map" doesn't add anything and instead removes some abstraction from your variable. It will cause a refactoring if you change the underlying datastructure.
– Jeroen Vannevel
Apr 20 '14 at 17:25
@Jeroen ypu have a point. But changing the underlying datastructure will cause a refactoring either way.
– Vogel612♦
Apr 20 '14 at 17:30
I would useStandardCharsets.UTF_8
rather than theString
. Otherwise +1
– Boris the Spider
Apr 20 '14 at 18:41
@BoristheSpider In this case it would requireStandardCharsets.UTF_8.toString()
not justStandardCharsets.UTF_8
.
– user40964
Apr 21 '14 at 9:24
I would name it
characters
instead of characterMap
since that "map" doesn't add anything and instead removes some abstraction from your variable. It will cause a refactoring if you change the underlying datastructure.– Jeroen Vannevel
Apr 20 '14 at 17:25
I would name it
characters
instead of characterMap
since that "map" doesn't add anything and instead removes some abstraction from your variable. It will cause a refactoring if you change the underlying datastructure.– Jeroen Vannevel
Apr 20 '14 at 17:25
@Jeroen ypu have a point. But changing the underlying datastructure will cause a refactoring either way.
– Vogel612♦
Apr 20 '14 at 17:30
@Jeroen ypu have a point. But changing the underlying datastructure will cause a refactoring either way.
– Vogel612♦
Apr 20 '14 at 17:30
I would use
StandardCharsets.UTF_8
rather than the String
. Otherwise +1– Boris the Spider
Apr 20 '14 at 18:41
I would use
StandardCharsets.UTF_8
rather than the String
. Otherwise +1– Boris the Spider
Apr 20 '14 at 18:41
@BoristheSpider In this case it would require
StandardCharsets.UTF_8.toString()
not just StandardCharsets.UTF_8
.– user40964
Apr 21 '14 at 9:24
@BoristheSpider In this case it would require
StandardCharsets.UTF_8.toString()
not just StandardCharsets.UTF_8
.– user40964
Apr 21 '14 at 9:24
add a comment |
My notes in code:
public class CountLetters {
// Throwing Exception is too general, you should throw IOException
public static void main(String args) throws Exception {
// It is better practice to define Map, instead of TreeMap
// The name of variable hashMap could be better, for example characterMap or characters
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
// 'continue' is unnecessary as last statement in a loop
// It is better to put following 'else if' and 'else' here and to remove negation in condition
// like this: if(Character.isLetter(c)){ if ( ... ) { ... } else { ... } }
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
// You should call scanner.close() here
// I would wrap this into if (!hashMap.isEmpty()) { ... }, but it is not really needed
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
I would rewrite the class like this:
public class CountLetters {
public static void main(String args) throws IOException {
Map<Character, Integer> characters = new TreeMap<Character, Integer>();
Scanner scanner = null;
try {
scanner = new Scanner(new File("C:/text.txt"),"utf-8");
while (scanner.hasNext()) {
char line = scanner.nextLine().toLowerCase().toCharArray();
for (Character character : line) {
if (Character.isLetter(character)){
if (characters.containsKey(character)) {
characters.put(character, characters.get(character) + 1);
} else {
characters.put(character, 1);
}
}
}
}
} finally {
if (scanner != null){
scanner.close();
}
}
if (!characters.isEmpty()){
for (Map.Entry<Character, Integer> entry : characters.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
}
It's great to see such a new user being so active! +1, and feel free to join us in our chat room sometime! :)
– syb0rg
Apr 20 '14 at 16:07
add a comment |
My notes in code:
public class CountLetters {
// Throwing Exception is too general, you should throw IOException
public static void main(String args) throws Exception {
// It is better practice to define Map, instead of TreeMap
// The name of variable hashMap could be better, for example characterMap or characters
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
// 'continue' is unnecessary as last statement in a loop
// It is better to put following 'else if' and 'else' here and to remove negation in condition
// like this: if(Character.isLetter(c)){ if ( ... ) { ... } else { ... } }
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
// You should call scanner.close() here
// I would wrap this into if (!hashMap.isEmpty()) { ... }, but it is not really needed
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
I would rewrite the class like this:
public class CountLetters {
public static void main(String args) throws IOException {
Map<Character, Integer> characters = new TreeMap<Character, Integer>();
Scanner scanner = null;
try {
scanner = new Scanner(new File("C:/text.txt"),"utf-8");
while (scanner.hasNext()) {
char line = scanner.nextLine().toLowerCase().toCharArray();
for (Character character : line) {
if (Character.isLetter(character)){
if (characters.containsKey(character)) {
characters.put(character, characters.get(character) + 1);
} else {
characters.put(character, 1);
}
}
}
}
} finally {
if (scanner != null){
scanner.close();
}
}
if (!characters.isEmpty()){
for (Map.Entry<Character, Integer> entry : characters.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
}
It's great to see such a new user being so active! +1, and feel free to join us in our chat room sometime! :)
– syb0rg
Apr 20 '14 at 16:07
add a comment |
My notes in code:
public class CountLetters {
// Throwing Exception is too general, you should throw IOException
public static void main(String args) throws Exception {
// It is better practice to define Map, instead of TreeMap
// The name of variable hashMap could be better, for example characterMap or characters
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
// 'continue' is unnecessary as last statement in a loop
// It is better to put following 'else if' and 'else' here and to remove negation in condition
// like this: if(Character.isLetter(c)){ if ( ... ) { ... } else { ... } }
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
// You should call scanner.close() here
// I would wrap this into if (!hashMap.isEmpty()) { ... }, but it is not really needed
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
I would rewrite the class like this:
public class CountLetters {
public static void main(String args) throws IOException {
Map<Character, Integer> characters = new TreeMap<Character, Integer>();
Scanner scanner = null;
try {
scanner = new Scanner(new File("C:/text.txt"),"utf-8");
while (scanner.hasNext()) {
char line = scanner.nextLine().toLowerCase().toCharArray();
for (Character character : line) {
if (Character.isLetter(character)){
if (characters.containsKey(character)) {
characters.put(character, characters.get(character) + 1);
} else {
characters.put(character, 1);
}
}
}
}
} finally {
if (scanner != null){
scanner.close();
}
}
if (!characters.isEmpty()){
for (Map.Entry<Character, Integer> entry : characters.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
}
My notes in code:
public class CountLetters {
// Throwing Exception is too general, you should throw IOException
public static void main(String args) throws Exception {
// It is better practice to define Map, instead of TreeMap
// The name of variable hashMap could be better, for example characterMap or characters
TreeMap<Character, Integer> hashMap = new TreeMap<Character, Integer>();
File file = new File("C:/text.txt");
Scanner scanner = new Scanner(file,"utf-8");
while (scanner.hasNext()) {
char chars = scanner.nextLine().toLowerCase().toCharArray();
for (Character c : chars) {
if(!Character.isLetter(c)){
// 'continue' is unnecessary as last statement in a loop
// It is better to put following 'else if' and 'else' here and to remove negation in condition
// like this: if(Character.isLetter(c)){ if ( ... ) { ... } else { ... } }
continue;
}
else if (hashMap.containsKey(c)) {
hashMap.put(c, hashMap.get(c) + 1);
} else {
hashMap.put(c, 1);
}
}
}
// You should call scanner.close() here
// I would wrap this into if (!hashMap.isEmpty()) { ... }, but it is not really needed
for (Map.Entry<Character, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
I would rewrite the class like this:
public class CountLetters {
public static void main(String args) throws IOException {
Map<Character, Integer> characters = new TreeMap<Character, Integer>();
Scanner scanner = null;
try {
scanner = new Scanner(new File("C:/text.txt"),"utf-8");
while (scanner.hasNext()) {
char line = scanner.nextLine().toLowerCase().toCharArray();
for (Character character : line) {
if (Character.isLetter(character)){
if (characters.containsKey(character)) {
characters.put(character, characters.get(character) + 1);
} else {
characters.put(character, 1);
}
}
}
}
} finally {
if (scanner != null){
scanner.close();
}
}
if (!characters.isEmpty()){
for (Map.Entry<Character, Integer> entry : characters.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
}
answered Apr 20 '14 at 11:24
user40964
It's great to see such a new user being so active! +1, and feel free to join us in our chat room sometime! :)
– syb0rg
Apr 20 '14 at 16:07
add a comment |
It's great to see such a new user being so active! +1, and feel free to join us in our chat room sometime! :)
– syb0rg
Apr 20 '14 at 16:07
It's great to see such a new user being so active! +1, and feel free to join us in our chat room sometime! :)
– syb0rg
Apr 20 '14 at 16:07
It's great to see such a new user being so active! +1, and feel free to join us in our chat room sometime! :)
– syb0rg
Apr 20 '14 at 16:07
add a comment |
- I would change the map
hashMap
tocharacterCounterMap
.
Then I would initialized the map at first with characters, like
for(char c = 'a'; c <= 'z'; c++) {
characterCounterMap.put(c,0);
}
Then it will help you to shortening the if-else ladder, like
if(Character.isLetter(character)) {
characterCounterMap.put(character, characterCounterMap.get(character) + 1);
} // see no else
4
Caution!Character.isLetter()
is Unicode-aware, and supports more than just 'a'..'z'.
– 200_success
Apr 21 '14 at 8:37
add a comment |
- I would change the map
hashMap
tocharacterCounterMap
.
Then I would initialized the map at first with characters, like
for(char c = 'a'; c <= 'z'; c++) {
characterCounterMap.put(c,0);
}
Then it will help you to shortening the if-else ladder, like
if(Character.isLetter(character)) {
characterCounterMap.put(character, characterCounterMap.get(character) + 1);
} // see no else
4
Caution!Character.isLetter()
is Unicode-aware, and supports more than just 'a'..'z'.
– 200_success
Apr 21 '14 at 8:37
add a comment |
- I would change the map
hashMap
tocharacterCounterMap
.
Then I would initialized the map at first with characters, like
for(char c = 'a'; c <= 'z'; c++) {
characterCounterMap.put(c,0);
}
Then it will help you to shortening the if-else ladder, like
if(Character.isLetter(character)) {
characterCounterMap.put(character, characterCounterMap.get(character) + 1);
} // see no else
- I would change the map
hashMap
tocharacterCounterMap
.
Then I would initialized the map at first with characters, like
for(char c = 'a'; c <= 'z'; c++) {
characterCounterMap.put(c,0);
}
Then it will help you to shortening the if-else ladder, like
if(Character.isLetter(character)) {
characterCounterMap.put(character, characterCounterMap.get(character) + 1);
} // see no else
answered Apr 20 '14 at 15:23
Anirban Nag 'tintinmj'
1,7421033
1,7421033
4
Caution!Character.isLetter()
is Unicode-aware, and supports more than just 'a'..'z'.
– 200_success
Apr 21 '14 at 8:37
add a comment |
4
Caution!Character.isLetter()
is Unicode-aware, and supports more than just 'a'..'z'.
– 200_success
Apr 21 '14 at 8:37
4
4
Caution!
Character.isLetter()
is Unicode-aware, and supports more than just 'a'..'z'.– 200_success
Apr 21 '14 at 8:37
Caution!
Character.isLetter()
is Unicode-aware, and supports more than just 'a'..'z'.– 200_success
Apr 21 '14 at 8:37
add a comment |
All the points in Vogel612's answer should be taken into consideration. Your failure to close resources is your biggest issue.
My main aim with this answer is to show how how this should now be done with Java 8.
Your current method uses very traditional Java loops and conditions. Here is how the code should look with the Java 8 APIs:
public static void main(final String args) {
final Path file = Paths.get("C:/text.txt");
try (final Stream<String> lines = Files.lines(file)) {
final Map<Character, Integer> count = lines.
flatMap(line -> IntStream.range(0, line.length()).mapToObj(line::charAt)).
filter(Character::isLetter).
map(Character::toLowerCase).
collect(TreeMap::new, (m, c) -> m.merge(c, 1, Integer::sum), Map::putAll);
count.forEach((letter, c) -> System.out.println(letter + ": " + c));
} catch (IOException e) {
System.out.println("Failed to read file.");
e.printStackTrace(System.out);
}
}
This code has exactly the same function as your code but is significantly shorter - it leverages Java 8's new Stream
API combined with the all new lambdas.
The code uses Files.lines
to get a Stream<String>
consisting of each line. It then uses flatMap
to turn that into a Stream<Character>
by "flattening" a Stream<Stream<Character>>
which we get by creating an IntStream
of [0, line.length())
and calling line.charAt
for each element of the IntStream
. The char
is then autoboxed to a Character
.
We use the filter
method of Stream
to strip out things that aren't letters.
Now we use the new Map.merge
method with takes a key and a value and in addition a lambda that takes two values. If the key does not exist in the map then it is simply added with the given value. If it does exist in the map then the lambda is called with the existing value and the new value; the value returned from the lambda is then put into the map.
We use the collect
method of the Stream<Character>
to "reduce" the stream to a mutable collection, in this case a TreeMap
.
Finally we use the new forEach
method on Map
to print out the contents of the map.
As a demonstration of the power of Java 8, in order to sort the output by count rather than by the character (as you had in your post), simply change the print to:
count.entrySet().stream().
sorted((l, r) -> l.getValue().compareTo(r.getValue())).
forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
This is really a nice rewrite to Java 8.
– user40964
Apr 21 '14 at 9:27
Something I just noticed... what's the reason you're flatmapping over an IntStream that calls into charAt instead of usingArrays.stream
onto toCharArray?flatMap(line -> Arrays.stream(line.toCharArray())
should work just as well and is IMO cleaner...
– Vogel612♦
Sep 20 '16 at 8:42
1
@Vogel612 you may have a point - I remember having lots of issues withchar
andint
and boxing/unboxing; the only downside I can see from usingtoCharArray
is that it creates a newchar
- but creating the newInterStream
might well be even worse. Another issue I spotted, now that I'm looking at it, is that in the last part(l, r) -> l.getValue().compareTo(r.getValue())
could simply beMap.Entry.comparingByValue()
.
– Boris the Spider
Sep 20 '16 at 9:11
but is significantly shorter
I see that you consider this to be an advantage, but I do not believe shorter code is always better . To me it is usually more difficult to read, to runtime it does not matter at all anyway.. So where is the advantage?
– Koray Tugay
Feb 6 '18 at 19:43
@KorayTugay I believe it is only more difficult to use because you are not used to reading it. I think the above Java 8 code is more terse and expresses semantic meaning much better - you read what the intent of the code is rather than all the boilerplate around the code.
– Boris the Spider
Feb 6 '18 at 21:12
|
show 3 more comments
All the points in Vogel612's answer should be taken into consideration. Your failure to close resources is your biggest issue.
My main aim with this answer is to show how how this should now be done with Java 8.
Your current method uses very traditional Java loops and conditions. Here is how the code should look with the Java 8 APIs:
public static void main(final String args) {
final Path file = Paths.get("C:/text.txt");
try (final Stream<String> lines = Files.lines(file)) {
final Map<Character, Integer> count = lines.
flatMap(line -> IntStream.range(0, line.length()).mapToObj(line::charAt)).
filter(Character::isLetter).
map(Character::toLowerCase).
collect(TreeMap::new, (m, c) -> m.merge(c, 1, Integer::sum), Map::putAll);
count.forEach((letter, c) -> System.out.println(letter + ": " + c));
} catch (IOException e) {
System.out.println("Failed to read file.");
e.printStackTrace(System.out);
}
}
This code has exactly the same function as your code but is significantly shorter - it leverages Java 8's new Stream
API combined with the all new lambdas.
The code uses Files.lines
to get a Stream<String>
consisting of each line. It then uses flatMap
to turn that into a Stream<Character>
by "flattening" a Stream<Stream<Character>>
which we get by creating an IntStream
of [0, line.length())
and calling line.charAt
for each element of the IntStream
. The char
is then autoboxed to a Character
.
We use the filter
method of Stream
to strip out things that aren't letters.
Now we use the new Map.merge
method with takes a key and a value and in addition a lambda that takes two values. If the key does not exist in the map then it is simply added with the given value. If it does exist in the map then the lambda is called with the existing value and the new value; the value returned from the lambda is then put into the map.
We use the collect
method of the Stream<Character>
to "reduce" the stream to a mutable collection, in this case a TreeMap
.
Finally we use the new forEach
method on Map
to print out the contents of the map.
As a demonstration of the power of Java 8, in order to sort the output by count rather than by the character (as you had in your post), simply change the print to:
count.entrySet().stream().
sorted((l, r) -> l.getValue().compareTo(r.getValue())).
forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
This is really a nice rewrite to Java 8.
– user40964
Apr 21 '14 at 9:27
Something I just noticed... what's the reason you're flatmapping over an IntStream that calls into charAt instead of usingArrays.stream
onto toCharArray?flatMap(line -> Arrays.stream(line.toCharArray())
should work just as well and is IMO cleaner...
– Vogel612♦
Sep 20 '16 at 8:42
1
@Vogel612 you may have a point - I remember having lots of issues withchar
andint
and boxing/unboxing; the only downside I can see from usingtoCharArray
is that it creates a newchar
- but creating the newInterStream
might well be even worse. Another issue I spotted, now that I'm looking at it, is that in the last part(l, r) -> l.getValue().compareTo(r.getValue())
could simply beMap.Entry.comparingByValue()
.
– Boris the Spider
Sep 20 '16 at 9:11
but is significantly shorter
I see that you consider this to be an advantage, but I do not believe shorter code is always better . To me it is usually more difficult to read, to runtime it does not matter at all anyway.. So where is the advantage?
– Koray Tugay
Feb 6 '18 at 19:43
@KorayTugay I believe it is only more difficult to use because you are not used to reading it. I think the above Java 8 code is more terse and expresses semantic meaning much better - you read what the intent of the code is rather than all the boilerplate around the code.
– Boris the Spider
Feb 6 '18 at 21:12
|
show 3 more comments
All the points in Vogel612's answer should be taken into consideration. Your failure to close resources is your biggest issue.
My main aim with this answer is to show how how this should now be done with Java 8.
Your current method uses very traditional Java loops and conditions. Here is how the code should look with the Java 8 APIs:
public static void main(final String args) {
final Path file = Paths.get("C:/text.txt");
try (final Stream<String> lines = Files.lines(file)) {
final Map<Character, Integer> count = lines.
flatMap(line -> IntStream.range(0, line.length()).mapToObj(line::charAt)).
filter(Character::isLetter).
map(Character::toLowerCase).
collect(TreeMap::new, (m, c) -> m.merge(c, 1, Integer::sum), Map::putAll);
count.forEach((letter, c) -> System.out.println(letter + ": " + c));
} catch (IOException e) {
System.out.println("Failed to read file.");
e.printStackTrace(System.out);
}
}
This code has exactly the same function as your code but is significantly shorter - it leverages Java 8's new Stream
API combined with the all new lambdas.
The code uses Files.lines
to get a Stream<String>
consisting of each line. It then uses flatMap
to turn that into a Stream<Character>
by "flattening" a Stream<Stream<Character>>
which we get by creating an IntStream
of [0, line.length())
and calling line.charAt
for each element of the IntStream
. The char
is then autoboxed to a Character
.
We use the filter
method of Stream
to strip out things that aren't letters.
Now we use the new Map.merge
method with takes a key and a value and in addition a lambda that takes two values. If the key does not exist in the map then it is simply added with the given value. If it does exist in the map then the lambda is called with the existing value and the new value; the value returned from the lambda is then put into the map.
We use the collect
method of the Stream<Character>
to "reduce" the stream to a mutable collection, in this case a TreeMap
.
Finally we use the new forEach
method on Map
to print out the contents of the map.
As a demonstration of the power of Java 8, in order to sort the output by count rather than by the character (as you had in your post), simply change the print to:
count.entrySet().stream().
sorted((l, r) -> l.getValue().compareTo(r.getValue())).
forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
All the points in Vogel612's answer should be taken into consideration. Your failure to close resources is your biggest issue.
My main aim with this answer is to show how how this should now be done with Java 8.
Your current method uses very traditional Java loops and conditions. Here is how the code should look with the Java 8 APIs:
public static void main(final String args) {
final Path file = Paths.get("C:/text.txt");
try (final Stream<String> lines = Files.lines(file)) {
final Map<Character, Integer> count = lines.
flatMap(line -> IntStream.range(0, line.length()).mapToObj(line::charAt)).
filter(Character::isLetter).
map(Character::toLowerCase).
collect(TreeMap::new, (m, c) -> m.merge(c, 1, Integer::sum), Map::putAll);
count.forEach((letter, c) -> System.out.println(letter + ": " + c));
} catch (IOException e) {
System.out.println("Failed to read file.");
e.printStackTrace(System.out);
}
}
This code has exactly the same function as your code but is significantly shorter - it leverages Java 8's new Stream
API combined with the all new lambdas.
The code uses Files.lines
to get a Stream<String>
consisting of each line. It then uses flatMap
to turn that into a Stream<Character>
by "flattening" a Stream<Stream<Character>>
which we get by creating an IntStream
of [0, line.length())
and calling line.charAt
for each element of the IntStream
. The char
is then autoboxed to a Character
.
We use the filter
method of Stream
to strip out things that aren't letters.
Now we use the new Map.merge
method with takes a key and a value and in addition a lambda that takes two values. If the key does not exist in the map then it is simply added with the given value. If it does exist in the map then the lambda is called with the existing value and the new value; the value returned from the lambda is then put into the map.
We use the collect
method of the Stream<Character>
to "reduce" the stream to a mutable collection, in this case a TreeMap
.
Finally we use the new forEach
method on Map
to print out the contents of the map.
As a demonstration of the power of Java 8, in order to sort the output by count rather than by the character (as you had in your post), simply change the print to:
count.entrySet().stream().
sorted((l, r) -> l.getValue().compareTo(r.getValue())).
forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
edited Apr 21 '14 at 16:38
answered Apr 20 '14 at 17:28
Boris the Spider
940917
940917
This is really a nice rewrite to Java 8.
– user40964
Apr 21 '14 at 9:27
Something I just noticed... what's the reason you're flatmapping over an IntStream that calls into charAt instead of usingArrays.stream
onto toCharArray?flatMap(line -> Arrays.stream(line.toCharArray())
should work just as well and is IMO cleaner...
– Vogel612♦
Sep 20 '16 at 8:42
1
@Vogel612 you may have a point - I remember having lots of issues withchar
andint
and boxing/unboxing; the only downside I can see from usingtoCharArray
is that it creates a newchar
- but creating the newInterStream
might well be even worse. Another issue I spotted, now that I'm looking at it, is that in the last part(l, r) -> l.getValue().compareTo(r.getValue())
could simply beMap.Entry.comparingByValue()
.
– Boris the Spider
Sep 20 '16 at 9:11
but is significantly shorter
I see that you consider this to be an advantage, but I do not believe shorter code is always better . To me it is usually more difficult to read, to runtime it does not matter at all anyway.. So where is the advantage?
– Koray Tugay
Feb 6 '18 at 19:43
@KorayTugay I believe it is only more difficult to use because you are not used to reading it. I think the above Java 8 code is more terse and expresses semantic meaning much better - you read what the intent of the code is rather than all the boilerplate around the code.
– Boris the Spider
Feb 6 '18 at 21:12
|
show 3 more comments
This is really a nice rewrite to Java 8.
– user40964
Apr 21 '14 at 9:27
Something I just noticed... what's the reason you're flatmapping over an IntStream that calls into charAt instead of usingArrays.stream
onto toCharArray?flatMap(line -> Arrays.stream(line.toCharArray())
should work just as well and is IMO cleaner...
– Vogel612♦
Sep 20 '16 at 8:42
1
@Vogel612 you may have a point - I remember having lots of issues withchar
andint
and boxing/unboxing; the only downside I can see from usingtoCharArray
is that it creates a newchar
- but creating the newInterStream
might well be even worse. Another issue I spotted, now that I'm looking at it, is that in the last part(l, r) -> l.getValue().compareTo(r.getValue())
could simply beMap.Entry.comparingByValue()
.
– Boris the Spider
Sep 20 '16 at 9:11
but is significantly shorter
I see that you consider this to be an advantage, but I do not believe shorter code is always better . To me it is usually more difficult to read, to runtime it does not matter at all anyway.. So where is the advantage?
– Koray Tugay
Feb 6 '18 at 19:43
@KorayTugay I believe it is only more difficult to use because you are not used to reading it. I think the above Java 8 code is more terse and expresses semantic meaning much better - you read what the intent of the code is rather than all the boilerplate around the code.
– Boris the Spider
Feb 6 '18 at 21:12
This is really a nice rewrite to Java 8.
– user40964
Apr 21 '14 at 9:27
This is really a nice rewrite to Java 8.
– user40964
Apr 21 '14 at 9:27
Something I just noticed... what's the reason you're flatmapping over an IntStream that calls into charAt instead of using
Arrays.stream
onto toCharArray? flatMap(line -> Arrays.stream(line.toCharArray())
should work just as well and is IMO cleaner...– Vogel612♦
Sep 20 '16 at 8:42
Something I just noticed... what's the reason you're flatmapping over an IntStream that calls into charAt instead of using
Arrays.stream
onto toCharArray? flatMap(line -> Arrays.stream(line.toCharArray())
should work just as well and is IMO cleaner...– Vogel612♦
Sep 20 '16 at 8:42
1
1
@Vogel612 you may have a point - I remember having lots of issues with
char
and int
and boxing/unboxing; the only downside I can see from using toCharArray
is that it creates a new char
- but creating the new InterStream
might well be even worse. Another issue I spotted, now that I'm looking at it, is that in the last part (l, r) -> l.getValue().compareTo(r.getValue())
could simply be Map.Entry.comparingByValue()
.– Boris the Spider
Sep 20 '16 at 9:11
@Vogel612 you may have a point - I remember having lots of issues with
char
and int
and boxing/unboxing; the only downside I can see from using toCharArray
is that it creates a new char
- but creating the new InterStream
might well be even worse. Another issue I spotted, now that I'm looking at it, is that in the last part (l, r) -> l.getValue().compareTo(r.getValue())
could simply be Map.Entry.comparingByValue()
.– Boris the Spider
Sep 20 '16 at 9:11
but is significantly shorter
I see that you consider this to be an advantage, but I do not believe shorter code is always better . To me it is usually more difficult to read, to runtime it does not matter at all anyway.. So where is the advantage?– Koray Tugay
Feb 6 '18 at 19:43
but is significantly shorter
I see that you consider this to be an advantage, but I do not believe shorter code is always better . To me it is usually more difficult to read, to runtime it does not matter at all anyway.. So where is the advantage?– Koray Tugay
Feb 6 '18 at 19:43
@KorayTugay I believe it is only more difficult to use because you are not used to reading it. I think the above Java 8 code is more terse and expresses semantic meaning much better - you read what the intent of the code is rather than all the boilerplate around the code.
– Boris the Spider
Feb 6 '18 at 21:12
@KorayTugay I believe it is only more difficult to use because you are not used to reading it. I think the above Java 8 code is more terse and expresses semantic meaning much better - you read what the intent of the code is rather than all the boilerplate around the code.
– Boris the Spider
Feb 6 '18 at 21:12
|
show 3 more comments
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f47704%2fcounting-characters-in-a-text-file%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown