Преобразование итерации массива в лямбда-функцию с использованием Java8 - PullRequest
3 голосов
/ 28 мая 2019

Я пытаюсь преобразовать в лямбда-функцию

Пока что я могу преобразовать вышеуказанный код в лямбда-функцию, как показано ниже

Stream.of(acceptedDetails, rejectedDetails)
.filter(list -> !isNull(list) && list.length > 0)
.forEach(new Consumer<Object>() {
    public void accept(Object acceptedOrRejected) {
        String id;
        if(acceptedOrRejected instanceof EmployeeValidationAccepted) {
            id = ((EmployeeValidationAccepted) acceptedOrRejected).getId();
        } else {
            id = ((EmployeeValidationRejected) acceptedOrRejected).getAd().getId();
        }

        if(acceptedOrRejected instanceof EmployeeValidationAccepted) {
            dates1.add(new Integer(id.split("something")[1]));
            Integer empId = Integer.valueOf(id.split("something")[2]);
            empIds1.add(empId);
        } else {
            dates2.add(new Integer(id.split("something")[1]));
            Integer empId = Integer.valueOf(id.split("something")[2]);
            empIds2.add(empId);
        }
    }
});

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

Может кто-нибудь сказать, пожалуйста, как я могу импровизировать преобразованную функцию лямбда

Ответы [ 3 ]

4 голосов
/ 28 мая 2019

Как правило, при попытке рефакторинга кода вы должны сосредоточиться только на необходимых изменениях.

Только потому, что вы собираетесь использовать Stream API, нет причин загромождать код проверками на null или пустыми массивами, которых нет в коде на основе цикла. Вы также не должны изменить BigInteger на Integer.

Тогда у вас есть два разных входа и вы хотите получить разные результаты от каждого из них, другими словами, у вас есть две совершенно разные операции. Хотя разумно рассмотреть возможность совместного использования общего кода между ними, после того как вы определили идентичный код, нет смысла пытаться выразить две совершенно разные операции как одну операцию.

Во-первых, давайте посмотрим, как мы это сделаем для традиционного цикла:

static void addToLists(String id, List<Integer> empIdList, List<BigInteger> dateList) {
    String[] array = id.split("-");
    dateList.add(new BigInteger(array[1]));
    empIdList.add(Integer.valueOf(array[2]));
}
List<Integer> empIdAccepted = new ArrayList<>();
List<BigInteger> dateAccepted = new ArrayList<>();

for(EmployeeValidationAccepted acceptedDetail : acceptedDetails) {
    addToLists(acceptedDetail.getId(), empIdAccepted, dateAccepted);
}

List<Integer> empIdRejected = new ArrayList<>();
List<BigInteger> dateRejected = new ArrayList<>();

for(EmployeeValidationRejected rejectedDetail : rejectedDetails) {
    addToLists(rejectedDetail.getAd().getId(), empIdRejected, dateRejected);
}

Если мы хотим выразить то же, что и для потоковых операций, существует препятствие иметь два результата для каждой операции. До JDK 12 действительно потребовалось встроенное решение:

static Collector<String,?,Map.Entry<List<Integer>,List<BigInteger>>> idAndDate() {
    return Collectors.mapping(s -> s.split("-"),
        Collectors.teeing(
            Collectors.mapping(a -> Integer.valueOf(a[2]), Collectors.toList()),
            Collectors.mapping(a -> new BigInteger(a[1]),  Collectors.toList()),
            Map::entry));
}
Map.Entry<List<Integer>, List<BigInteger>> e;
e = Arrays.stream(acceptedDetails)
        .map(EmployeeValidationAccepted::getId)
        .collect(idAndDate());

List<Integer> empIdAccepted = e.getKey();
List<BigInteger> dateAccepted = e.getValue();

e = Arrays.stream(rejectedDetails)
    .map(r -> r.getAd().getId())
    .collect(idAndDate());

List<Integer> empIdRejected = e.getKey();
List<BigInteger> dateRejected = e.getValue();

Поскольку метод не может возвращать два значения, для их хранения используется Map.Entry.

