simplificar reglas online mapas mapa logicos karnaugh funciones ejercicios circuitos booleanas java java-8 java-stream

java - reglas - ¿Cómo crear una lista<T> a partir del mapa<K, V> y la lista<K> de teclas?



simplificar circuitos logicos (3)

Usando Java 8 lambdas, ¿cuál es la mejor manera de crear efectivamente una nueva List<T> dada una List<K> de claves posibles y un Map<K,V> ? Este es el escenario en el que se le proporciona una List de posibles claves de Map y se espera que genere una List<T> donde T es algún tipo que se construye en base a algún aspecto de V , los tipos de valor del mapa.

He explorado algunos y no me siento cómodo afirmando que una forma es mejor que otra (quizás con una excepción, ver código). Aclararé "mejor" como una combinación de claridad de código y eficiencia de tiempo de ejecución. Estos son los que se me ocurrieron. Estoy seguro de que alguien puede hacerlo mejor, que es un aspecto de esta pregunta. No me gusta el aspecto de filter de la mayoría, ya que significa la necesidad de crear estructuras intermedias y varias pasadas sobre la List nombres. En este momento, estoy optando por el Ejemplo 6 - un bucle ''ol''. ( NOTA: Algunos pensamientos crípticos están en los comentarios del código, especialmente "la necesidad de hacer referencia externamente ..." Esto significa que es externo a la lambda ) .

public class Java8Mapping { private final Map<String,Wongo> nameToWongoMap = new HashMap<>(); public Java8Mapping(){ List<String> names = Arrays.asList("abbey","normal","hans","delbrook"); List<String> types = Arrays.asList("crazy","boring","shocking","dead"); for(int i=0; i<names.size(); i++){ nameToWongoMap.put(names.get(i),new Wongo(names.get(i),types.get(i))); } } public static void main(String[] args) { System.out.println("in main"); Java8Mapping j = new Java8Mapping(); List<String> testNames = Arrays.asList("abbey", "froderick","igor"); System.out.println(j.getBongosExample1(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", "))); System.out.println(j.getBongosExample2(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", "))); System.out.println(j.getBongosExample3(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", "))); System.out.println(j.getBongosExample4(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", "))); System.out.println(j.getBongosExample5(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", "))); System.out.println(j.getBongosExample6(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", "))); } private static class Wongo{ String name; String type; public Wongo(String s, String t){name=s;type=t;} @Override public String toString(){return "Wongo{name="+name+", type="+type+"}";} } private static class Bongo{ Wongo wongo; public Bongo(Wongo w){wongo = w;} @Override public String toString(){ return "Bongo{wongo="+wongo+"}";} } // 1: Create a list externally and add items inside ''forEach''. // Needs to externally reference Map and List public List<Bongo> getBongosExample1(List<String> names){ final List<Bongo> listOne = new ArrayList<>(); names.forEach(s -> { Wongo w = nameToWongoMap.get(s); if(w != null) { listOne.add(new Bongo(nameToWongoMap.get(s))); } }); return listOne; } // 2: Use stream().map().collect() // Needs to externally reference Map public List<Bongo> getBongosExample2(List<String> names){ return names.stream() .filter(s -> nameToWongoMap.get(s) != null) .map(s -> new Bongo(nameToWongoMap.get(s))) .collect(Collectors.toList()); } // 3: Create custom Collector // Needs to externally reference Map public List<Bongo> getBongosExample3(List<String> names){ Function<List<Wongo>,List<Bongo>> finisher = list -> list.stream().map(Bongo::new).collect(Collectors.toList()); Collector<String,List<Wongo>,List<Bongo>> bongoCollector = Collector.of(ArrayList::new,getAccumulator(),getCombiner(),finisher, Characteristics.UNORDERED); return names.stream().collect(bongoCollector); } // example 3 helper code private BiConsumer<List<Wongo>,String> getAccumulator(){ return (list,string) -> { Wongo w = nameToWongoMap.get(string); if(w != null){ list.add(w); } }; } // example 3 helper code private BinaryOperator<List<Wongo>> getCombiner(){ return (l1,l2) -> { l1.addAll(l2); return l1; }; } // 4: Use internal Bongo creation facility public List<Bongo> getBongosExample4(List<String> names){ return names.stream().filter(s->nameToWongoMap.get(s) != null).map(s-> new Bongo(nameToWongoMap.get(s))).collect(Collectors.toList()); } // 5: Stream the Map EntrySet. This avoids referring to anything outside of the stream, // but bypasses the lookup benefit from Map. public List<Bongo> getBongosExample5(List<String> names){ return nameToWongoMap.entrySet().stream().filter(e->names.contains(e.getKey())).map(e -> new Bongo(e.getValue())).collect(Collectors.toList()); } // 6: Plain-ol-java loop public List<Bongo> getBongosExample6(List<String> names){ List<Bongo> bongos = new ArrayList<>(); for(String s : names){ Wongo w = nameToWongoMap.get(s); if(w != null){ bongos.add(new Bongo(w)); } } return bongos; } }


