java - java - 如何在这种情况下对列表进行流排序?

我有一个在Java中的对象列表,有两个时间戳,比如:

Obj (TimeStamp ts, TimeStamp generationTs, int value).

最后,我不希望列表中有两个相同的项,如果有,我想只保留最近的那个。

实际上,我有那个代码,但是,我想知道是否用流,能不能做的更好?

 

list.sort(Collections.reverseOrder());


List<Obj> returnedList = Lists.newArrayList();


if (!list.isEmpty()) {


 returnedList.add(list.get(0));


 Iterator<Obj> i = list.iterator();


 while (i.hasNext()) {


 Obj lastObj = returnedList.get(returnedList.size() - 1);


 Obj nextObj = i.next();


 if (!lastObj.getTs().isEqual(nextObj.getTs())) {


 returnedList.add(nextObj);


 } else {


 if (lastObj.getGenerationTs().isBefore(nextObj.getGenerationTs())) {


 returnedList.remove(lastObj);


 returnedList.add(nextObj);


 }


 }


 }


}



如果列表为:

 

{(" 2019-05-02T09:00:00Z" ," 2019-05-02T21:00:00Z" , 1),


(" 2019-05-02T09:30:00Z" ," 2019-05-02T21:00:00Z" , 2),


(" 2019-05-02T10:00:00Z" ," 2019-05-02T21:00:00Z" , 3),


(" 2019-05-02T10:30:00Z" ," 2019-05-02T21:00:00Z" , 4),


(" 2019-05-02T09:30:00Z" ," 2019-05-02T22:00:00Z" , 5),


(" 2019-05-02T10:00:00Z" ," 2019-05-02T22:00:00Z" , 6) }



它必须返回:

 

{(" 2019-05-02T09:00:00Z" ," 2019-05-02T21:00:00Z" , 1),


(" 2019-05-02T09:30:00Z" ," 2019-05-02T22:00:00Z" , 5),


(" 2019-05-02T10:00:00Z" ," 2019-05-02T22:00:00Z" , 6) 


(" 2019-05-02T10:30:00Z" ," 2019-05-02T21:00:00Z" , 4) }



时间:

当然,你可以使用Stream使用map collector来完成它,然后获取值,

 

Collection<Obj> objects = list.stream()


 . collect(Collectors.toMap(Obj::getTimeStamp,


 Function.identity(),


 (o1, o2) -> o1.getGenerationTs().isBefore(o2.getGenerationTs())? o2 : o1)


 . values();



List<Obj> listOfObjects = new ArrayList<>(objects);



或者更短

 

Collection<Obj> objects = list.stream()


 . collect(Collectors.toMap(Obj::getTimeStamp,


 Function.identity(),


 (o1, o2) -> o1.getGenerationTs().isBefore(o2.getGenerationTs())? o2 : o1),


 m -> new ArrayList<>(m.values()));



你可以这样尝试:

 

Map<TimeStamp, Optional<Obj>> result = 


 list.stream().collect(Collectors.groupingBy(


 Obj::getTs,


 Collectors.maxBy(Comparator.comparing(Obj::getGenerationTs))


 ));



更完整的选项如@Naman中所述:

 

list.stream().collect(Collectors.groupingBy(


 Obj::getTs,


 Collectors.maxBy(Comparator.comparing(Obj::getGenerationTs))


 )).values().stream()


 . filter(Optional::isPresent) 


 . map(Optional::get)


 . collect(Collectors.toList());



以下是实现的一种方式。

对第一个时间戳进行分组,然后使用maxBy查找具有最新生成时间戳的对象,最后排序第一个时间戳,并且将它打印出来。

maxBy会产生一个Optional的事实有点难看,但是,我找不到避免它的方法。

 

import static java.util.stream.Collectors.groupingBy;


import static java.util.stream.Collectors.maxBy;



import java.time.Instant;


import java.util.Optional;


import java.util.stream.Stream;



import org.junit.jupiter.api.Test;



public class SortTest {



@Test


public void t() {


 final Stream<Obj> s = Stream.of(new Obj(" 2019-05-02T09:00:00Z" ," 2019-05-02T21:00:00Z" , 1),


 new Obj(" 2019-05-02T09:30:00Z" ," 2019-05-02T21:00:00Z" , 2),


 new Obj(" 2019-05-02T10:00:00Z" ," 2019-05-02T21:00:00Z" , 3),


 new Obj(" 2019-05-02T10:30:00Z" ," 2019-05-02T21:00:00Z" , 4),


 new Obj(" 2019-05-02T09:30:00Z" ," 2019-05-02T22:00:00Z" , 5),


 new Obj(" 2019-05-02T10:00:00Z" ," 2019-05-02T22:00:00Z" , 6));



 s.collect(groupingBy(o -> o.ts, maxBy((o1, o2) -> o1.generationTs.compareTo(o2.generationTs))))


 . values()


 . stream()


 . map(Optional::get)


 . sorted((o1, o2) -> o1.ts.compareTo(o2.ts))


 . forEach(System.out::println);



}



private class Obj {


 Instant ts;


 Instant generationTs;


 int i;



 Obj(final String ts, final String generationTs, final int i) {


 this.ts = Instant.parse(ts);


 this.generationTs = Instant.parse(generationTs);


 this.i = i;


 }



 @Override


 public String toString() {


 return String.format(" %s %s %d" , ts, generationTs, i);


 }


}


}



如果已经有排序的列表(按generationTs降序),就像在示例代码中一样,可以使用HashSetCollection.removeIf()从该列表中删除所有重复时间戳:

 

list.sort(Comparator.comparing(Obj::getTs)


 . thenComparing(Comparator.comparing(Obj::getGenerationTs)


 . reversed()));



Set<Timestamp> keys = new HashSet<>();


list.removeIf(o ->!keys.add(o.getTs()));



使用此解决方案,不必创建新列表,只需修改你拥有的列表,集合将所有要维护的密钥存储在列表中,由于列表排序,所以,列表中的最新对象将保留在列表中,而且其他值将被移除。

你共享的数据的结果将为:

 

Obj[ts=2019-05-02T09:00:00Z, generationTs=2019-05-02T21:00:00Z, value=1]


Obj[ts=2019-05-02T09:30:00Z, generationTs=2019-05-02T22:00:00Z, value=5]


Obj[ts=2019-05-02T10:00:00Z, generationTs=2019-05-02T22:00:00Z, value=6]


Obj[ts=2019-05-02T10:30:00Z, generationTs=2019-05-02T21:00:00Z, value=4]



如果已经有排序列表,那么这个解决方案应该是最快的。

...