2011-01-21 3 views
12

Есть библиотека, которая делает это:Iterable Sum в Java?

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; 
    } 
} 

public interface Func<TInput, TOutput> { 
    TOutput run(TInput input); 
} 

ответ

10

Есть в основном две полезные библиотеки, которые могут помочь с этим; Google Guava и Apache Commons Collections.

Что вы пытаетесь сделать, это в основном две операции, первое отображение, затем сокращение. Я никогда не использовал Commons Collections в какой-либо степени, поэтому я не могу больше рассказать об этом, но я знаю, что в Google Guava нет поддержки для сокращения (или сворачивания) (см. Issue 218). Это не слишком сложно, чтобы добавить себя, хотя (не проверено):

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; 
    } 
} 

Таким образом, вы можете комбинировать его с гуавы Iterables.transform() следующим образом:

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 
    } 
} 

И тогда (при условии, вы» ве импорта static'ed соответствующие классы):

reduce(transform(iterable, new MyMapper()), 0, new Summer()); 

Также см this question.

+0

Не могли бы вы исправить свой пример? Я получаю 'Метод apply (A) в типе Функция не применима для аргументов (B, A)' в строке fun.apply(). –

+0

Может быть. Возможно нет.Ответ 5 лет, просто используйте Java 8 потоков :) –

9

Java не является функциональным langugae и часто проще и быстрее, просто используя простой цикл.

Вы могли бы написать что-то вроде

List<String> list = /* ... */ 
int totalLength = Iterables.sum(list, new Func<String, Integer>() { 
    public Integer run(String input) { 
     return input.length(); 
    } 
}); 

однако ИМХО его короче и проще просто написать.

List<String> list = /* ... */ 
int totalLength = 0; 
for(String s: list) totalLength += s.length(); 

Когда закрытие станет стандартным в Java, это изменится, но на данный момент петля часто является лучшим способом.

+1

Просто помните, хотя: «короче! = Легче читать», когнитивное предвзятость, о котором большинство программистов не знают. –

+0

@ Шридхар-Сарнобат согласился с тем, что следует учитывать при использовании лямбда, esp вложенных лямбдов. например, 'a -> b -> a> b' является коротким, но не очень ясным. –

3

Вы могли бы просто использовать Lamdaj - библиотека для управления коллекциями в псевдо-функциональный и статически типизированных путь:

sum = Lambda.sum(iterable); 

Это может также делать другие типы агрегации или вы можете addd вы владеете Накопители:

sum = Lambda.aggregate(seq, new InitializedPairAggregator<Integer>(0) { 
    protected Integer aggregate(Integer first, Integer second) { 
     return first + second; 
    } 
}); 

См Features другие примеры.

7

Поскольку Java-теперь вне получать определенную сумму на коллекции проста:

collection.stream().reduce(0, Integer::sum) 

К сожалению, поток не доступен на итерируемых, но всегда можно преобразовать. Массивы проще:

Смежные вопросы