2

I am having 2 lists, let's say

1 -> List<Employee> list1;
2 -> List<Age> list2

class Employee {
    String name;
    String age;
    String address;
}

class Age {
    String name;
    String age;
}

What I like to do is I need to replace the age for all records in Employee List with the age in Age List having same name in both. How could I achieve this in Java 8?

4 Answers 4

7

You can stream the List<Age> inside the iterating the items of List<Employee>:

list1.forEach(e -> e.setAge(
     list2.stream()                                     // Stream<Age>
          .filter(a -> a.getName().equals(e.getName())) // ... find the name match
          .map(Age::getAge)                             // ... extract the age
          .findAny().orElse("unknown")));               // ... return it or else "unknown"
  • In case there is found no match of names, the default value is set to unknown.
  • Also, you have to assure there are no duplicated names on which the age is based on.
  • The age is String - is it okay?
  • Are you sure there are no null values?

In case you want to remove such unmatched entries, I suggest you use rather Iterator with Map<String, List<Age>>:

Map<String, List<Age>> map = list2.stream()
    .collect(Collectors.groupingBy(             // Map<String, List<Age>>
         Age::getName));                        // ... where 'name' is the key

final  Iterator<Employee> iterator = list1.iterator();
while (iterator.hasNext()) {                    // Iterating List<Employee>
    final Employee e = iterator.next();         // Employee
    if (map.containsKey(e.getName())) {         // If there is Age with equal 'name'
        final String age = map.get(e.getName()) // ... get the first found age
            .get(0).getAge();
        e.setAge(age);                          // ... set it to the Employee
    } else iterator.remove();                   // ... or else remove the Employee
}

Again care about the points I listed above. Moreover, if you don't want to use the first found age map.get(e.getName()).get(0).getAge(), you need to perform Stream::reduce over the Stream<Age> like:

// null doesn't occurunless the Age::getAge returns null
// The reducing would be much easier if the age is int
// Feel free to find max/min of the ages... up to you
String age = map.get(e.getName()).stream().map(Age::getAge).reduce(...).orElse(null); 

Conclusion: The way is a but clumsy and I, personally, would stick with the procedural for-loop approach.

Sign up to request clarification or add additional context in comments.

4 Comments

What if no match for name is found in Employee list then we should delete that particular record from Employee list?
It's up to you. My solution, in that case, sets the age as unknown. You can either remove these entries (Stream::filter) or leave them with unknown age. It depends on the business logic you implement.
Could you please update the code with Stream:filter so as to remove those entries if no match found?
I updated my answer. Finally, the Stream::filter would require to iterate the list of Age twice (first to filter unmatched, second to map the new value), so I suggest you to use Iterator).
3

This will work only if the name is unique.

Start by making a map from name to age:

Map<String,String> nameToAge = list2.stream().collect(Collectors.toMap(Item::getName, Item::getAge));

Now use nameToAge in your lookups:

List<String> names = list1.stream()
    .map(e -> new Employee(e.getName(), nameToAge.get(e.getName()), e.getAddress()))
    .collect(Collectors.toList());

The above code assumes that your classes have properly encapsulated their properties, and that the Employee class has the appropriate constructor.

Comments

2

Instead of using list2 for searching, you should create a HashMap. Because the ArrayList has O(n) performance for searching age by name from list2, whereas the HashMap Map<String, String> ageWithName has O(1) performance for searching on average.

Function<Employee, String> findAge takes employee as input and gives you the age:

Map<String, String> ageWithName = list2.stream().collect(Collectors.toMap(Age::getName, Age::getAge));
Function<Employee, String> findAge = e -> ageWithName.get(e.getName());

list1.forEach(e -> e.setAge(findAge.apply(e)));

Comments

1

A direct approach would be

for (int i = 0; i < list1.size(); i++) {
    Employee employee = list1.get(i);
    for (int j = 0; j < list2.size(); j++) {
       Age age = list2.get(j);
       if (employee.name.equals(age.name)) {
           employee.age = age.age;
           list1.set(i, employee);
       }
    }
}

You can also implement an equals method in employee, and make use of list#indexOf

for (int i = 0; i < list1.size(); i++) {
    Employee employee = list1.get(i);
    int ageIndex = list2.indexOf(employee); //or list2.indexOf(employee.name);
    if (ageIndex != -1) {
       employee.age = list2.get(ageIndex).age;
       list1.set(i, employee);
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.