Convert a stream to a map when key / value mapping functions have a common computational step

I have a collection of objects Employeeand you need to turn it into a map of hyperlink widgets for presentation purposes.

For each employee, an entry is added to the result with a key that is an identifier (here is the national insurance number), and the value is a hyperlink widget. Here's the first attempt:

static Map<String, Hyperlink> toHyperlinksByNIN(Collection<Employee> employees) {
    return employees.stream()
            .collect(Collectors.toMap(
                    Employee::determineUniqueNINumber,
                    employee -> new Hyperlink(
                          employee.getName(), employee.determineUniqueNINumber())));
}

Unfortunately, this decision will not be fulfilled, since the NI number is not actually part of the employee model, but it must be retrieved from the expensive remote service with every call Employee.determineUniqueNINumber. This method is too expensive to call more than once on an employee record.

How can i get the desired Map

  • Stream API,
  • / (Employee.determineUniqueNINumber) ?
+4
5

, / :

return employees.stream()
       .collect(HashMap::new,
                (map, e) -> {
                  String number = e.determineUniqueNINumber();
                  map.put(number, new Hyperlink( e.getName(), number));
                },
                Map::putAll);
+1

Hyperlink UniqueNINumber getter? , :

return employees
        .stream()
        .map(employee -> new Hyperlink(employee.getName(), employee
            .determineUniqueNINumber()))
        .collect(Collectors.toMap(Hyperlink::getUniqueNINumber, i -> i));

Hyperlink:

public class Hyperlink {
    private String name;
    private String uniqueNINumber;

    public Hyperlink(String name, String uniqueNINumber) {
        this.name = name;
        this.uniqueNINumber = uniqueNINumber;
    }

    public String getName() {
        return name;
    }

    public String getUniqueNINumber() {
        return uniqueNINumber;
    }

    // Other stuff

}
+2

, - -, NINumber - .

, , AbstractMap.SimpleEntry.

:

return employees.stream()
        .map(emp -> new AbstractMap.SimpleEntry<>(emp.determineUniqueNINumber(),emp.getName()))
        .collect(Collectors.toMap(
                mapEntry -> mapEntry.getKey(),
                mapEntry -> new Hyperlink(mapEntry.getValue(), mapEntry.getKey())));

. , , getName() Employee, Employee.

+2

, - determineUniqueNINumber:

public class Employee {

    private String niNumber;

    public String determineUniqueNINumber() {
        if (niNumber == null) {
            niNumber = resultOfLongAndCostlyMethod();
        }
        return niNumber;
    }

}

, , .

Another solution is to keep the insurance number inside a custom type Tuple. It will store the employee along with the insurance number.

static Map<String, Hyperlink> toHyperlinksByNIN(Collection<Employee> employees) {
    return employees.stream()
            .map(e -> new Tuple<>(e, e.determineUniqueNINumber()))
            .collect(Collectors.toMap(
                    t -> t.getValue2(),
                    t -> new Hyperlink(t.getValue1().getName(), t.getValue2())));
}

class Tuple<T1, T2> {

    private final T1 value1;
    private final T2 value2;

    public Tuple(T1 value1, T2 value2) {
        this.value1 = value1;
        this.value2 = value2;
    }

    public T1 getValue1() {
        return value1;
    }

    public T2 getValue2() {
        return value2;
    }

}
+1
source

You can convert to an auxiliary data class to make sure that you get only one number:

private static class EmployeeAndNINumber {
  private Employee employee;
  private String niNumber;

  public EmployeeAndNINumber(Employee employee) { 
    this.employee = employee;
    this.niNumber = employee.determineUniqueNINumber();
  }

  public Employee getEmployee() { return this.employee; }
  public String getNINumber() { return this.niNumber; }

  public Hyperlink toHyperlink() {
    return new Hyperlink(employee.getName(), this.getNINumber());
  }
}

Then you can convert this data class by getting the NI number once and only once, then use this information to create your map:

employees.stream()
  .map(EmployeeAndNINumber::new) 
  .collect(toMap(EmployeeAndNINumber::getNINumber,
                 EmployeeAndNINumber::toHyperlink));
0
source

Source: https://habr.com/ru/post/1609698/


All Articles