Search code examples
javajava-8guavacollectors

Process list stream and collect into map/ImmutableMap with only non null values


How to process a list of string and collec it into Map or Immutable map only for those whose value is present

String anotherParam = "xyz";
Map.Builder<String,String> resultMap = ImmutableMap.builder(..)

 listOfItems.stream()
            .filter(Objects::nonNull)
            .distinct()
            .forEach(
                    item -> {
                        final Optional<String> result =     
    getProcessedItem(item,anotherParam);

                        if (result.isPresent()) {

    resultMap.put(item, result.get());
                        }
                    });
        return resultMap.build();

Please tell, is there a better way to achieve this via collect?


Solution

  • If you have access to Apache Commons library you can make use of Pair.class

    Map<String, String> resultMap = ImmutableMap.copyof(listOfItems()
        .stream()
        .filter(Objects::nonNull)
        .distinct()
        .map(it -> Pair.of(it, getProcessedItem(it,anotherParam))
        .filter(pair -> pair.getValue().isPresent())
        .collect(toMap(Pair::getKey, pair -> pair.getValue().get())))
    

    But it's a good practice to make special data classes which describes your mapping item->result more specificly

    Here is an example, create class like this:

    static class ItemResult(){
        public final String item;
        public final Optional<String> result;
    
        public ItemResult(String item, Optional<String> result){
            this.item = item;
            this.result = result;
        }
    
        public boolean isPresent(){
            return this.result.isPresent();
        }
    
        public String getResult(){
            return result.get();
        }
    }
    

    And use it like that:

    Map<String, String> resultMap = ImmutableMap.copyOf(listOfItems()
        .stream()
        .filter(Objects::nonNull)
        .distinct()
        .map(it -> new ItemResult(it, getProcessedItem(it,anotherParam))
        .filter(ItemResult::isPresent)
        .collect(toMap(ItemResult::item, ItemResult::getResult)))
    

    You can read here why Google gave up the idea of tuples and pairs and don't use them in most cases

    If after all you don't want to use any other class you can leverage api of the Optional:

    Map.Builder<String,String> resultMap = ImmutableMap.builder(..)
    
    listOfItems.stream()
            .filter(Objects::nonNull)
            .distinct()
            .forEach(item -> getProcessedItem(item,anotherParam)
                             .ifPresent(result -> resultMap.put(item result));
        return resultMap.build();