2015-03-24 12 views
8

Zasadniczo szukam sposobu, aby uniknąć pracy zJakikolwiek sposób przesyłać strumieniowo mapę w stylu "(k, v)" zamiast pracować z (wpis)?

entry -> entry.getValue 

i

entry -> entry.getKey 

podobny do tego, co Map.forEach() robi.

Gdybym tylko mógł dostać się w drodze do pracy jako map.stream().filter((k,v) ->) ... i tak dalej

Wydaje się, że interfejs jest nazywany BiConsumer. Być może z konwerterem do BiConsumer lub Stream.generate() w jakiś sposób

+0

Czy możesz być bardziej konkretny? Prawdopodobnie dodaj przykład. –

+1

Nie ma sposobu, jak łatwo można dowiedzieć się z API 'Map'. Ale co jest nie tak z podejściem 'entrySet()'? –

+0

Muszę wykonywać i.getEntry i i.getKey wiele razy – Whimusical

Odpowiedz

10

Ponieważ jest to powtarzające się pytanie, wrzucę pełne rozwiązanie do ringu. Jest to typ PairStream, który domyślnie jest prostym opakowaniem dla zwykłych Stream (choć możliwe są alternatywy 0123).

skupia się na zapewnieniu dogodne operacje pośrednie i te operacje końcowe, które nie może być łatwo przeprowadzone poprzez wywołanie jednej z metod keys(), values() lub entries() powrotu do konwencjonalnego jednostronnego elementu Stream i łańcucha to operacja terminala. Na przykład: PairStream.from(map).filterValue(predicate).keys().findAny() jest prostym sposobem uzyskania klucza, którego odwzorowana wartość pasuje do predykatu. filterValue to wygodna operacja pośrednia, a następnie keys powraca do zwykłego Stream, umożliwiając dowolne działanie terminala dla kluczy.

Niektóre przykłady

Map<String,Integer> m=new HashMap<>(); 
    m.put("foo", 5); 
    m.put("bar", 7); 
    m.put("baz", 42); 
    // {b=49, f=5} 
    Map<Character,Integer> m2=PairStream.from(m) 
     .mapKey(s->s.charAt(0)) 
     .toMap(Integer::sum); 

    // foo bar 
    String str=PairStream.from(m) 
     .filterValue(i->i<30) 
     .keys().sorted(Comparator.reverseOrder()) 
     .collect(Collectors.joining(" ")); 

Map<String,Integer> map=new HashMap<>(); 
    map.put("muhv~", 26); 
    map.put("kfool", 3); 
    String str = PairStream.from(map) 
     .sortedByValue(Comparator.naturalOrder()) 
     .flatMapToInt((s,i)->s.codePoints().map(c->c^i)) 
     .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) 
     .toString(); 

Oto pełna klasa (nie testowałem wszystkie operacje, jednak większość z nich jest prosta):

import java.util.*; 
import java.util.function.*; 
import java.util.stream.*; 

public interface PairStream<K,V> { 
    static <K,V> PairStream<K,V> from(Map<K,V> map) { 
     return from(map.entrySet().stream()); 
    } 
    static <K,V> PairStream<K,V> from(Stream<Map.Entry<K,V>> s) { 
     return()->s; 
    } 
    static <K,V> PairStream<K,V> from(Stream<K> s, Function<? super K, ? extends V> f) { 
     return()->s.map(k->new AbstractMap.SimpleImmutableEntry<>(k, f.apply(k))); 
    } 

