2015-02-28 24 views
30

Podczas korzystania ze strumieni Java 8 dość często zdarza się, że można pobrać listę, utworzyć z niej strumień, wykonać transakcję i ją przekonwertować. Coś jak:Dlaczego Stream nie miał metody toList()?

Stream.of(-2,1,2,-5) 
     .filter(n -> n > 0) 
     .map(n -> n * n) 
     .collect(Collectors.toList()); 

Dlaczego nie ma skrótu/wygodna metoda dla „.collect(Collectors.toList())” części? W interfejsie On Stream istnieje metoda przekształcania wyników w tablicę o nazwie toArray(), dlaczego brakuje toList()?

IMHO, konwertowanie wyniku na listę jest częstsze niż w tablicy. Mogę z tym żyć, ale denerwujące jest nazywanie tej brzydoty.

Wszelkie pomysły?

+15

Prosiłbym przeciwny pytanie: dlaczego 'toArray()' zamiast 'zbierać (ToArray ()) '. Eksplozja interfejsu API jest czymś, co JDK ma tendencję do odparcia jak najwięcej. Oczekuję, że będzie dobre uzasadnienie dla 'toArray()'. –

+10

Dlaczego warto zatrzymać się na 'toList'? Pozwala również dodać 'toStack'' toSet' 'toMap'. – Pshemo

+0

@MarkoTopolnik prawy, thx, Eddited odpowiedź –

Odpowiedz

3

Jeśli chodzi o "dlaczego", to uważam, że w komentarzach jest sporo argumentów. Jednak zgadzam się z tobą, że denerwowanie nie jest możliwe. Podobnie dzieje się z metodą toIterable().

Pokażę ci sztuczkę, która pozwala ci użyć tych dwóch metod. Na szczęście Java jest bardzo elastyczna i pozwala robić wiele interesujących rzeczy. Około 10 lat temu przeczytałem this article, który opisuje dowcipną sztuczkę do "podłączania" metod do dowolnego interfejsu. Sztuczka polega na użyciu proxy do dostosowania interfejsu, który nie ma pożądanych metod. Z biegiem lat odkryłem, że ma on wszystkie zalety adaptera, podczas gdy brakuje w nim wszystkich wad. Tak to nazywam dużą sprawą.

Oto przykładowy kod, tak aby pokazać pomysł:

public class Streams { 

    public interface EnhancedStream<T> 
     extends Stream<T> { 

     List<T> toList(); 

     Iterable<T> toIterable(); 
    } 

    @SuppressWarnings("unchecked") 
    public static <T> EnhancedStream<T> enhance(Stream<T> stream) { 

     return (EnhancedStream<T>) Proxy.newProxyInstance(
      EnhancedStream.class.getClassLoader(), 
      new Class<?>[] {EnhancedStream.class}, 
      (proxy, method, args) -> { 

      if ("toList".equals(method.getName())) { 

       return stream.collect(Collectors.toList()); 

      } else if ("toIterable".equals(method.getName())) { 

       return (Iterable<T>) stream::iterator; 

      } else { 
       // invoke method on the actual stream 
       return method.invoke(stream, args); 
      } 
     }); 
    } 

    public static void main(String[] args) { 

     Stream<Integer> stream1 = Stream.of(-2, 1, 2, -5). 
      filter(n -> n > 0).map(n -> n * n); 
     List<Integer> list = Streams.enhance(stream1).toList(); 
     System.out.println(list); // [1, 4] 

     Stream<Integer> stream2 = Stream.of(-2, 1, 2, -5). 
      filter(n -> n > 0).map(n -> n * n); 
     Iterable<Integer> iterable = Streams.enhance(stream2).toIterable(); 
     iterable.forEach(System.out::println); // 1 
               // 4 
    } 
} 

Pomysł polega na użyciu interfejs EnhancedStream który rozciąga Stream interfejs Java poprzez zdefiniowanie metod, które chcesz dodać. Następnie dynamiczny serwer proxy implementuje ten rozszerzony interfejs poprzez delegowanie oryginalnych metod Stream do dostosowywanego strumienia rzeczywistego, a po prostu zapewnia wbudowaną implementację nowych metod (tych, które nie są zdefiniowane w Stream).

Ten serwer proxy jest dostępny za pomocą statycznej metody, która przezroczyście wykonuje wszystkie operacje proxy.

Proszę zauważyć, że nie twierdzę, że jest to ostateczne rozwiązanie. Zamiast tego, jest to tylko przykład, który można znacznie poprawić, tj. Dla każdej metody z Stream, która zwraca inną Stream, można również zwrócić serwer proxy dla tego. Umożliwiłoby to uzyskanie łańcuchów EnhancedStream s (konieczne jest ponowne zdefiniowanie tych metod w interfejsie EnhancedStream, aby zwracały one współczynnik zastępczy EnhancedStream). Ponadto brakuje prawidłowej obsługi wyjątku, a także bardziej niezawodnego kodu, który decyduje o tym, czy delegować wykonanie metod do oryginalnego strumienia, czy też nie.

+0

To jest świetna odpowiedź! dzięki za udostępnienie. – asgs

10

Ostatnio napisałem małą bibliotekę o nazwie StreamEx która rozciąga się strumienie Java i zapewnia dokładną tej metody wśród wielu innych cech:

StreamEx.of(-2,1,2,-5) 
    .filter(n -> n > 0) 
    .map(n -> n * n) 
    .toList(); 

toset także() toCollection (dostawcy), łączenie(), groupingBy() i dostępne są tam inne metody skrótów.

1

Oto proste klasy pomocnika, który sprawia, że ​​to łatwiejsze:

public class Li { 
    public static <T> List<T> st(final Stream<T> stream) { 
     return stream.collect(Collectors.toList()); 
    } 
} 

Przykładowe zastosowanie:

List<String> lowered = Li.st(Stream.of("HELLO", "WORLD").map(String::toLowerCase)); 
Powiązane problemy