我使用以下代码创建了Set<DateCount>
。这将创建一组7个DateCount
个对象,初始计数为0,从当前一天开始,每周对应一天。
// Create an initial Set of DateCount objects for the current week with counts of 0
Set<DateCount> dateCounts = IntStream.range(0, DAYS_IN_WEEK)
.mapToObj(idx -> new DateTime().withTimeAtStartOfDay().plusDays(idx))
.map(dt -> new DateCount(dt.toDate(), 0L))
.collect(toSet());
我有一个从数据库存储库返回的List<Object[]>
。列表中的每个Object[]
在索引0处都有BigDecimal
,在索引1处有Long
。BigDecimal
实际上是日期,类似于20141001
。我想知道的是,是否有一种方法可以使用Stream方式使用此数据更新dateCounts
集。现在,我正在执行以下操作,只是遍历对象数组列表并创建新的DateCount
对象,然后将这些对象添加到dateCounts
集。 DateCount
类具有自定义equals()
和hashCode()
方法,可确保dateCounts
集合在任何给定日期仅包含一个DateCount
实例。
data.stream()
.forEach(obj -> {
DateTime date = null;
try {
date = (sdf == null) ? new DateTime(obj[0]) : new DateTime(sdf.parse(obj[0].toString()));
dateCounts.add(new DateCount(date.toDate(), (Long) obj[1]));
} catch (ParseException e) {
e.printStackTrace();
}
});
在旁注中,我想知道是否还有一种方法可以避免在我的lambda表达式中执行try / catch只是为了将String
解析为Date
更新 - 这是我到目前为止所提出的。虽然在.map调用中流过List<Object[]>
并不确定是否有更好的方法,但这似乎并不自然。我介绍了自己的Tuple<X, Y>
课程,因为如果没有访问者方法,在流中使用Object[]
并不能很好地工作。同样在最后两行中,我创建了TreeSet
Comparator
,以便按日期对数据进行排序。我觉得这不是最好的方法,但我尝试拨打sorted()
并DateCount
实施Comparable
这似乎工作得很好,但是collect(toSet())
{调用{1}}方法,排序出窗口。我认为它是流媒体通话的本质。我很好奇是否可以在收集方法调用之前对其进行排序,并在调用collect之后保留排序。
Set<DateCount> dateCounts = IntStream.range(0, DAYS_IN_WEEK)
.mapToObj(idx -> new Tuple<>(new DateTime().withTimeAtStartOfDay().plusDays(idx).toDate(), 0L))
.map(t -> {
Tuple<String, Long> d = data.stream()
.map(arr -> new Tuple<>(arr[0].toString(), (Long) arr[1]))
.filter(tuple -> sdf.format(t.getX()).equals(tuple.getX()))
.findFirst().orElse(new Tuple<>(sdf.format(t.getX()), 0L));
return new DateCount(DateTime.parse(d.getX(), DateTimeFormat.forPattern("yyyyMMdd")).toDate(), d.getY());
})
.collect(toSet());
TreeSet<DateCount> set = new TreeSet<>((a, b) -> a.compareTo(b));
set.addAll(dateCounts);
答案 0 :(得分:0)
您可以为该集合提供Supplier
。只需替换
.collect(toSet());
与
.collect(toCollection(() -> new TreeSet<>((a, b) -> a.compareTo(b))));
但请注意,为(a, b) -> a.compareTo(b)
指定Comparator
意味着自然排序。如果您的元素实现Comparable
,则不必提供这样的比较器。你可以简单地使用
.collect(toCollection(TreeSet::new));
如果DateCount
采用compareTo
方法但未实施Comparator
,您也可以指定DateCount::compareTo
而不是(a, b) -> a.compareTo(b)
。
请注意,在您的第一个mapToObj
操作中,无需将DateTime
包裹在Tuple
中。您只需映射到DateTime
并在下一个map
操作中使用该值(因为我在那里只看到t.getX()
,所以根本没有使用0L
的第二个值)。
毕竟,我不确定你想要实现什么,但我有强烈的感觉,你可能会看一下Collectors.groupingBy
......
答案 1 :(得分:0)
您可以将数据库数据映射到Map<DateTime,Long>
以便快速查找,因此您只需处理一次。然后,当您浏览Set<DateCount>
时,可以使用peek()
提取更新的值。这是一个例子。我使用Date
代替DateTime
只是为了更容易编写示例代码。
Instant baseDate = Instant.now();
Date date1 = new Date(baseDate.plus(1, ChronoUnit.DAYS).toEpochMilli());
Date date2 = new Date(baseDate.plus(2, ChronoUnit.DAYS).toEpochMilli());
Date date3 = new Date(baseDate.plus(3, ChronoUnit.DAYS).toEpochMilli());
Set<DateCount> dateCounts = new TreeSet<DateCount>(); // Our mock Set
dateCounts.add(new DateCount(date1, 0L));
dateCounts.add(new DateCount(date2, 0L));
dateCounts.add(new DateCount(date3, 0L));
List<Object[]> data = new ArrayList<Object[]>(); // Our mock database Data
data.add(new Object[]{date1.toInstant().toEpochMilli(), 5L});
data.add(new Object[]{date2.toInstant().toEpochMilli(), 3L});
data.add(new Object[]{date3.toInstant().toEpochMilli(), 2L});
//Map our data so we only have to process it once, and then we can look it up
Map<Date,Long> mappedData = data.stream()
.collect(Collectors.toConcurrentMap(k->new Date((long) k[0]), v->(Long)v[1]));
//Update the data using peek as we stream through it
dateCounts.stream()
.peek(dc->dc.setCount(mappedData.get(dc.getDate())))
.forEachOrdered(System.out::println);
运行时,这是输出,以便您可以看到dateCounts已更新:
DateCount: Thu Dec 25 11:25:56 EST 2014 - 5
DateCount: Fri Dec 26 11:25:56 EST 2014 - 3
DateCount: Sat Dec 27 11:25:56 EST 2014 - 2
以下是我用来使上述代码工作的DateCount的实现......我想它与你的不太相似:
public class DateCount implements Comparable<DateCount>{
private Date date = null;
private Long count = 0L;
public DateCount(Date datetime, Long count){
this.date = datetime;
this.count = count;
}
public void setDateTime(Date date){
this.date = date;
}
public Date getDate(){
return date;
}
public void setCount(long count){
this.count = count;
}
public long getCount(){
return count;
}
@Override
public int hashCode(){
return date.hashCode() + 459;
}
@Override
public boolean equals(Object o){
boolean result = false;
if (o instanceof DateCount){
DateCount other = (DateCount) o;
result = this.compareTo(other) == 0;
}
return result;
}
@Override
public int compareTo(DateCount other) {
if (this.date == null){
return other.getDate() == null ? 0 : -1;
} else {
return date.compareTo(other.getDate());
}
}
@Override
public String toString(){
return "DateCount: " + date.toString() + " - " + count;
}
}