Processing a list of cards using Java 8 streams

How can I simplify this code into a single lambda expression? The idea is that there is a list of cards and I would like to create a new list of cards using a filter on the key. In this example, I want to redirect it so that it only stores the "x" and "z" keys.

    Map<String, String> m0 = new LinkedHashMap<>();
    m0.put("x", "123");
    m0.put("y", "456");
    m0.put("z", "789");

    Map<String, String> m1 = new LinkedHashMap<>();
    m1.put("x", "000");
    m1.put("y", "111");
    m1.put("z", "222");

    List<Map> l = new ArrayList<>(Arrays.asList(m0, m1));
    List<Map> tx = new ArrayList<>();
    for(Map<String, String> m : l) {
        Map<String, String> filtered = m.entrySet()
                .stream()
                .filter(map -> map.getKey().equals("x") || map.getKey().equals("z"))
                .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
        tx.add(filtered);
    }
    System.err.println("l: " + l);
    System.err.println("tx: " + tx);

      

Output:

    l: [{x=123, y=456, z=789}, {x=000, y=111, z=222}]
    tx: [{x=123, z=789}, {x=000, z=222}]

      

+3


source to share


5 answers


You can of course convert the whole operation to a single Stream operation.

// no need to copy a List (result of Array.asList) to an ArrayList, by the way
List<Map<String, String>> l = Arrays.asList(m0, m1);

List<Map<String, String>> tx = l.stream().map(m -> m.entrySet().stream()
        .filter(map -> map.getKey().equals("x") || map.getKey().equals("z"))
        .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())))
    .collect(Collectors.toList());

      

But note that streaming Map

and filtering is a linear time complexity operation, as it checks every key of every card against a filter, while you only have a very small number of real keys that you want to store. Thus, it is much easier and more efficient (for large maps) to use



List<Map<String, String>> tx = l.stream()
    .map(m -> Stream.of("x", "y")
                    .filter(m::containsKey).collect(Collectors.toMap(key->key, m::get)))
    .collect(Collectors.toList());

      

which will only do four searches per map. If that bothers you, you can even reduce it to two searches, however the constant factor doesn't matter for the total time complexity, which will be constant if the map has constant time searches, for example HashMap

. Even for maps with O(log(n))

complex search times, for example TreeMap

, this will be more efficient than linear scanning if the maps are more than three displays of the example code.

+8


source


You can try something like this:

List<Map<String, String>> l = Arrays.asList(m0, m1);

l.forEach(map -> {
    map.entrySet().removeIf(e -> !e.getKey().equals("x") && !e.getKey().equals("z"));
});

      

It just removes all mappings in each Map<String, String>

one if the input key is not x

or z

.



Edit: you should be using the Radiodef equivalent, but a shorter method!

List<Map<String, String>> l = Arrays.asList(m0, m1);

l.forEach(map -> map.keySet().retainAll(Arrays.asList("x", "z"));

      

+2


source


Try the following code (I declared a list for desiredKeys

):

public class Main {
    public static void main(String[] args) {
        Map<String, String> m0 = new HashMap<>();
        m0.put("x", "123");
        m0.put("y", "456");
        m0.put("z", "789");

        Map<String, String> m1 = new HashMap<>();
        m1.put("x", "000");
        m1.put("y", "111");
        m1.put("z", "222");

        List<Map<String, String>> l = new ArrayList<>(Arrays.asList(m0, m1));

        List<String> desiredKeys = Lists.newArrayList("x", "z");

        List<Map<String, String>> transformed = l.stream().map(map -> map.entrySet().stream()
                .filter(e -> desiredKeys.stream().anyMatch(k -> k.equals(e.getKey())))
                .collect(Collectors.toMap(e -> e.getKey(), p -> p.getValue()))).filter(m -> !m.isEmpty()).collect(Collectors.toList());

        System.err.println(l);
        System.err.println(transformed);
    }
}

      

+1


source


Try this, it should work:

Map<String, String> m0 = new HashMap<>();
        m0.put("x", "123");
        m0.put("y", "456");
        m0.put("z", "789");

        Map<String, String> m1 = new HashMap<>();
        m1.put("x", "000");
        m1.put("y", "111");
        m0.put("z", "222");

        List<Map> l = new ArrayList<>(Arrays.asList(m0, m1));
        List<Map> transformed = new ArrayList<Map>() ;
        l.stream().map(map -> {
            Set<String> keys = map.keySet() ;
            Map<String, String> newMap = new HashMap<>();
            for(String key : keys){
               if(key.equals("x")|| key.equals("z")) 
                    newMap.put(key, map.get(key).toString()) ;
            }
            return newMap ;
        }).forEach(map -> transformed.add(map)); 

        System.out.println(transformed);

      

+1


source


What about:

 tx = StreamEx.of(l)
              .map(m -> EntryStream.of(m).filterKeys(k -> k.equals("x") || k.equals("z")).toMap())
              .toList();

      

StreamEx

+1


source







All Articles