2015-04-08 5 views
6

Ho un paio di Map oggetti che sono calettati dallo stesso tipo K con i valori diverso digitati V1...VN, che per lo scopo di questa domanda non condividono un supertipo *:Come filtrare e raccogliere in modo efficiente una mappa risultante con valori di mappe derivate in modo diverso da alcune mappe?

Map<K, V1> kv1 
Map<K, V2> kv2 
Map<K, V3> kv3 
... 
Map<K, VN> kvN 

Ho bisogno di creare una mappa risultante di tipo Map<K, V>, filtrando ognuna di queste mappe in modo diverso, quindi utilizzare un "mapper valori" per mappare i valori V1...VN a valori V comunemente tipizzati (ovvero uno Function<? super Entry<K, VN>, ? extends V>) su queste mappe. Come tale, ho il seguente metodo static di supporto per eseguire i primi due passaggi:

public static <K, VN, V> Map<K, V> filterAndMapValue(final Map<K, VN> map, 
     final Predicate<? super Entry<K, VN>> predicate, 
     final Function<? super Entry<K, VN>, ? extends V> mapper) { 
    return map.entrySet().stream().filter(predicate) 
      .collect(Collectors.toMap(Entry::getKey, mapper)); 
} 

miei casi d'uso attuali lo rendono sicuro supporre che solo dopo il filtraggio su ogni mappa mi darà chiavi distinte per la oggetto finale Map (ci possono essere le stesse chiavi utilizzate in ogni mappa), ma nel caso in cui non valga per il futuro, so che posso fornire un'espressione supplementare mergeFunction a Collectors.toMap(Function, Function, BinaryOperator) per gestirlo correttamente.

Il codice finale ora si legge qualcosa di simile al seguente:

Map<K,V> result = filterAndMapValue(kv1, predicateForKV1, mapV1toV); 
result.putAll(filterAndMapValue(kv2, predicateForKV2, mapV2toV)); 
result.putAll(filterAndMapValue(kv2, predicateForKV3, mapV3toV)); 
... 
result.putAll(filterAndMapValue(kvN, predicateForKVN, mapVNtoV)); 
// do something with result 

Domanda: C'è un modo più efficiente di fare questo? Questo sembra un altro caso di riduzione di materiale (mappe filtrate) in una raccolta finale (Map) che richiede diverse chiamate di riduzione (la parte di mappatura del valore), e non sono sicuro se mi sto avvicinando a questo nel modo corretto oppure no.

* - In tal caso, suppongo che V1...VN possa implementare un metodo principale, ad esempio V convertToV(Object... possiblyAdditionalArgumentsToPerformTheConversion), in modo che il mio problema si riduca a solo applicare diverse forme di filtro per mappe diverse. Se esiste anche una soluzione più semplice data questa supposizione alternativa, sentitevi liberi di menzionarlo anche.

+0

Come creare un oggetto per contenere tutti i valori V1 ... VN e utilizzare solo una mappa? – BobTheBuilder

+0

@BobTheBuilder intendi raccogliere tutti i valori delle mappe all'inizio? La mappatura dei valori viene eseguita in modo diverso per tipi diversi, quindi alla fine devo anche conoscere i mapping delle chiavi per i valori di 'V' appena creati ... Ho anche fornito una modifica per indicare che le" uniche chiavi risultanti 'è applicabile * solo * dopo * il filtro sulle mappe, quindi le mappe originali possono avere le stesse chiavi. Non sarei in grado di fare una 'Mappa ' in questo senso. Spero che questo risponda alla tua domanda. –

+0

Ciò che intendo è creare una classe MyValues ​​con campi V1..VN.Se si utilizza tale classe, MyValues ​​contiene tutti i valori per ogni chiave in un oggetto ed è più facile da gestire e mantenere – BobTheBuilder

risposta

2

Se si cambia il metodo per

public static <K, VN, V> Stream<Entry<K, V>> filterAndMapValue(Map<K, VN> map, 
    Predicate<? super Entry<K, VN>> predicate, 
    Function<? super Entry<K, VN>, ? extends V> mapper) { 

    return map.entrySet().stream().filter(predicate) 
       .map(e->new AbstractMap.SimpleEntry<>(e.getKey(), mapper.apply(e))); 
} 

è possibile eseguire l'operazione come una singola Stream un'operazione simile:

Stream.of(filterAndMapValue(kv1, predicateForKV1, mapV1toV), 
      filterAndMapValue(kv2, predicateForKV2, mapV2toV), 
      filterAndMapValue(kv3, predicateForKV3, mapV3toV), 
      …) 
     .flatMap(Function.identity()) 
     .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 

Si noti che per un piccolo numero di mappe di ingresso, è possibile utilizzare Stream.concat ma man mano che il numero aumenta, l'approccio mostrato sopra è preferibile.

Non mi aspetto un notevole aumento delle prestazioni, ma questo approccio verificherà l'ipotesi che non vi siano chiavi duplicate nelle voci rimanenti.

+0

Grazie per la risposta! –

Problemi correlati