Расчёт средней заработной платы с помощью стримов

Задача: расчёт средней заработной платы отделов с помощью стримов

public class Task implements AutoCloseable {

    public static List<Double> avgEmp = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        Map<String, Department> departments = new HashMap<>();

        String path = args.length > 0 ? args[0] : null;

        assert path != null;
        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(path), "CP1251")); br) {
            String line; // Как мне объявить line в while
            while ((line = br.readLine()) != null) {
                if (check(line)) {
                    String[] strings = line.split("/");
                    Employee emp = new Employee(strings[0], new Double(strings[1]));
                    if (!departments.containsKey(strings[2])) {
                        departments.put(strings[2], new Department(strings[2]));
                    }
                    departments.get(strings[2]).addEmployee(emp);
                    System.out.println(strings[0] + " " + strings[1] + " " + strings[2] + " - корректна, обрабатываем");
                }
            }
            systemMessage(0);
            avg(departments);

        } catch (FileNotFoundException e) {
            System.out.println("Файл не был найден, проверьте путь");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Исправьте путь к файлу, выходите за массив");
        } catch (NullPointerException e) {
            System.out.println("Вы забыли прописать путь к файлу");
        } finally {
            System.out.println("Читаемый файл закрыт");
        }
    }

    public static boolean check(String line) {
        String regex = "[А-Яа-яЁёA-Za-z\\s]+";
        if (!(line = line.trim()).isEmpty()) {
            String[] strings = line.split("/");
            if (strings.length < 3) {
                System.out.println(line + " - неверный формат строки, ожидаем ФИО/10000.12/Департамент");
                return false;
            }
            if (!(strings[0].matches(regex) && strings[2].matches(regex))) {
                System.out.println(line + " - неверный формат ввода, нужно: Буквы/Цифры/Буквы");
                return false;
            }
            if (!strings[1].matches("\\d{5}(\\.\\d{1,2})?")) {
                System.out.println(line + " - некорректный ввод цифр, знаков. Зарплата не может быть отрицательной и иметь меньше 5 знаков");
                return false;
            }
            return true;
        }
        return false;
    }


    public static void avg(Map<String, Department> departments) {
        for (Department dep : departments.values()) {
            Double avg = dep.salaryAvg();
            avgEmp.add(avg);
            System.out.println("Средняя заработная плата отдела " + dep.getName   () + ": " + avg);
        }
    }

Ответы (1 шт):

Автор решения: Дмитрий

Слишком сложная реализация.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@lombok.Data
@lombok.AllArgsConstructor
public class Employee implements Comparable<Employee>{
    
    private final String name;    
    private final Double salary;    
    private final String departmentName;
    
    public static Map<String, Queue<Employee>> fromFile(String fileName, String separator) throws IOException {
        try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
            return stream
                    .map(line -> line.split(separator))                    
                    .map(args->Employee.of(args))
                    .filter(employee->employee!=null)
                    .collect(Collectors.groupingBy(Employee::getDepartmentName, HashMap::new, 
                            Collectors.toCollection(PriorityQueue::new)));
        }
    }
    
    private static Employee of(String[] args) {
        try {
            return new Employee(args[0].trim(), Double.valueOf(args[1].trim()), args[2].trim());
        } catch (NumberFormatException | NullPointerException | ArrayIndexOutOfBoundsException e) {
            return null;
        }
    }
    
    @Override
    public int compareTo(Employee employee) {
        return this.salary.compareTo(employee.salary);
    }
    
}

import java.io.IOException;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Queue;
import java.util.stream.Collectors;
import org.springframework.data.util.Pair;

public class Task {
    
    public static void main(String[] args) throws IOException {
        
        Map<String, Queue<Employee>> employes = Employee.fromFile("1.txt", "/");
        
        Map<String, Double> salaryAvg = 
                employes.entrySet().stream()
                        .map(e->Pair.of(e.getKey(), e.getValue().stream().mapToDouble(Employee::getSalary).average().getAsDouble()))
                        .sorted(Comparator.comparingDouble(Pair::getSecond))
                .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond, (o1, o2) -> o1, LinkedHashMap::new));
        
        System.out.println(salaryAvg);
        
        //todo
        
    }

}

Сделайте класс сотрудников немного "умнее". Пусть он сам себя парсит. Тогда из класса Task просто вызывайте метод класса сотрудников для того, чтобы преобразовать текстовый файл в коллекцию типа Map, при этом в аргументах передается имя файла и разделитель, но поскольку используется split разделителем может быть и регулярка для более сложных случаев.

Из метода вы получите практически такую же Map, только ее значение будет типизировано очередью с приоритетом, а не листом. Для этого типа коллекции можно задать компаратор и получить элементы при переборе в нужном порядке (так мы упорядочим сотрудников по наростанию зп).

Получив такую коллекцию мы можем посчитать среднюю зп для всех отделов, которые также положим в коллекцию типа ключ-значение, где ключ - имя отдела, значение - средняя зп, причем эта коллекция также сортирована по возростанию зп.

Дальше дело техники. Вы имеете две упорядоченные коллекции : имя отдела - список сотрудников в порядке возрастания зп и имя отдела - средняя зп в отделе по возрастанию. В комментариях вам подсказали алгоритм. У очереди есть удобные инструменты для получения с удалением и пр., чего нет в листе. Класса департамент больше нет, поэтому будет достаточно изменить его имя в самом классе работника и добавить в Map по ключу нового отдела.

Я использую lombok (которую вы также можете подключить) и класс Pair и спринга (он есть также в апачевских утилитах), но можете обойтись и без него.

→ Ссылка