Caveat: Do not make actual use of my Answer here. In real work, I would go with the simple solution shown in correct Answer by Louis Wasserman.
Given that you were attempting to use streams, I did the same. Below are my results. Note: Usually streams can simplify long or convoluted conventional code. But this here is an example of the opposite. My stream-based code is less clear than Wasserman’s.
First, we establish an example class similar to yours. I use a Java 16+ record for brevity, but that is besides the point here.
record Person( Integer id , String name ) { }
For example data, I make an unmodifiable list of people having ID values of 1, 2, and 3.
List < Person > people = List.of(
new Person( 1 , "Alice" ) ,
new Person( 2 , "Bob" ) ,
new Person( 3 , "Carol" )
);
And an example map of person’s ID to their survey answer.
Map < Integer, String > mapOfPersonIdToAnswer =
new HashMap <>(
Map.of(
1 , "Poodle" ,
3 , "Cockatiel" ,
4 , "Akita"
) );
Now comes our logic.
The main problem in the Question’s code is that you were asking the removeAll
method to compare the C
objects in your list of contacts. But you defined your map as having not the C
objects themselves as keys but rather their nested Integer
ID field numbers as keys. We need to extract the ID field value from all your contacts (your C
objects, here, our Person
objects).
So we establish a set of all the persons’ ID field value, a Set< Integer >
. Generally best to default to immutability, so I return an unmodifiable set as provided by Collectors.toUnmodifiableSet
.
Set < Integer > idsOfPeople = people.stream().map( person -> person.id() ).collect( Collectors.toUnmodifiableSet() );
Same line of code, adding commentary.
Set < Integer > idsOfPeople =
people
.stream() // Make a stream of our `List` of `Person` objects.
.map( person -> person.id() ) // For each `Person` object in our stream, produce the ID field value to produce a new stream of `Integer` objects.
.collect( // Gather the `Integer` objects from our second stream into a collection.
Collectors.toUnmodifiableSet() // Produce an unmodifiable set into which we collect our `Integer` objects.
); // Return a `Set< Integer >`, a set of integer objects.
By the way… At first I thought to add a .distinct()
call after the map
call, in case we had duplicate Person
objects in the list. But calling .distinct()
is superfluous because a Set
by definition eliminates duplicates.
Now on to removing entries from our map where the key of the entry is found to match a Person ID in our set idsOfPeople
. The trick is that the Set
interface has no method for removeAllEntriesWhereKeyIsFoundInSet
. But there is an indirect route to accomplish that goal.
The Map#keySet
method produces as special kind of set, a set that is a view onto the keys in the map, not a separate independent set. If we delete a key from that key-set, we also delete its entry from the map.
Bonus: The Set
interface includes a removeAll
method. If we call removeAll
, while passing our idsOfPeople
set, on our special key-set view, we effectively remove all the entries where entry key is found in idsOfPeople
.
For more discussion, see Remove multiple keys from Map in efficient way?.
mapOfPersonIdToAnswer.keySet().removeAll( idsOfPeople );
Result when run. As expected, we are left with only a single entry in our map, the last entry, the one for ID 4 for which we have no such person in our people
list.
mapOfPersonIdToAnswer = {4=Akita}
Not that I recommend it, but you could turn those two lines into a single-line.
mapOfPersonIdToAnswer.keySet().removeAll( people.stream().map( person -> person.id() ).collect( Collectors.toUnmodifiableSet() ) );
As I said above, this is not better than the Wasserman solution, but it was fun to explore.
Here is the entire code example.
package work.basil.mapping;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class App {
public static void main ( String[] args ) {
App app = new App();
app.demo();
}
private void demo () {
record Person( Integer id , String name ) { }
List < Person > people = List.of(
new Person( 1 , "Alice" ) ,
new Person( 2 , "Bob" ) ,
new Person( 3 , "Carol" )
);
Map < Integer, String > mapOfPersonIdToAnswer =
new HashMap <>(
Map.of(
1 , "Poodle" ,
3 , "Cockatiel" ,
4 , "Akita"
) );
Set < Integer > idsOfPeople = people.stream().map( person -> person.id() ).collect( Collectors.toUnmodifiableSet() );
mapOfPersonIdToAnswer.keySet().removeAll( idsOfPeople );
System.out.println( "mapOfPersonIdToAnswer = " + mapOfPersonIdToAnswer );
}
}