Jeśli dodać importu statycznych dla Stream.concat i Stream.of , pierwszy przykład można zapisać w następujący sposób:
Stream<Foo> stream = concat(stream1, concat(stream2, of(element)));
Importowanie metod statycznych z nazwami rodzajowymi może spowodować, że kod stanie się trudny do odczytania i utrzymania ( zanieczyszczenie przestrzeni nazw ). Dlatego lepiej byłoby utworzyć własne metody statyczne o bardziej znaczących nazwach. Jednak dla demonstracji pozostanę przy tej nazwie.
public static <T> Stream<T> concat(Stream<? extends T> lhs, Stream<? extends T> rhs) {
return Stream.concat(lhs, rhs);
}
public static <T> Stream<T> concat(Stream<? extends T> lhs, T rhs) {
return Stream.concat(lhs, Stream.of(rhs));
}
W przypadku tych dwóch metod statycznych (opcjonalnie w połączeniu ze statycznymi importami) oba przykłady można zapisać w następujący sposób:
Stream<Foo> stream = concat(stream1, concat(stream2, element));
Stream<Foo> stream = concat(
concat(stream1.filter(x -> x!=0), stream2).filter(x -> x!=1),
element)
.filter(x -> x!=2);
Kod jest teraz znacznie krótszy. Zgadzam się jednak, że czytelność się nie poprawiła. Więc mam inne rozwiązanie.
W wielu sytuacjach Collectors można wykorzystać do rozszerzenia funkcjonalności strumieni. Mając dwa Kolektory na dole, dwa przykłady można zapisać w następujący sposób:
Stream<Foo> stream = stream1.collect(concat(stream2)).collect(concat(element));
Stream<Foo> stream = stream1
.filter(x -> x!=0)
.collect(concat(stream2))
.filter(x -> x!=1)
.collect(concat(element))
.filter(x -> x!=2);
Jedyna różnica między żądaną składni i składni powyższego jest to, że trzeba wymienić concat (...) z honor (concat (...)) . Dwie metody statyczne można zaimplementować w następujący sposób (opcjonalnie używane w połączeniu z importami statycznymi):
private static <T,A,R,S> Collector<T,?,S> combine(Collector<T,A,R> collector, Function<? super R, ? extends S> function) {
return Collector.of(
collector.supplier(),
collector.accumulator(),
collector.combiner(),
collector.finisher().andThen(function));
}
public static <T> Collector<T,?,Stream<T>> concat(Stream<? extends T> other) {
return combine(Collectors.toList(),
list -> Stream.concat(list.stream(), other));
}
public static <T> Collector<T,?,Stream<T>> concat(T element) {
return concat(Stream.of(element));
}
Oczywiście rozwiązanie to ma wadę, o której należy wspomnieć. zbieranie to operacja końcowa, która zużywa wszystkie elementy strumienia. Ponadto concat kolektorów tworzy pośrednią ArrayList za każdym razem, gdy jest ona używana w łańcuchu. Obie operacje mogą mieć znaczący wpływ na zachowanie programu. Jeśli jednak czytelność jest ważniejsza niż wydajność , może to być bardzo pomocne podejście.