#java #collections #java-8 #functional-programming #java-stream
#java #Коллекции #java-8 #функциональное программирование #java-stream
Вопрос:
У меня есть этот объект:
public class MenuPriceByDay implements Serializable {
private BigDecimal avgPrice;
private BigDecimal minPrice;
private BigDecimal maxPrice;
private Date updateDate;
..
}
и этот другой:
public class Statistics {
double min;
double max;
double average;
public Statistics() {
super();
}
public Statistics(double min, double max, double average) {
super();
this.min = min;
this.max = max;
this.average = average;
}
}
а также список цен:
List<MenuPriceByDay> prices = new ArrayList<MenuPriceByDay>();
которые я хочу преобразовать в карту:
Map<LocalDate, Statistics> last30DPerDay =
prices
.stream()
.collect(Collectors.toMap(MenuPriceByDay::getUpdateDate, p -> new Statistics( p.getAvgPrice().doubleValue(),
p.getMaxPrice().doubleValue(),
p.getMinPrice().doubleValue())));
но у меня возникла проблема с компиляцией:
Type mismatch: cannot convert from Map<Date,Object> to
Map<LocalDate,Statistics>
Комментарии:
1. Что делать, если у вас есть 2 price с одинаковым LocalDate. Какую статистику следует использовать тогда?
Ответ №1:
UpdateDate
имеет тип Date
, и вы пытаетесь собрать как LocalDate
, поэтому вам просто нужно преобразовать Date
в LocalDate
:
Map<LocalDate, Statistics> collect = prices.stream()
.collect(Collectors.toMap(m -> m.getUpdateDate()
.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate(),
p -> new Statistics(p.getAvgPrice().doubleValue(),
p.getMaxPrice().doubleValue(),
p.getMinPrice().doubleValue())
));
Более подробная информация о преобразовании даты в LocalDate или LocalDateTime и обратно
Или вы можете создать метод вместо этого следующим образом :
public LocalDate convertToLocalDateViaInstant(Date dateToConvert) {
return dateToConvert.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
}
и ваш код может быть :
Map<LocalDate, Statistics> collect = prices.stream()
.collect(Collectors.toMap(
m -> this.convertToLocalDateViaInstant(m.getUpdateDate()),
p -> new Statistics(p.getAvgPrice().doubleValue(),
p.getMaxPrice().doubleValue(),
p.getMinPrice().doubleValue())
));
Лучшее решение
лучшее решение — просто изменить updateDate
тип на LocalDate
:
private LocalDate updateDate;
и вы можете просто использовать свой код
Ответ №2:
Обратите внимание, что при использовании Collectors.toMap
вы можете получить IllegalStateException
, если у вас есть дубликаты ключей.
Если сопоставленные ключи содержат дубликаты (в соответствии с Object.equals(объект)), при выполнении операции сбора выдается исключение IllegalStateException.
Вместо этого вы можете использовать toMap(keyMapper, valueMapper, mergeFunction)
. Также существует решение с groupingBy
, и в этом случае вы должны решить, какая статистика должна использоваться при дублировании ключа:
Map<LocalDate, Optional<Statistics>> map = prices.stream()
.collect(groupingBy(p -> p.getUpdateDate()
.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate(),
mapping(p-> new Statistics(
p.getMinPrice().doubleValue(),
p.getAvgPrice().doubleValue(),
p.getMaxPrice().doubleValue()),
reducing((s1, s2) -> ???) // here