0

I have a hashmap in Java with a string key and a HashSet value. The hashset may contain many PlacementBundles inside it.

public Map<String, Set<PlacementBundle>> placementByConcept;

I am trying to remove the value from the HashSet while iterating the map which matches a specific condition.

I tried the below code but cannot remove the matching element from the HashSet.

placementByConcept.entrySet()
                  .stream()
                  .map(e -> e.getValue()
                             .removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));
Arun Sudhakaran
  • 2,167
  • 4
  • 27
  • 52
Sathesh
  • 486
  • 2
  • 12
  • 29

3 Answers3

3

you can use forEach:

placementByConcept.entrySet().forEach(e -> e.getValue().removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));
Pp88
  • 830
  • 6
  • 19
  • Yes, adding forEach fixed it. Thanks – Sathesh Jul 27 '22 at 05:22
  • 4
    Or even simpler `placementByConcept.values().forEach(set -> set.removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));` – Holger Jul 27 '22 at 08:08
  • 1
    This answer could be improved by explaining why this helps - i.e. the lazy processing of stream pipelines that is only triggered by terminal operations like forEach, but not by intermediate operations like map (In this example here, the stream is eliminated completely). – Hulk Jul 27 '22 at 10:53
  • Any idea how to extract the matching elements instead of removing them from the Set ? – Sathesh Jul 27 '22 at 23:14
  • @Pp88 s.getBeatObjectiveId() does not exist since s is an hashSet in my case. – Sathesh Jul 28 '22 at 04:31
  • 1
    Sorry my bad try `placementByConcept.values().stream().flatMap(Set::stream).filter(s -> s.getBeatObjectiveId().equals("non-scored")).collect(Collectors.toSet());` – Pp88 Jul 28 '22 at 04:57
1

In your case Set<PlacementBundle> is an immutable collection. You can't remove an element from it.

Thank you Holger for pointing out the assumption I made which may not be true for the asked question.

If Set is immutable collection and you use foreach as suggested in the accepted answer, you will get UnsupportedOperationException

import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
public class Test {

    public static void main(String[] args) {
        Map<String, Set<PlacementBundle>> placementByConcept = new HashMap<>();
        placementByConcept.put("concept1", Set.of(
                PlacementBundle.builder().beatObjectiveId("scored").build(),
                PlacementBundle.builder().beatObjectiveId("non-scored").build())
        );
        placementByConcept.put("concept2", Set.of(
                PlacementBundle.builder().beatObjectiveId("scored").build(),
                PlacementBundle.builder().beatObjectiveId("non-scored").build())
        );

        log.info("Original: {}", placementByConcept);

        /* This won't give any exception, neither will remove the entries */
        placementByConcept.entrySet()
                          .stream()
                          .map(e -> e.getValue()
                                     .removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));
        log.info("Does not work: {}", placementByConcept);

        /* This will give you the exception UnsupportedOperationException */
        // placementByConcept.entrySet().forEach(e -> e.getValue().removeIf(s -> s.getBeatObjectiveId().equals("non-scored")));

        /* This is one of the correct way */
        for (Map.Entry<String, Set<PlacementBundle>> entry : placementByConcept.entrySet()) {
            var filtered = entry.getValue().stream()
                    .filter(placementBundle -> !placementBundle.getBeatObjectiveId().equals("non-scored"))
                    .collect(Collectors.toUnmodifiableSet());
            log.debug("New Value Set: {}", filtered);
            entry.setValue(filtered);
        }

        log.info("After: {}", placementByConcept);
    }

}

@Builder
@Data
class PlacementBundle {
    private String beatObjectiveId;
}

Output:

Original: {concept2=[PlacementBundle(beatObjectiveId=scored), PlacementBundle(beatObjectiveId=non-scored)], concept1=[PlacementBundle(beatObjectiveId=scored), PlacementBundle(beatObjectiveId=non-scored)]}

Does not work: {concept2=[PlacementBundle(beatObjectiveId=scored), PlacementBundle(beatObjectiveId=non-scored)], concept1=[PlacementBundle(beatObjectiveId=scored), PlacementBundle(beatObjectiveId=non-scored)]}

After: {concept2=[PlacementBundle(beatObjectiveId=scored)], concept1=[PlacementBundle(beatObjectiveId=scored)]}
SwaPpeR
  • 68
  • 6
  • 2
    How do you know that the OP is using an immutable collection? All I see in the question, is the mentioning of `HashSet`, which is definitely mutable. – Holger Jul 27 '22 at 08:17
  • Thanks for pointing [Holger](https://stackoverflow.com/users/2711488/holger). Corrected the wrong assumption. – SwaPpeR Jul 28 '22 at 10:46
1
public class Remove {
    public static void main(String[] args)
    {
        HashMap<Integer, String>
            map = new HashMap<>();

        map.put(1, "Stack");
        map.put(2, "Overflow");
        map.put(3, "StackOverflow");

        int keyToBeRemoved = 2;

        System.out.println("Original HashMap: "
                        + map);

        map.entrySet()
            .removeIf(
                entry -> (keyToBeRemoved == entry.getKey()));

        System.out.println("New HashMap: "
                        + map);
    }
}

Output:

Original HashMap: {1=Stack, 2=Overflow, 3=StackOverflow}
New HashMap: {1=Stack, 3=StackOverflow}
Kakarot
  • 11
  • 4
  • 1
    `map.entrySet() .removeIf(entry -> (keyToBeRemoved == entry.getKey()));` is a very convoluted way to do just `map.remove(keyToBeRemoved);` The latter is not only simpler, it will do the job without a linear search through the entire map. – Holger Jul 27 '22 at 08:11
  • 1
    Please read [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). While this code block may answer the OP's question, this answer would be much more useful if you explain how this code is different from the code in the question, what you've changed, why you've changed it and why that solves the problem without introducing others. – Saeed Zhiany Jul 27 '22 at 10:51