2011-01-21 25 views
12

Czy istnieje biblioteka, która wykonuje to:Iterable Suma w Javie?

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

Odpowiedz

10

Istnieją dwa przydatnych bibliotek, które mogą w tym pomóc; Google Guava i Apache Commons Collections.

Co próbujesz zrobić, to zasadniczo dwie operacje, pierwsze mapowanie, a następnie redukcja. Nigdy nie używałem Commons Commons w żadnym stopniu, więc nie mogę ci więcej o tym powiedzieć, ale wiem, że nie ma żadnego wsparcia dla redukcji (lub składania) przynajmniej w Google Guava (zobacz Issue 218). To nie jest zbyt trudne, aby dodać siebie choć (nie testowane):

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

W ten sposób można połączyć je z guawy Iterables.transform() tak:

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

A potem (o ile” ve import static'ed odpowiednich klas):

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

Zobacz także this question.

+0

Czy możesz poprawić swój przykład? Otrzymuję 'Metoda stosuje się (A) w typie Funkcja nie ma zastosowania dla argumentów (B, A)' na linii fun.apply(). –

+0

Może. Prawdopodobnie nie.Odpowiedź brzmi 5 lat, po prostu korzystaj ze strumieni Java 8 :) –

9

Java nie jest langugae funkcjonalne i często jest prostsze i szybsze, aby po prostu użyć zwykłej pętli.

Można napisać coś podobnego

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

jednak IMHO jego krótsze i prostsze po prostu pisać.

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

Kiedy zamknięcie stanie się standardem w Javie, zmieni się, ale na razie często najlepszą metodą jest pętla.

+1

Pamiętaj tylko: "krótszy! = Łatwiejszy do odczytania", nastawienie poznawcze, o którym większość programistów nie wie. –

+0

@ Sridhar-Sarnobat zgodził się, na co należy zwrócić szczególną uwagę, używając lambda, szczególnie zagnieżdżonych lambd. np. "a -> b -> a> b" jest krótkie, ale niezbyt wyraźne. –

3

mógłby po prostu użyć Lamdaj - biblioteką do manipulowania zbiorami w pseudo-funkcjonalne i statycznie wpisane sposób:

Można również wykonać inne typy agregacji lub można addd jesteś właścicielem agregatorów:

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

Zobacz Features dla innych przykładów.

7

Ponieważ Java 8 jest teraz na uzyskanie sumy na zbiorach jest prosta:

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

Niestety strumień nie jest dostępny na iterables ale zawsze można konwertować. Tablice są łatwiejsze:

LongStream.of(1, 2, 3).sum()