我打算使用功能组件(特别是Collector)将基于Java 7的代码重构为Java 8。该计划很简单:通过编码来学习它。让我们开始吧…
我们正在处理的Java对象概述 重构的Java对象
以下代码旨在从Company对象(该公司具有员工列表)中获取一些特定的数据。我要执行以下操作:
在Java 8之前,很难重构此逻辑并找出一些通用部分。但是功能组件使重构变得容易。
Java 8之前的版本 让我们看一下代码,
找出公司中最普通的员工姓名
private static String findMostRepeatingNameInCompany(Company company) { String repeatingName = null; Map<String, Integer> nameAndCount = new HashMap<>(); for (Department department : company.getDepartments()) { for (Employee employee : department.getEmployees()) { if (!nameAndCount.containsKey(employee.getName())) { nameAndCount.put(employee.getName(), 1); } else { Integer count = nameAndCount.get(employee.getName()); count++; nameAndCount.put(employee.getName(), count); } } } for (Map.Entry<String, Integer> entry : nameAndCount.entrySet()) { if (entry.getValue().equals(Collections.max(nameAndCount.values()))) { repeatingName = entry.getKey(); } } return repeatingName; }
寻找薪水最高的员工
private static Employee findEmployeeWithHighestSalaryInTheCompany(Company company) { Employee costlyEmployee = null; Map<Employee, Long> employeeAndSalary = new HashMap<>(); for (Department department : company.getDepartments()) { for (Employee employee : department.getEmployees()) { employeeAndSalary.put(employee, employee.getSalary()); } } for (Map.Entry<Employee, Long> entry : employeeAndSalary.entrySet()) { if (entry.getValue().equals(Collections.max(employeeAndSalary.values()))) { costlyEmployee = entry.getKey(); } } return costlyEmployee; }
找到所有男人的薪水总和
private static Long findSumOfAllMenSalary(Company company) { Long totalSalary = 0L; for (Department department : company.getDepartments()) { for (Employee employee : department.getEmployees()) { if (employee.getGender().equals(Gender.MALE)) { totalSalary = totalSalary + employee.getSalary(); } } } return totalSalary; }
查找公司中最受欢迎的等级/乐队
private static Band findMostPopularBandInCompany(Company company) { Band popularBand = null; Map<Band, Integer> bandAndCount = new HashMap<>(); for (Department department : company.getDepartments()) { for (Employee employee : department.getEmployees()) { if (!bandAndCount.containsKey(employee.getBand())) { bandAndCount.put(employee.getBand(), 1); } else { Integer count = bandAndCount.get(employee.getBand()); count++; bandAndCount.put(employee.getBand(), count); } } } for (Map.Entry<Band, Integer> entry : bandAndCount.entrySet()) { if (entry.getValue().equals(Collections.max(bandAndCount.values()))) { popularBand = entry.getKey(); } } return popularBand; }
在所有情况下,我们都需要遍历各个部门,然后遍历员工。但是,由于我们要收集的条件和数据的格式不同,因此我们将无法找到常见的东西并对其进行适当的重构。让我们看看Java 8是如何处理的。
首先,让我们使用流并进行处理。
找出公司中最普通的员工姓名 让我们看一下的逻辑nameAndCount,因为迭代(部门和员工上的for循环)已定义result(nameAndCount)。我们可以使用流来获取该值:
company.getDepartments().stream().flatMap(department -> department.getEmployees().stream()) .forEach(employee -> addToNameAndCountMap(nameAndCount, employee)); ... ... private static void addToNameAndCountMap(Map<String, Integer> nameAndCount, Employee employee) { if (!nameAndCount.containsKey(employee.getName())) { nameAndCount.put(employee.getName(), 1); } else { Integer count = nameAndCount.get(employee.getName()); count++; nameAndCount.put(employee.getName(), count); } }
为什么我们没有通过收集获得结果?我们在那里有一些逻辑。我们需要识别重复项。地图的值是员工姓名的数量。让我们看看最终结果。
private static String findMostRepeatingNameInCompany(Company company) { Map<String, Integer> nameAndCount = new HashMap<>(); company.getDepartments().stream() .flatMap(department -> department.getEmployees().stream()) .forEach(employee -> addToNameAndCountMap(nameAndCount, employee)); String repeatingName = null; Integer maxCount = Collections.max(nameAndCount.values()); Optional<String> repeat = nameAndCount.entrySet().stream() .filter(e -> e.getValue().equals(maxCount)) .map(Map.Entry::getKey).findFirst(); if (repeat.isPresent()) { repeatingName = repeat.get(); } return repeatingName; } private static void addToNameAndCountMap(Map<String, Integer> nameAndCount, Employee employee) { if (!nameAndCount.containsKey(employee.getName())) { nameAndCount.put(employee.getName(), 1); } else { Integer count = nameAndCount.get(employee.getName()); count++; nameAndCount.put(employee.getName(), count); } }
寻找薪水最高的员工 让我们在这里也进行相同的重构。但是在这里,我们可以使用collect,因为逻辑相当简单。
private static Employee findEmployeeWithHighestSalaryInTheCompany(Company company) { Employee costlyEmployee = null; Map<Employee, Long> employeeAndSalary = company.getDepartments().stream() .flatMap(department -> department.getEmployees().stream()) .collect(Collectors.toMap(employee -> employee, Employee::getSalary, (a, b) -> b)); Long maxSalary = Collections.max(employeeAndSalary.values()); Optional<Employee> costly = employeeAndSalary.entrySet().stream() .filter(e -> e.getValue().equals(maxSalary)).map(Map.Entry::getKey).findFirst(); if(costly.isPresent()) { costlyEmployee = costly.get(); } return costlyEmployee; }
找到所有男人的薪水总和 好吧,这似乎简单得多。
private static Long findSumOfAllMenSalary(Company company) { return company.getDepartments().stream() .flatMap(d -> d.getEmployees().stream()) .filter(e -> e.getGender().equals(Gender.MALE)) .map(Employee::getSalary).mapToLong(Long::longValue).sum(); }
查找公司中最受欢迎的等级/乐队 我们需要应用与第一种方法类似的逻辑。(我的意思是findMostRepeatingNameInCompany。)让我们尝试一下。
private static Band findMostPopularBandInCompany(Company company) { Map<Band, Integer> bandAndCount = new HashMap<>(); company.getDepartments().stream() .flatMap(department -> department.getEmployees().stream()) .forEach(employee -> addToBandAndCoutMap(bandAndCount, employee)); Band popularBand = null; Integer maxBand = Collections.max(bandAndCount.values()); Optional<Band> popular = bandAndCount.entrySet().stream() .filter(e -> e.getValue().equals(maxBand)) .map(Map.Entry::getKey).findFirst(); if(popular.isPresent()) { popularBand = popular.get(); } return popularBand; } private static void addToBandAndCoutMap(Map<Band, Integer> bandAndCount, Employee employee) { if (!bandAndCount.containsKey(employee.getBand())) { bandAndCount.put(employee.getBand(), 1); } else { Integer count = bandAndCount.get(employee.getBand()); count++; bandAndCount.put(employee.getBand(), count); } }
我们有一些共同的价值观,如果我们发现了那些共同的事物并适当地重构它,也许能够节省一些行,让我们一起去做吧……
这次,让我们stream.collect()到处使用方法。因此,我们仅对两个方法调用findMostRepeatingNameInCompany和进行了更改findMostPopularBandInCompany。
找出公司中最普通的员工姓名 让我们尝试在此处使用collect,并避免addToNameAndCountMap方法调用。如果使用正确的收集器,则可以使用自定义键或值将数据收集到地图。我们将为此使用Collectors.groupingBy(Employee::getName,Collectors.counting())。
addToNameAndCountMap
Collectors.groupingBy(Employee::getName,Collectors.counting())
private static String findMostRepeatingNameInCompany(Company company) { Map<String, Long> nameAndCount = company.getDepartments().stream() .flatMap(department -> department.getEmployees().stream()) .collect(Collectors.groupingBy(Employee::getName,Collectors.counting())); Long maxCount = Collections.max(nameAndCount.values()); Optional<String> repeat = nameAndCount.entrySet().stream() .filter(e -> e.getValue().equals(maxCount)) .map(Map.Entry::getKey).findFirst(); String repeatingName = null; if (repeat.isPresent()) { repeatingName = repeat.get(); } return repeatingName; }
查找公司中最受欢迎的等级/乐队 让我们在这里也做同样的事情。
private static Band findMostPopularBandInCompany(Company company) { Map<Band, Long> bandAndCount = company.getDepartments().stream() .flatMap(department -> department.getEmployees().stream()) .collect(Collectors.groupingBy(Employee::getBand, Collectors.counting())); Band popularBand = null; Long maxBand = Collections.max(bandAndCount.values()); Optional<Band> popular = bandAndCount.entrySet().stream() .filter(e -> e.getValue().equals(maxBand)) .map(Map.Entry::getKey).findFirst(); if(popular.isPresent()) { popularBand = popular.get(); } return popularBand; }
现在我们有了一个通用的模式,似乎有可能从中提取一些功能组件。让我们探讨这种可能性。
我们可以看到,方法findMostRepeatingNameInCompany,findEmployeeWithHighestSalaryInTheCompany并findMostPopularBandInCompany遵循类似的模式。唯一的区别是地图的类型和我们使用的收集器。因此,让我们编写一个返回通用映射并接受收集器的方法。
private static <T> Map<T, Long> processEmployeeToMap(Company company, Collector<Employee, ?, Map<T,Long>> employeeMapCollector) { return company.getDepartments().stream() .flatMap(department -> department.getEmployees().stream()) .collect(employeeMapCollector); }
这是一种通用方法,适用于不同的输入,如果T是字符串,它将使用收集器获取aMap<String, Long>作为返回类型。如果我们使用另一个对象(例如Employee),则返回类型将为Map<Employee, Long>。让我们尝试在接下来的三个方法中使用此方法调用。
aMap<String, Long>
Map<Employee, Long>
private static String findMostRepeatingNameInCompany(Company company) { Collector<Employee, ?, Map<String, Long>> repeatingNameCollector = Collectors.groupingBy(Employee::getName,Collectors.counting()); Map<String, Long> nameAndCount = processEmployeeToMap(company,repeatingNameCollector); Long maxCount = Collections.max(nameAndCount.values()); Optional<String> repeat = nameAndCount.entrySet().stream() .filter(e -> e.getValue().equals(maxCount)) .map(Map.Entry::getKey).findFirst(); String repeatingName = null; if (repeat.isPresent()) { repeatingName = repeat.get(); } return repeatingName; }
寻找薪水最高的员工 让我们在这里也进行相同的重构:
private static Employee findEmployeeWithHighestSalaryInTheCompany(Company company) { Collector<Employee,?,Map<Employee,Long>> highSalary = Collectors.toMap(employee -> employee, Employee::getSalary, (a, b) -> b); Map<Employee, Long> employeeAndSalary = processEmployeeToMap(company,highSalary); Long maxSalary = Collections.max(employeeAndSalary.values()); Optional<Employee> costly = employeeAndSalary.entrySet().stream() .filter(e -> e.getValue().equals(maxSalary)).map(Map.Entry::getKey).findFirst(); Employee costlyEmployee = null; if(costly.isPresent()) { costlyEmployee = costly.get(); } return costlyEmployee; }
private static Band findMostPopularBandInCompany(Company company) { Collector<Employee,?,Map<Band,Long>> popularBandCollector = Collectors.groupingBy(Employee::getBand, Collectors.counting()); Map<Band, Long> bandAndCount = processEmployeeToMap(company,popularBandCollector); Band popularBand = null; Long maxBand = Collections.max(bandAndCount.values()); Optional<Band> popular = bandAndCount.entrySet().stream() .filter(e -> e.getValue().equals(maxBand)) .map(Map.Entry::getKey).findFirst(); if(popular.isPresent()) { popularBand = popular.get(); } return popularBand; }
找到所有男人的薪水总和 好吧,我们这里需要一种不同的方式,因为这种方式与其余方法完全不同。
public static Function<Department, Stream<Employee>> allEmployeeInDept = department -> department.getEmployees().stream(); private static Long findSumOfAllMenSalary(Company company) { return company.getDepartments().stream() .flatMap(d -> allEmployeeInDept.apply(d)) .filter(e -> e.getGender().equals(Gender.MALE)) .map(Employee::getSalary).mapToLong(Long::longValue).sum(); }
Well, we have used the functional components and achieved a better reusable code, but still, this can be cleaned up a bit.
You might have noticed that all these methods need a single value in return, not a list. And, we use optional and some logic to identify that single element. Can we extract that too, let’s try that now.
private static <T> T fetchParamsFromMap(Map<T, Long> param) { return param.entrySet().stream() .filter(e -> e.getValue().equals(Collections.max(param.values()))) .map(Map.Entry::getKey).findFirst().orElse(null); }
This method returns the key of the biggest param in a map, using generics, this one can be applied to all of our methods
Find out the most common employee name in the company
private static String findMostRepeatingNameInCompany(Company company) { Collector<Employee, ?, Map<String, Long>> repeatingNameCollector = Collectors.groupingBy(Employee::getName, Collectors.counting()); Map<String, Long> nameAndCount = processEmployeeToMap(company, repeatingNameCollector); return fetchParamsFromMap(nameAndCount); }
that simple? yes, it is
Find the highest salaried employee
private static Employee findEmployeeWithHighestSalaryInTheCompany(Company company) { Collector<Employee, ?, Map<Employee, Long>> highSalary = Collectors.toMap(employee -> employee, Employee::getSalary, (a, b) -> b); Map<Employee, Long> employeeAndSalary = processEmployeeToMap(company, highSalary); return fetchParamsFromMap(employeeAndSalary); }
Find the most popular grade/band in the company
private static Band findMostPopularBandInCompany(Company company) { Collector<Employee, ?, Map<Band, Long>> popularBandCollector = Collectors.groupingBy(Employee::getBand, Collectors.counting()); Map<Band, Long> bandAndCount = processEmployeeToMap(company, popularBandCollector); return fetchParamsFromMap(bandAndCount); }
完毕?不,我们还没有完成……还有很多事情要做。
在这些方法中findMostRepeatingNameInCompany,findEmployeeWithHighestSalaryInTheCompany和findMostPopularBandInCompany,仍然是一个重复的模式,我们用processEmployeeToMap了,马上,我们叫fetchParamsFromMap。让我们结合起来。
findMostRepeatingNameInCompany,findEmployeeWithHighestSalaryInTheCompany
findMostPopularBandInCompany
fetchParamsFromMap
private static <T> T fetchBestOfMappedEmployees(Company company, Collector<Employee, ?, Map<T, Long>> employeeMapCollector) { Map<T, Long> mappedResults = company.getDepartments().stream() .flatMap(department -> department.getEmployees().stream()) .collect(employeeMapCollector); return mappedResults.entrySet().stream() .filter(e -> e.getValue().equals(Collections.max(mappedResults.values()))) .map(Map.Entry::getKey).findFirst().orElse(null); }
现在,让我们看看我们的will方法的外观:
private static String findMostRepeatingNameInCompany(Company company) { Collector<Employee, ?, Map<String, Long>> repeatingNameCollector = Collectors.groupingBy(Employee::getName, Collectors.counting()); return fetchBestOfMappedEmployees(company, repeatingNameCollector); }
private static Employee findEmployeeWithHighestSalaryInTheCompany(Company company) { Collector<Employee, ?, Map<Employee, Long>> highSalary = Collectors.toMap(employee -> employee, Employee::getSalary, (a, b) -> b); return fetchBestOfMappedEmployees(company, highSalary); }
private static Band findMostPopularBandInCompany(Company company) { Collector<Employee, ?, Map<Band, Long>> popularBandCollector = Collectors.groupingBy(Employee::getBand, Collectors.counting()); return fetchBestOfMappedEmployees(company, popularBandCollector); }
我们甚至可以继续简化此过程,可以将收集器放到外面,父调用可以传递该集合,然后我们就不需要此方法本身了。
原文链接:http://codingdict.com