    default PairStream<K,V> distinct() { 
     return from(entries().distinct()); 
    } 
    default PairStream<K,V> peek(BiConsumer<? super K, ? super V> action) { 
     return from(entries().peek(e->action.accept(e.getKey(), e.getValue()))); 
    } 
    default PairStream<K,V> skip(long n) { 
     return from(entries().skip(n)); 
    } 
    default PairStream<K,V> limit(long maxSize) { 
     return from(entries().limit(maxSize)); 
    } 
    default PairStream<K,V> filterKey(Predicate<? super K> mapper) { 
     return from(entries().filter(e->mapper.test(e.getKey()))); 
    } 
    default PairStream<K,V> filterValue(Predicate<? super V> mapper) { 
     return from(entries().filter(e->mapper.test(e.getValue()))); 
    } 
    default PairStream<K,V> filter(BiPredicate<? super K, ? super V> mapper) { 
     return from(entries().filter(e->mapper.test(e.getKey(), e.getValue()))); 
    } 
    default <R> PairStream<R,V> mapKey(Function<? super K,? extends R> mapper) { 
     return from(entries().map(e->new AbstractMap.SimpleImmutableEntry<>(
      mapper.apply(e.getKey()), e.getValue() 
     ))); 
    } 
    default <R> PairStream<K,R> mapValue(Function<? super V,? extends R> mapper) { 
     return from(entries().map(e->new AbstractMap.SimpleImmutableEntry<>(
      e.getKey(), mapper.apply(e.getValue()) 
     ))); 
    } 
    default <R> Stream<R> map(BiFunction<? super K, ? super V,? extends R> mapper) { 
     return entries().map(e->mapper.apply(e.getKey(), e.getValue())); 
    } 
    default DoubleStream mapToDouble(ToDoubleBiFunction<? super K, ? super V> mapper) { 
     return entries().mapToDouble(e->mapper.applyAsDouble(e.getKey(), e.getValue())); 
    } 
    default IntStream mapToInt(ToIntBiFunction<? super K, ? super V> mapper) { 
     return entries().mapToInt(e->mapper.applyAsInt(e.getKey(), e.getValue())); 
    } 
    default LongStream mapToLong(ToLongBiFunction<? super K, ? super V> mapper) { 
     return entries().mapToLong(e->mapper.applyAsLong(e.getKey(), e.getValue())); 
    } 
    default <RK,RV> PairStream<RK,RV> flatMap(
      BiFunction<? super K, ? super V,? extends PairStream<RK,RV>> mapper) { 
     return from(entries().flatMap(
      e->mapper.apply(e.getKey(), e.getValue()).entries())); 
    } 
    default <R> Stream<R> flatMapToObj(
      BiFunction<? super K, ? super V,? extends Stream<R>> mapper) { 
     return entries().flatMap(e->mapper.apply(e.getKey(), e.getValue())); 
    } 
    default DoubleStream flatMapToDouble(
      BiFunction<? super K, ? super V,? extends DoubleStream> mapper) { 
     return entries().flatMapToDouble(e->mapper.apply(e.getKey(), e.getValue())); 
    } 
    default IntStream flatMapToInt(
      BiFunction<? super K, ? super V,? extends IntStream> mapper) { 
     return entries().flatMapToInt(e->mapper.apply(e.getKey(), e.getValue())); 
    } 
    default LongStream flatMapToLong(
      BiFunction<? super K, ? super V,? extends LongStream> mapper) { 
     return entries().flatMapToLong(e->mapper.apply(e.getKey(), e.getValue())); 
    } 
    default PairStream<K,V> sortedByKey(Comparator<? super K> comparator) { 
     return from(entries().sorted(Map.Entry.comparingByKey(comparator))); 
    } 
    default PairStream<K,V> sortedByValue(Comparator<? super V> comparator) { 
     return from(entries().sorted(Map.Entry.comparingByValue(comparator))); 
    } 

    default boolean allMatch(BiPredicate<? super K,? super V> predicate) { 
     return entries().allMatch(e->predicate.test(e.getKey(), e.getValue())); 
    } 
    default boolean anyMatch(BiPredicate<? super K,? super V> predicate) { 
     return entries().anyMatch(e->predicate.test(e.getKey(), e.getValue())); 
    } 
    default boolean noneMatch(BiPredicate<? super K,? super V> predicate) { 
     return entries().noneMatch(e->predicate.test(e.getKey(), e.getValue())); 
    } 
    default long count() { 
     return entries().count(); 
    } 

