Iterable Sum in Java?
Is there a library that does this:
public class Iterables{
private Iterables() {}
public static <T> int sum(Iterable<T> iterable, Func<T, Integer> func) {
int result = 0;
for (T item : iterable)
result += func.run(item);
return result;
开发者_如何学Python }
}
public interface Func<TInput, TOutput> {
TOutput run(TInput input);
}
There are basically two useful libraries that can help with this; Google Guava and Apache Commons Collections.
What you are trying to do is basically two operations, first mapping, then reduction. I've never used Commons Collections to any extent myself so I can't tell you more about that, but I know there is no support for reduction (or folding) in Google Guava at least (see Issue 218). This is not too hard to add yourself though (not tested):
interface Function2<A, B> {
B apply(B b, A a);
}
public class Iterables2 {
public static <A, B> B reduce(Iterable<A> iterable,
B initial, Function2<A, B> fun) {
B b = initial;
for (A item : iterable)
b = fun.apply(b, item);
return b;
}
}
That way you can combine it with Guavas Iterables.transform() like so:
class Summer implements Function2<Integer, Integer> {
Integer apply(Integer b, Integer a) {
return b + a;
}
}
class MyMapper<T> implements Function<T, Integer> {
Integer apply(T t) {
// Do stuff
}
}
And then (provided you've import static'ed the relevant classes):
reduce(transform(iterable, new MyMapper()), 0, new Summer());
Also see this question.
Java is not a functional langugae and often it simpler and faster to just using a plain loop.
You could write something like
List<String> list = /* ... */
int totalLength = Iterables.sum(list, new Func<String, Integer>() {
public Integer run(String input) {
return input.length();
}
});
however IMHO its shorter and simpler to just write.
List<String> list = /* ... */
int totalLength = 0;
for(String s: list) totalLength += s.length();
When closures become standard in Java, this will change but for now a loop is often the best way.
Since Java 8 is now out getting a sum on collections is simple:
collection.stream().reduce(0, Integer::sum)
Unfortunately stream is not available on iterables but one can always convert. Arrays are easier:
LongStream.of(1, 2, 3).sum()
Functional Java has a sum method:
http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/function/Integers.html#sum%28fj.data.List%29
Here's an example:
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
ints.add(3);
int sum = Integers.sum(fj.data.List.iterableList(ints));
You could simply use Lamdaj - a library to manipulate collections in a pseudo-functional and statically typed way:
sum = Lambda.sum(iterable);
It can also do other types of aggregation or you can addd you own Aggregators:
sum = Lambda.aggregate(seq, new InitializedPairAggregator<Integer>(0) {
protected Integer aggregate(Integer first, Integer second) {
return first + second;
}
});
See Features for other examples.
精彩评论