2014-10-02 53 views
2

我有一个Set<DateCount>我使用下面的代码创建。这将创建一组7 DateCount对象,初始计数为0,从当天开始的当天每一天为一个对象。如何组合不同于Java流8

// 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实例。

​​

在一个侧面说明,我不知道是否还有一个方式,以避免在做我的lambda表达式一个try/catch只是解析StringDate

更新 - 这是我到目前为止所提出的。虽然在.map调用中流过List<Object[]>似乎并不自然,但不确定是否有更好的方法。我介绍了我自己的Tuple<X, Y>类,因为在流中使用Object[]不会很好,因为没有访问方法。同样在最后2行中,我将创建一个TreeSet,并使用Comparator,以便我可以按日期对数据进行排序。我不觉得这是最好的方法,但我试着拨打sorted()并使DateCount实现Comparable,这似乎工作正常,但只要collect(toSet())方法被调用,排序就会消失。我认为这是一个流式呼叫的本质。我很好奇在收集方法调用之前是否有办法对它进行排序,并在调用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

您可以提供一个Supplier收集。只是

.collect(toCollection(() -> new TreeSet<>((a, b) -> a.compareTo(b)))); 

更换

.collect(toSet()); 

但是注意,对于Comparator指定(a, b) -> a.compareTo(b)意味着自然排序。如果您的元素实现了Comparable,则不需要提供这样的比较器。你可以简单地使用

.collect(toCollection(TreeSet::new)); 

如果DateCountcompareTo方法,但没有实现Comparator你也可以指定DateCount::compareTo而非(a, b) -> a.compareTo(b)


注意,在你的第一个mapToObj操作不需要包裹DateTimeTuple。您可以简单地映射到DateTime并在接下来的map操作中使用该值(因为我在那里只看到t.getX(),第二个值是0L根本没有使用)。


毕竟,我不知道你想实现什么,但我有强烈的感觉,那你可能看看Collectors.groupingBy ......

+0

谢谢,供应商的方法看起来不错。我主要感到不舒服的是数据'data.stream()'的嵌套流式传输。看起来你不需要映射到我最初的'mapToObj'中的'Tuple'。我会看看groupingBy方法。谢谢 :) – 2014-10-02 14:06:55

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