Вставка нового элемента в Java Карта или Обновление и существующий элемент по его ключу - PullRequest
0 голосов
/ 17 марта 2020

Бизнес-запрос заключается в том, что я создаю List<ResponsePOJO> из List<RequestPOJO>. Это кажется достаточно простым.
Запрос на самом деле нуждается в некоторой ( больше ) обработке, то есть мне нужно сохранить пару параметров, а затем для каждого элемента выполнить запрос к CassandraMicroservice, который возвращает * 1008. *. Каждый элемент этого <List<CassandraPOJO> имеет свой собственный List<DataPOJO>, который должен попадать в категорию, представленную некоторыми специфическими c характеристиками в List<ResponsePOJO>. По сути, для каждого элемента в List<RequestPOJO> я строю List<List<DataPOJO>>, с которым нужно разобраться. К сожалению, все застряло так, как есть Я не могу изменить архитектуру .
Короче, моя проблема , что я не могу найти простой createOrUpdate на Map. Я попытался сделать updateOrCreate типа BiFunction. Я думаю, этого должно быть достаточно, чтобы сделать что-то вроде BiFunction, Map>, которое должно выглядеть следующим образом ( псевдокод ):

private BiFunction<ResponsePOJO, Map<Integer, ResponsePOJO>, Map<Integer, ResponsePOJO>> updateOrCreate(?*) {
        return (newValue, currentResult) -> {
            if (currentResult.contains(newValue)) 
                currentResult.updateParams(newValue);            
            else
                currentResult.put(newValue);
            return currentResult;
        };
    }  

? * Я заметил, что вызов BiFunction не имеет параметров, откуда он знает, к какому типу относятся его параметры? ( не мой главный вопрос , но я думаю, что одной из причин моей проблемы является отсутствие истинного понимания BiFunction вместе с Map.compute)
(почти) полный фрагмент кода:

// the POJOs, using lombok (anotations skipped)  
public class RequestPOJO {
    private Long id;
    private Long idEntity;
    private Long idInventory;
    // some are omitted for brevity
}  

@Builder(toBuilder = true)
public class ResponsePOJO {
    private Integer id;
    private Long noInventory;
    private String nameSpecies;
    private Double g1;
    private Double g2;
    private Double g3;
    // some are omitted for brevity

    public void updateParams(ResponsePOJO resp) {
        // only these fields need updating, because of business logic
        this.g1 += resp.getG1();
        this.g2 += resp.getG2();
        this.g3 += resp.getG3();
    }
}

public class CassandraPOJO {
    private Long id;
    private List<DataPOJO> detailsDataPojo;
    private Long noInventory;
    // some are omitted for brevity
}


public class DataPOJO {
    private Long idSpecies;
    private Long idQualityClass;
    private Double height;
    private Double diameter;
    private Double noCount;
    // some are omitted for brevity
}

// the business logic
public List<ResponsePOJO> compute(List<RequestPOJO> requestPojoList, List<SpeciesPOJO>speciesList) {
        List<ResponsePOJO> responseList = new ArrayList<>();
        for (RequestPOJO requestPojo : requestPojoList) {
            Long idEntity = requestPojo.getIdEntity();
            Long noInventory = requestPojo.getIdInventory(); // yes I know this is wrong, stick to the question

            List<CassandraPOJO> res = cassandraMicroservice.getByIdEntityFilteredByNoInventory(idEntity, noInventory);

            res.stream().forEach(dar -> {  
                Map<Long, List<DataPOJO>> listDataPojoBySpeciesId =
                        dar.getDetailsDataPojo().stream().collect(
                                Collectors.groupingBy(DataPOJO::getIdSpecies, Collectors.toList())
                        );
                responseList.addAll(
                    classifyDataPojo(listDataPojoBySpeciesId, speciesList, dar.getNoInventory()) 
                );
            });
        }

        Comparator<ResponsePOJO> compareResponsePojo = Comparator.comparing(ResponsePOJO::getNameSpecies);
        return responseList.stream()
                .sorted(compareResponsePojo).collect(Collectors.toList());
    }  

    private List<ResponsePOJO> classifyDataPojo(Map<Long, List<DataPOJO>> listDataPojoBySpeciesId, List<SpeciesPOJO> speciesList, Long noInventory) {
        Map<Integer, ResponsePOJO> result = new HashMap();
        for (Long speciesId : listDataPojoBySpeciesId.keySet()) {
            String nameSpecies = speciesList.stream().filter(s -> s.getIdSpecies() == speciesId).findFirst().get().getNameSpecies(); // it's guaranteed to be found
            for (DataPOJO dataP : listDataPojoBySpeciesId.get(speciesId)) {
                Double volumeUnit = getVolumeUnit(dataP);
                Double equivalenceCoefficient = getEquivalentClass(dataP, speciesList);
                CustomTypeEnum customType = getCustomType(speciesList, dataP.getDiameter, speciesId);
                resp = ResponsePOJO.builder()
                        .noInventory(noInventory)
                        .nameSpecies(nameSpecies)
                        .build();
                switch (customType) {
                    case G1:
                        resp.setG1(volumeUnit * equivalenceCoefficient * dataP.getNoCount());
                        break;
                    case G2:
                        resp.setG2(volumeUnit * equivalenceCoefficient * dataP.getNoCount());
                        break;                    
                    case G3:
                        resp.setG3(volumeUnit * equivalenceCoefficient * dataP.getNoCount());
                        break;
                    default:
                        break;
                }
            }
            int diameter = ComputeUtil.getDiamenterClass(resp.getDiameter());
            // doesn't compile, it says Wrong 2nd argument type. Found: BiFunction<ResponsePOJO, Map<Integer, ResponsePOJO>, Map<Integer,ResponsePOJO>> Required: BiFunction<? super Integer,? super ResponsePOJO,? extends ResponsePOJO>             
            result.compute(diameter, updateOrCreate());
            // I have fiddled with reduce and merge but to no avail
            // result.values().stream().reduce(new ArrayList<>(), updateOrCreate(), combiningFunction());
            // result.merge(diameter, update())
        }
        return result.values().stream().collect(Collectors.toList());
    }

Я выбрал Map, потому что я хочу, чтобы он был максимально быстрым, этот метод compute(...) вызывается довольно часто, и я не хочу искать по всему списку ответов каждый раз, когда мне нужно что-то обновить. Я не хочу менять POJO, особенно CassandraPOJO, который имеет DataPOJO.
. Как вы можете видеть, это смесь как классических for, так и java8 stream. Я намереваюсь изменить весь код в соответствии с java8, но мне понадобилось немало времени, чтобы написать этот (сложный, сложный, понятный) код.
Я твердо убежден, что существует более простое решение, но я не могу понять это самостоятельно.

1 Ответ

2 голосов
/ 17 марта 2020

Из этого длинного кода мне кажется, что вы просто ищете merge операцию, такую ​​как:

result.merge(diameter, resp, (a, b) -> { 
    a.updateParams(b);
    return a;
});
return new ArrayList<>(result.values());

, которую можно абстрагировать как

private BiFunction<ResponsePOJO, ResponsePOJO, ResponsePOJO> mergeResponse() {
    return (a, b) -> {
        a.updateParams(b);
        return a;
    };
}

и используется далее как

result.merge(diameter, resp, mergeResponse());
...