    Stream<Map.Entry<K,V>> entries(); 
    default Stream<K> keys() { 
     return entries().map(Map.Entry::getKey); 
    } 
    default Stream<V> values() { 
     return entries().map(Map.Entry::getValue); 
    } 
    default Optional<Map.Entry<K,V>> maxByKey(Comparator<? super K> comparator) { 
     return entries().max(Map.Entry.comparingByKey(comparator)); 
    } 
    default Optional<Map.Entry<K,V>> maxByValue(Comparator<? super V> comparator) { 
     return entries().max(Map.Entry.comparingByValue(comparator)); 
    } 
    default Optional<Map.Entry<K,V>> minByKey(Comparator<? super K> comparator) { 
     return entries().min(Map.Entry.comparingByKey(comparator)); 
    } 
    default Optional<Map.Entry<K,V>> minByValue(Comparator<? super V> comparator) { 
     return entries().min(Map.Entry.comparingByValue(comparator)); 
    } 
    default void forEach(BiConsumer<? super K, ? super V> action) { 
     entries().forEach(e->action.accept(e.getKey(), e.getValue())); 
    } 
    default void forEachOrdered(BiConsumer<? super K, ? super V> action) { 
     entries().forEachOrdered(e->action.accept(e.getKey(), e.getValue())); 
    } 

    default Map<K,V> toMap() { 
     return entries().collect(
      Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 
    } 
    default Map<K,V> toMap(BinaryOperator<V> valAccum) { 
     return entries().collect(
      Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, valAccum)); 
    } 
} 
6

Nie, nie ma sposobu, aby to zrobić; Stream działa tylko z jednym typem elementu. Nie można obejść podejścia getKey i getValue.

(na żywo na klawiszach i nazywając get rzeczywistości nie wydaje się być lepsza od twojej perspektywy, a to może być ściśle mniej wydajne.)

9

To trochę niefortunne, że głównym sposobem przetwarzać mapowanie strumieniami to przesyłanie strumieniowe wpisów na mapie. Oznacza to, że musisz wyodrębnić klucze i wartości wpisów. Jest to trochę gadatliwe, ale wcale nie jest tak źle. Mimo to rozsądne może być rozważenie pewnych metod pomocniczych, które będą dostosowywać BiPredicate lub BiFunction, aby mogły być używane w etapach strumienia wpisów mapowych. Wyglądają tak:

static <K,V> Predicate<Map.Entry<K,V>> p(BiPredicate<? super K, ? super V> bip) { 
    return entry -> bip.test(entry.getKey(), entry.getValue()); 
} 

static <K,V,R> Function<Map.Entry<K,V>,R> m(BiFunction<? super K, ? super V, R> bif) { 
    return entry -> bif.apply(entry.getKey(), entry.getValue()); 
} 

Po uzyskaniu tych można użyć ich do uproszczenia (?) Strumienia wprowadzania mapy. Załóżmy, że masz Map<String,Integer> i chcesz wybrać pozycje, w których długość klucza łańcucha jest większa niż liczba całkowita, a następnie sformatuj klucz i wartość w łańcuch. Tradycyjnie chcesz to zrobić:

map.entrySet().stream() 
        .filter(e -> e.getKey().length() > e.getValue()) 
        .map(e -> e.getKey() + ":" + e.getValue()) 
        .forEach(System.out::println); 

Dzięki funkcji pomocniczych powyżej, można przepisać ten rurociąg jak:

map.entrySet().stream() 
        .filter(p((k, v) -> k.length() > v)) 
        .map(m((k, v) -> k + ":" + v)) 
        .forEach(System.out::println); 

OK, można zaoszczędzić kilka znaków. Czy warto?

+3

Z pewnością warto, ponieważ parametry lambda można nazwać inaczej niż "klucz" i "wartość", co prowadzi do lepszego dokumentowania samego siebie. –