Чтобы использовать это решение с версиями Java до JDK 12, вы можете использовать реализацию, опубликованную в конце этого ответа . Тогда вам также придется заменить Map::entry на AbstractMap.SimpleImmutableEntry::new.

Или вы используете специальный сборщик, написанный для этой конкретной операции:

static Collector<String,?,Map.Entry<List<Integer>,List<BigInteger>>> idAndDate() {
    return Collector.of(
        () -> new AbstractMap.SimpleImmutableEntry<>(new ArrayList<>(), new ArrayList<>()),
        (e,id) -> {
            String[] array = id.split("-");
            e.getValue().add(new BigInteger(array[1]));
            e.getKey().add(Integer.valueOf(array[2]));
        },
        (e1, e2) -> {
            e1.getKey().addAll(e2.getKey());
            e1.getValue().addAll(e2.getValue());
            return e1;
        });
}

Другими словами, использование Stream API не всегда упрощает код.

Как последнее замечание, нам не нужно использовать Stream API для использования лямбда-выражений. Мы также можем использовать их для перемещения цикла в общий код.

static <T> void addToLists(T[] elements, Function<T,String> tToId,
                           List<Integer> empIdList, List<BigInteger> dateList) {
    for(T t: elements) {
        String[] array = tToId.apply(t).split("-");
        dateList.add(new BigInteger(array[1]));
        empIdList.add(Integer.valueOf(array[2]));
    }
}
List<Integer> empIdAccepted = new ArrayList<>();
List<BigInteger> dateAccepted = new ArrayList<>();
addToLists(acceptedDetails, EmployeeValidationAccepted::getId, empIdAccepted, dateAccepted);

List<Integer> empIdRejected = new ArrayList<>();
List<BigInteger> dateRejected = new ArrayList<>();
addToLists(rejectedDetails, r -> r.getAd().getId(), empIdRejected, dateRejected);
2 голосов
/ 28 мая 2019

Подобный подход, как @roookeee, уже опубликованный, но, возможно, немного более кратким, будет хранить отображения с использованием функций отображения, объявленных как:

Function<String, Integer> extractEmployeeId = empId -> Integer.valueOf(empId.split("-")[2]);
Function<String, BigInteger> extractDate = empId -> new BigInteger(empId.split("-")[1]);

, затем перейдите к отображению как:

Map<Integer, BigInteger> acceptedDetailMapping = Arrays.stream(acceptedDetails)
        .collect(Collectors.toMap(a -> extractEmployeeId.apply(a.getId()),
                a -> extractDate.apply(a.getId())));

Map<Integer, BigInteger> rejectedDetailMapping = Arrays.stream(rejectedDetails)
        .collect(Collectors.toMap(a -> extractEmployeeId.apply(a.getAd().getId()),
                a -> extractDate.apply(a.getAd().getId())));

Здесь и далее вы также можете получить доступ к дате принятия или отклонения, соответствующей employeeId сотрудника.

1 голос
/ 28 мая 2019

Как насчет этого:

 class EmployeeValidationResult {
    //constructor + getters omitted for brevity
    private final BigInteger date;
    private final Integer employeeId;
}

List<EmployeeValidationResult> accepted = Stream.of(acceptedDetails)
    .filter(Objects:nonNull)
    .map(this::extractValidationResult)
    .collect(Collectors.toList());

List<EmployeeValidationResult> rejected = Stream.of(rejectedDetails)
    .filter(Objects:nonNull)
    .map(this::extractValidationResult)
    .collect(Collectors.toList());


EmployeeValidationResult extractValidationResult(EmployeeValidationAccepted accepted) {
    return extractValidationResult(accepted.getId());
}

EmployeeValidationResult extractValidationResult(EmployeeValidationRejected rejected) {
    return extractValidationResult(rejected.getAd().getId());
}

EmployeeValidationResult extractValidationResult(String id) {
    String[] empIdList = id.split("-");
    BigInteger date = extractDate(empIdList[1])
    Integer empId = extractId(empIdList[2]);

    return new EmployeeValidationResult(date, employeeId);
}

Повторение операций filter или map - это хороший стиль, в котором четко указано, что происходит. Объединение двух списков объектов в один и использование instanceof загромождают реализацию и делают ее менее читаемой / поддерживаемой.

...