Si namesToWongoMap es una variable de instancia, realmente no puede evitar capturar un lambda.

Puedes limpiar el flujo dividiendo las operaciones un poco más:

return names.stream() .map(n -> namesToWongoMap.get(n)) .filter(w -> w != null) .map(w -> new Bongo(w)) .collect(toList());

return names.stream() .map(namesToWongoMap::get) .filter(Objects::nonNull) .map(Bongo::new) .collect(toList());

De esa manera no llamas al doble.

Esto es muy parecido al bucle for , excepto que, por ejemplo, en teoría podría namesToWongoMap si no se puede mutar al mismo tiempo namesToWongoMap .

No me gusta el aspecto de filter de la mayoría, ya que significa la necesidad de crear estructuras intermedias y varias pasadas sobre la List nombres.

No hay estructuras intermedias y solo hay una pasada sobre la List . Un canal de flujo dice "para cada elemento ... haga esta secuencia de operaciones". Cada elemento se visita una vez y se aplica la tubería.

Aquí hay algunas citas relevantes de la descripción del paquete java.util.stream :

Una secuencia no es una estructura de datos que almacena elementos; en su lugar, transporta elementos desde una fuente, como una estructura de datos, una matriz, una función de generador o un canal de E / S, a través de una tubería de operaciones computacionales.

El procesamiento de flujos perezosamente permite eficiencias significativas; en una tubería como el ejemplo de filtro-suma-mapa anterior, el filtrado, la asignación y la suma se pueden fusionar en una sola pasada en los datos, con un estado intermedio mínimo.


Un enfoque que no vi es retainAll

public List<Bongo> getBongos(List<String> names) { Map<String, Wongo> copy = new HashMap<>(nameToWongoMap); copy.keySet().retainAll(names); return copy.values().stream().map(Bongo::new).collect( Collectors.toList()); }

El mapa extra es un impacto de rendimiento mínimo, ya que solo está copiando punteros a objetos, no a los objetos en sí.


Creo que la respuesta de Radiodef casi lo clavó. La solución dada allí:

return names.stream() .map(namesToWongoMap::get) .filter(Objects::nonNull) .map(Bongo::new) .collect(toList());

Es probablemente lo mejor que se puede hacer en Java 8.

Aunque sí quería mencionar una pequeña arruga en esto. La llamada Map.get devuelve null si el nombre no está presente en el mapa, y esto se filtra posteriormente. No hay nada de malo en esto per se , aunque hace que la semántica null-mean-not-present se convierta en la estructura de la tubería.

En cierto sentido, desearíamos una operación de canalización de asignador que tenga la opción de devolver cero o uno de los elementos. Una forma de hacer esto con streams es con flatMap . La función flatmapper puede devolver un número arbitrario de elementos al flujo, pero en este caso queremos solo uno o uno. Aquí está cómo hacer eso:

return names.stream() .flatMap(name -> { Wongo w = nameToWongoMap.get(name); return w == null ? Stream.empty() : Stream.of(w); }) .map(Bongo::new) .collect(toList());

Admito que esto es bastante torpe y por eso no recomendaría hacer esto. Un enfoque ligeramente mejor pero algo oscuro es el siguiente:

return names.stream() .flatMap(name -> Optional.ofNullable(nameToWongoMap.get(name)) .map(Stream::of).orElseGet(Stream::empty)) .map(Bongo::new) .collect(toList());

pero todavía no estoy seguro de recomendar esto como está.

flatMap embargo, el uso de flatMap apunta a otro enfoque. Si tiene una política más complicada de cómo tratar el caso no presente, podría refactorizar esto en una función auxiliar que devuelva una Corriente que contenga el resultado o una Corriente vacía si no hay un resultado.

Finalmente, JDK 9, aún en desarrollo al momento de escribir este artículo, ha agregado Stream.ofNullable que es útil en estas situaciones:

return names.stream() .flatMap(name -> Stream.ofNullable(nameToWongoMap.get(name))) .map(Bongo::new) .collect(toList());

Además, JDK 9 también ha agregado Optional.stream que crea una secuencia cero o uno a partir de una Optional . Esto es útil en los casos en los que desea llamar a una función de devolución opcional desde flatMap . Ver esta respuesta y esta respuesta para más discusión.