Srinivas Lakshman
Srinivas Lakshman

Reputation: 489

Java 8 extract all keys from matching values in a Map

I'm relatively new to Java8 and I have a scenario where I need to retrieve all the keys from the Map which matched with the objects.

Wanted to know if there is a way to get all keys without iterating them from the list again.

Person.java
private String firstName;
private String lastName;
//setters and getters & constructor


MAIN Class.

String inputCriteriaFirstName = "john";   

Map<String, Person> inputMap = new HashMap<>();
Collection<Person> personCollection = inputMap.values();
List<Person> personList = new ArrayList<>(personCollection);
List<Person> personOutputList = personList.stream()
.filter(p -> p.getFirstName().contains(inputCriteriaFirstName ))
.collect(Collectors.toList());


//IS There a BETTER way to DO Below ??

Set<String> keys = new HashSet<>();
for(Person person : personOutputList) {
    keys.addAll(inputMap.entrySet().stream().filter(entry -> Objects.equals(entry.getValue(), person))
        .map(Map.Entry::getKey).collect(Collectors.toSet()));
}

Upvotes: 16

Views: 22277

Answers (4)

Sahil Aggarwal
Sahil Aggarwal

Reputation: 1351

You can also use foreach api provided in java8 under lambda's

Below is code for your main method :

public static  void main() {

        String inputCriteriaFirstName = "john";   

        Map<String, Person> inputMap = new HashMap<>();
        Set<String> keys = new HashSet<>();

        inputMap.forEach((key,value) -> {
            if(value.getFirstName().contains(inputCriteriaFirstName)){
                keys.add(key);
            }
        });
    }

Upvotes: 6

Andreas
Andreas

Reputation: 159086

So, you want a personOutputList with all the selected persons, and a keys set with the keys for those selected persons?

Best (for performance) option is to not discard the keys during search, then split the result into separate person list and key set.

Like this:

String inputCriteriaFirstName = "john";
Map<String, Person> inputMap = new HashMap<>();

Map<String, Person> tempMap = inputMap.entrySet()
        .stream()
        .filter(e -> e.getValue().getFirstName().contains(inputCriteriaFirstName))
        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
List<Person> personOutputList = new ArrayList<>(tempMap.values());
Set<String> keys = new HashSet<>(tempMap.keySet());

The keys set is explicitly made an updatable copy. If you don't need that, drop the copying of the key values:

Set<String> keys = tempMap.keySet();

Upvotes: 1

Eran
Eran

Reputation: 393801

Instead of iterating over all the entries of the Map for each Person, I suggest iterating over the Map once:

Set<String> keys =
     inputMap.entrySet()
             .stream()
             .filter(e -> personOutputList.contains(e.getValue()))
             .map(Map.Entry::getKey)
             .collect(Collectors.toCollection(HashSet::new));

This would still result in quadratic running time (since List.contains() has linear running time). You can improve that to overall linear running time if you create a HashSet containing the elements of personOutputList, since contains for HashSet takes constant time.

You can achieve that by changing

List<Person> personOutputList = 
    personList.stream()
              .filter(p -> p.getFirstName().contains(inputCriteriaFirstName))
              .collect(Collectors.toList());

to

Set<Person> personOutputSet = 
    personList.stream()
              .filter(p -> p.getFirstName().contains(inputCriteriaFirstName))
              .collect(Collectors.toCollection(HashSet::new));

Upvotes: 7

Eugene
Eugene

Reputation: 120848

inputMap.entrySet() 
        .stream()
        .filter(entry -> personOutputList.contains(entry.getValue()))
        .map(Entry::getKey)
        .collect(Collectors.toCollection(HashSet::new))

Upvotes: 15

Related Questions