Previous Question: Armstrong numbers in a given range using Java 8
Updated program for getting all Armstrong Numbers between 1
and 10_000_000
as per the suggestions in this answer.
public class ArmstrongNumbers {
public static void main(String[] args) {
IntStream.range(1, 10_000_000)
.filter((n) -> {
int size = Integer.toString(n).length();
return Integer.toString(n)
.chars()
.map(d -> d - '0')
.mapToDouble(v -> Math.pow(v, size))
.sum() == n;
}).forEach(System.out::println);
}
}
Output:
1
2
3
4
5
6
7
8
9
153
370
371
407
1634
8208
9474
54748
92727
93084
548834
1741725
4210818
9800817
9926315
Comments:
- Was able to replace the while loop with a stream.
- Code isn't shorter but is more readable.
Can this program be made even shorter? Any other suggestions also welcome.
2 Answers 2
Looking at the code for Integer.toString
, it seems to be a lot of work to convert an integer to a string. At the moment, you're doing the conversion twice.
However, getting the length of a string, via String.length
, is a simple getter. As such, you'd probably be better off with storing the string representation in a variable, and calling .length()
on that.
public static void main(String[] args) {
IntStream.range(1, 10_000_000)
.filter((n) -> {
final String number = Integer.toString(n);
return number.chars()
.map(d -> d - '0')
.mapToDouble(v -> Math.pow(v, number.length()))
.sum() == n;
}).forEach(System.out::println);
}
Also, good job coming up with the idea of substracting '0' from each character to cast it back to a number: that's a far better idea than what I had in mind (using complicated parse functions).
You could, in theory, perform this step at .mapToDouble
, via Math.pow((v - '0'), number.length())
but I'm not sure whether that helps the readability. It does make things a lot shorter, I guess.
A potential performance upgrade after this would be to consider storing the result of Math.pow
in a lookup table; whenever you surpass the current digit length, multiply each value of the lookup table by its index and continue. Right now, this doesn't seem very relevant to me, but later, this might be an issue.
There's not much to improve, but there are a couple of minor inconsistencies:
.filter((n) -> {
.map(d -> d - '0')
.mapToDouble(v -> Math.pow(v, size))
I would ditch the parentheses in the first lambda, as you have in the other two.
}).forEach(System.out::println);
I would insert a newline before the .
, as you have on the other chained method calls.
-
\$\begingroup\$ is this formatting intentional? It's kinda hard to read. \$\endgroup\$Pimgd– Pimgd2016年07月13日 14:06:23 +00:00Commented Jul 13, 2016 at 14:06
-
\$\begingroup\$ @Pimgd, it's just a combination of blockquote (because I'm quoting OP's code rather than writing anything new) and pre (because it's code). I don't find it hard to read, but maybe it's a matter of taste. You could always look into user stylesheets if you have an idea for how to make it look better. \$\endgroup\$Peter Taylor– Peter Taylor2016年07月13日 14:55:48 +00:00Commented Jul 13, 2016 at 14:55