如何在Java 8中组合不同的流

时间:2014-10-02 00:17:58

标签: java lambda java-8 java-stream

我使用以下代码创建了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处有LongBigDecimal实际上是日期,类似于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);

2 个答案:

答案 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;
    }
}