简体   繁体   中英

How to remove null or empty list from an “unmodifiable” map

I have an unmodifiable map ( Map<String, Object> ). This contains a key-value pair, where the value should be a List, but for a specific key the value is an empty List.

How can I remove that empty list from the map and return?

I have done it using predicate which checks if the value is an instance of collection and then check if Collections.isNotEmpty(..).

I want to know is there any better approach than this? (In Java 8)

public class StudentTest {

    Predicate<Object> isCollectionNotEmpty = input -> {
        if (input instanceof Collection) {
            return CommonCollectionUtils.isNotEmpty((Collection<?>)input);
        }
        return true;
    };
    Predicate<Object> isObjectNotNull = input -> Objects.nonNull(input);

    public static void main(String[] args) {

        StudentTest s = new StudentTest();
        final Map<String, Object> map1 = new HashMap<>();
        map1.put("student_batch_size", 200);
        map1.put("student_avg_height", 172);
        map1.put("Student_names", Collections.emptyList());

        final Map<String, Object> finalMap = s.getFinalMap(Collections.unmodifiableMap(map1));

        System.out.println(finalMap);
    }

    private Map<String, Object> getFinalMap(final Map<String, Object> inputMap) {
        final Map<String, Object> resultMap = new HashMap<>();
        //inputMap.values().remove(Collections.emptyList());
        resultMap.putAll(inputMap.entrySet().stream()
            .filter(entry -> isObjectNotNull.and(isCollectionNotEmpty).test(entry.getValue()))
        .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
        return resultMap;
    }

}

Expected Output:

{student_avg_height=172, student_batch_size=200}

Collection#removeIf might be a good option to consider.

private Map<String, Object> getFinalMap(final Map<String, Object> inputMap) {
    final Map<String, Object> resultMap = new HashMap<>(inputMap);
    resultMap.entrySet().removeIf(e -> e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty());

    return resultMap;
}

If a stream-based approach is more appealing to you

private Map<String, Object> getFinalMap(final Map<String, Object> inputMap) {
    return inputMap.entrySet()
        .stream()
        .filter(e -> !(e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

UPDATE

If you want to filter out null objects regardless of their type, here are slight changes to the methods mentioned above

1) removeIf

resultMap.entrySet()
    .removeIf(e -> Objects.isNull(e.getValue()) || 
                   (e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty());

2) stream-based

.filter(e -> Objects.nonNull(e.getValue()))
.filter(e -> !(e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty()))

I think your approach of using Object as a value in the Map is questionable. You don't fully control type safety. I'd consider replacing the student Map with an object reflecting single student batch properties.

class StudentBatch {
   int size;
   int avgHeight;
   Collection<String> names;

   boolean hasValidNames() {
       return names != null && !names.isEmpty();
   }
}

As you see I've put the hasValidNames predicate to the object which makes it reusable and allows to simplify the condition you're trying to optimize.

  List<StudentBatch> batchList = new ArrayList<>();
  batchList.stream().filter(StudentBatch::hasValidNames).collect(Collectors.toList());

instanceof checks the no nullity too, so no need to make it explicitly and you don't need to create a new HashMap even if the Map that you stream is not modifiable as the stream doesn't modify it directly but creates a new Map :

final Map<String, Object> finalMap =
     map
    .entry()
    .stream()
    .filter(e-> !isEmptyCollection(e))
    .collect(toMap(Entry::getKey, Entry::getValue);


public void boolean isEmptyCollection(Entry<String, Object> entry){     
    return entry.getValue() instanceof Collection && ((Collection<?>) entry.getValue()).isEmpty();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM