List of Java 8 sections in conditional groups using previous elements

Suppose I have a list of intervals (sorted by beginning), and I want to split them so that I have a list of overlapping groups of intervals. So for example with Interval like:

 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+")"; } } 

And a List<Interval> like:

 [(0,4),(1,7),(6,10),(13,17),(20,100),(22,31),(60,65)] 

I need the output of List<List<Interval>> :

 [[(0,4),(1,7),(6,10)],[(13,17)],[(20,100),(22,31),(60,65)]] 

I can code this, but I really enjoy the more functional approach of Java 8 and want to know if there is something like an idiomatic way to do this using Java 8 threads.

I looked at the "group by" styles provided by Collectors , but they do not seem to apply, since I really do not group the classifier - you cannot calculate groups based only on the property of each individual element, you must consider the properties of each element in relation to groups, which have been calculated so far.

Of course, there is an abnormal way to do this in functional languages ​​(although I speak as a person who is not really a functional programmer :-)). How can I do this with threads in Java 8?

+5
source share
2 answers

You can not. Threads are not suitable for this kind of problem; threads do not have the concept of "previous elements" and are allowed to control elements in random order. You can do it in Java, of course, and you can do it in functional languages, but that does not mean that threads work like the data structures of a functional language that you are used to.

+4
source

You were looking for a suitable place to study groupingBy collectors, but you are also right that they will not provide the necessary logic for merging intervals. But they conceptually combine elements into the state created by previous elements. You must implement a similar collector yourself.

Based on your specification that the elements are already pre-configured by the start index, you can do it like this:

 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(); 

This creates a Collection , not a List , but you can just create a List from it:

 List<List<Interval>> list = new ArrayList<>(merged); 

This must be done if you intend to save the result for a longer time, and not process it immediately, since the Collection returned by the collector is a view in TreeMap containing more resources than necessary.

I think in most cases you better solve the loopback solution.

+4
source

Source: https://habr.com/ru/post/1244834/


All Articles