假设我有一个间隔列表(按开始排序),我想将它们分解,以便我有一个重叠的间隔组列表。因此,例如,Interval
为:
public class Interval {
private final int start;
private final int end;
public Interval(int start,int end){
this.start = start;
this.end = end;
}
public int getStart(){return start;}
public int getEnd(){return end;}
public String toString(){ return "("+start+","+end+")"; }
}
和List<Interval>
一样:
[(0,4),(1,7),(6,10),(13,17),(20,100),(22,31),(60,65)]
我想要输出List<List<Interval>>
:
[[(0,4),(1,7),(6,10)],[(13,17)],[(20,100),(22,31),(60,65)]]
我可以对此进行编码,但我真的很喜欢Java 8的功能更强大的方法,并想知道是否有任何类似于使用Java 8流的惯用方法。
我已经看过提供Collectors的“分组依据”样式,但它们似乎不适用,因为我不是真的按分类器分组 - 你无法计算群组仅基于每个单独元素的属性,您必须考虑与目前已计算的组相关的每个元素的属性。
当然,在函数式语言中有非疯狂的方法(虽然我说的不是真正的函数式程序员:-))。如何在Java 8中使用流来完成它?
答案 0 :(得分:4)
你做不到。流不适合这类问题;流没有“先前元素”的概念,并且允许以任意顺序对元素进行操作。你可以用Java来做,当然,你可以用函数式语言来做,但这并不意味着流的工作就像你习惯的函数式语言数据结构一样。
答案 1 :(得分:4)
在研究groupingBy
收藏家时,你正在寻找合适的地方,但你也没有提供合并间隔的必要逻辑。但它们在概念上将元素合并到由先前元素创建的状态中。你必须自己实现一个类似的收藏家。
根据您的规范,元素已按其起始索引预先排序,您可以这样做:
Comparator<Interval> byStart = Comparator.comparingInt(Interval::getStart);
Comparator<Interval> byEnd = Comparator.comparingInt(Interval::getEnd);
Collection<List<Interval>> merged = intervalList.stream().collect(
() -> new TreeMap<Interval,List<Interval>>(byStart),
(map,i) -> {
Map.Entry<Interval,List<Interval>> e=map.floorEntry(i);
if(e!=null && Collections.max(e.getValue(), byEnd).getEnd()>=i.getStart())
e.getValue().add(i);
else map.computeIfAbsent(i, x->new ArrayList<>()).add(i);
},
(m1,m2) -> m2.forEach((i,list) -> {
Map.Entry<Interval,List<Interval>> e=m1.floorEntry(i);
if(e!=null && Collections.max(e.getValue(), byEnd).getEnd()>=i.getStart())
e.getValue().addAll(list);
else m1.put(i, list);
})
).values();
这会创建Collection
而不是List
,但您只需创建一个List
:
List<List<Interval>> list = new ArrayList<>(merged);
如果您打算将结果保留更长时间而不是立即处理,那么您应该明确地这样做,因为收集器返回的Collection
是TreeMap
的视图,其中包含的资源比必要的。
我想,在大多数情况下,你最好使用基于循环的解决方案。
答案 2 :(得分:0)
有同样的问题。也许您可以做的是通过过滤器按组间隔创建多个流,然后将所有单独的流连接在一起。如果要创建3个组,则为每个组创建3个独立的流,然后加入一个。