如何在Java 8中组合不同的流 [英] How to combine unlike streams in Java 8
问题描述
我有一个 Set< DateCount>
,我使用以下代码创建。这将创建一组7 DateCount
对象,初始计数为0,从当前一天开始,每周一天。
I have a Set<DateCount>
which I create using the following code. This creates a set of 7 DateCount
objects with an initial count of 0, one for each day of the current week starting from the current day.
// 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());
我有列表< Object []>
从数据库存储库返回的。列表中的每个 Object []
在索引0处具有 BigDecimal
并且 Long
在索引1处。 BigDecimal
实际上是一个日期,类似于 20141001
。我想知道的是,如果有一种方法可以使用Stream方式使用此数据更新 dateCounts
集。现在,我正在执行以下操作,只是遍历对象数组列表并创建新的 DateCount
对象,然后将这些对象添加到 dateCounts
设置。 DateCount
类有一个自定义 equals()
和 hashCode()
确保 dateCounts
集合包含任何给定日期的单个 DateCount
实例的方法。
I have a List<Object[]>
which is returned from a database repository. Each Object[]
in the list has a BigDecimal
at index 0 and a Long
at index 1. The BigDecimal
is actually a date, something like 20141001
. What I'm wondering is if there is a way I can update the dateCounts
set using this data in a Stream fashion. Right now, I'm doing the following which just iterates over the list of object arrays and creates new DateCount
objects that are then added to the dateCounts
set. The DateCount
class has a custom equals()
and hashCode()
method to ensure the dateCounts
set contains only a single DateCount
instance for any given date.
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表达式中仅用于将 String
解析为日期
。
On a side note, I'm wondering if there's also a way to avoid doing a try/catch in my lambda expression just for parsing a String
to a Date
.
更新 - 这是我到目前为止所提出的。虽然在.map调用中流式传输 List< Object []>
似乎并不自然,但不确定是否有更好的方法。我介绍了自己的 Tuple< X,Y>
类,因为在流中使用 Object []
不起作用非常好,因为没有访问方法。同样在最后两行中,我正在使用 Comparator
创建 TreeSet
,以便我可以按以下方式对数据进行排序日期。我觉得这不是最好的方法,但我尝试调用 sorted()
并使 DateCount
实现 Comparable
这似乎工作正常,但只要调用 collect(toSet())
方法,排序就会消失窗口。我认为这是流媒体通话的本质。我很好奇是否有办法在收集方法调用之前对其进行排序并在调用collect之后保留排序。
Update -- This is what I've come up with so far. It doesn't seem natural though to stream over the List<Object[]>
within the .map call, not sure if there's a better way. I introduced my own Tuple<X, Y>
class because working with an Object[]
in streams doesn't work very well given there's no accessor methods. Also in the last 2 lines, I'm creating a TreeSet
with a Comparator
so that I can sort the data by date. I don't feel that's the best approach, but I tried calling sorted()
and making DateCount
implement Comparable
which seems to work fine, but as soon as the collect(toSet())
method is called, the sorting goes out the window. I assume that's the nature of it being a streamed call. I'm curious if there's a way to sort it though before the collect method call and retain the sort after calling 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);
推荐答案
您可以将数据库数据映射到 Map< DateTime,Long>
可快速查找,因此您只需处理一次。然后,当您浏览 Set< DateCount>
时,可以使用 peek()
提取更新的值。这是一个例子。我使用 Date
代替 DateTime
只是为了更容易编写代码。
You can map your database data to a Map<DateTime,Long>
for quick look-ups so you only have to process it once. Then as you stream through your Set<DateCount>
you can pull in updated values using peek()
. Here's an example. I used Date
in place of DateTime
just to make it easier to code the example.
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已更新:
When run, here's the output, so that you can see the dateCounts are updated:
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的实现...我想它与你的不太相似:
And here's the implementation of DateCount I used to make the above code work... I imagine it's not too dissimilar to yours:
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;
}
}
这篇关于如何在Java 8中组合不同的流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!