如何在Java 8中组合不同的流 [英] How to combine unlike streams in Java 8

查看:125
本文介绍了如何在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