Así que me pregunto cuál es la mejor solución para el siguiente problema:
Tengo una lista de elementos (una clase personalizada) en una colección de Java ex
List<Item> itemList = ... item1,item2,item3 etc
Sin embargo, cada elemento de la colección tiene un par lógico correspondiente también en la colección (por lo que los pares no necesariamente se siguen entre sí por índice en la colección)
Tengo un método auxiliar como
Item calculateCorrectItem(Item item1, Item item2)
que puede devolver el correcto de un par en función de alguna lógica comercial (los detalles de eso no son relevantes)
Me gustaría reemplazar un elemento y su par en la colección, con el resultado del método anterior , de modo que cada 2 elementos de un par en la colección se reemplacen con el calculado en función de esos dos.
Algunos detalles: Podemos suponer que cada elemento tiene un solo par.
Cada artículo tiene la identificación del par como una propiedad, como
public class Item { private String id; private String pairId; the equal method is true when the ID of two items are the same. ...getters,setters }
Además, las referencias en la colección que quiero filtrar también existen en un caché global, donde cada elemento se puede recuperar fácilmente, como
globalCache.getItemById(String id)
Por lo tanto, se puede recuperar fácilmente una referencia de par real si se conoce la ID del par.
¿Cuál podría ser una solución elegante (tal vez utilizando Stream IPA)? Al final, la única expectativa es que la colección contenga un artículo de cada par, el orden no importa .
Con transmisiones, tendría que hacer esto usando acceso indexado:
List<Item> calculated = IntStream.range(0, itemList.size() / 2) .mapToObj(i -> calculateCorrectItem(itemList.get(2*i+0), itemList.get(2*i+1)) .collect(toList());
Si desea fusionar elementos en función de sus ID, puede agrupar los elementos por su ID:
itemList.stream() .collect(groupingBy(Item::getId)) // Yields a Map<IdType, List<Item>> .values() // Yields a Collection<List<Item>>, where each List<Item> contains items with the same id. .stream() .map(is -> /* invoke calculateCorrectItem, handling 1, 2 or more items in the list */) .collect(...)
Aquí hay otro enfoque que realiza una reducción mutable usando un mapa (puede usar un mapa hash si no es importante conservar el orden de ID de pares de la lista de fuentes):
Collection<Item> correctItems1 = itemList.stream().collect( LinkedHashMap<String, Item>::new, (map, item) -> map.merge(item.getPairId(), item, this::calculateCorrectItem), Map::putAll) .values(); List<Item> result = new ArrayList<>(correctItems1);
Supongo que el método calculateCorrectItem(Item item1, Item item2)
producirá el mismo resultado independientemente del orden de los argumentos y que los resultados duplicados deben eliminarse de la lista resultante.
List<Item> items = ... ; // obtain the items Map<String, Item> itemById = items.stream() .collect(Collectors.toMap(Item::getId, // relies on uniquness of Id Function.identity())); // set is used to alliminate duplicates since their order is not important Set<Item> itemSet = items.stream() .map(item-> pairById.containsKey(item.getPairId()) ? item : // if pair isn't present return the same item, othewise merge them calculateCorrectItem(item, pairById.get(item.getPairId()))) .collect(Collectors.toSet()); List<Item> result = new ArrayList<>(itemSet);