Java Package Collection Method ()

What would be a good way to implement a pack() collection operation using a Java thread? Here is what I would like to do:

 List<String> items = Arrays.asList("A", "A", "B", "B", "A", "C", "C", "A", "A", "A"); List<List<String>> packs = items.stream().pack(); // packs: [[A,A],[B,B],[A],[C,C],[A,A,A]] 

Alternatively, a package operation can return a list of tuples in the form (index, element, count):

 [(0, A, 2), (2, B, 2), (4, A, 1), (5, C, 2), (7, A, 3)] 

I have currently implemented this with a mutable battery as follows:

 Packer<String> packer = new Packer<>(); items.stream().forEach(packer); List<Triple<Integer, T, Integer>> packs = packer.get(); public class Packer<T> implements Consumer<T>, Supplier<List<Triple<Integer, T, Integer>>> { private List<Triple<Integer, T, AtomicInteger>> result = new ArrayList<>(); private Optional<Triple<Integer, T, AtomicInteger>> currentElement = Optional.empty(); private int count = 0; @Override public void accept(T t) { if (currentElement.isPresent() && currentElement.get().getMiddle().equals(t)) { currentElement.get().getRight().incrementAndGet(); } else { currentElement = Optional.of(Triple.of(count, t, new AtomicInteger(1))); result.add(currentElement.get()); } count++; } @Override public List<Triple<Integer, T, Integer>> get() { return result.stream().map(x -> Triple.of(x.getLeft(), x.getMiddle(), x.getRight().get())).collect(Collectors.toList()); } } 
+5
source share
4 answers

You can collapse neighboring elements using StreamEx :

 List<List<String>> packs = StreamEx.of(items) .collapse(Object::equals, Collectors.toList()) .collect(Collectors.toList()); 

Output:

 [[A, A], [B, B], [A], [C, C], [A, A, A]] 

JDoodle Demo

I'm not sure if StreamEx supports index folding.

+4
source

As @shmosel noted in his answer, this can be done by StreamEx

 List<List<String>> packs = StreamEx.of(items).collapse(Object::equals, Collectors.toList()).toList(); System.out.println(packs); // [[A, A], [B, B], [A], [C, C], [A, A, A]] MutableInt idx = MutableInt.of(0); List<Triple<Integer, String, Integer>> packs2 = StreamEx.of(items).collapse(Object::equals, Collectors.toList()) .map(l -> Triple.of(idx.getAndAdd(l.size()), l.get(0), l.size())).toList(); System.out.println(packs2); // [[0, A, 2], [2, B, 2], [4, A, 1], [5, C, 2], [7, A, 3]] 

[Update]: just found out, actually in StreamEx even simpler APIs for this OP are available:

 packs = StreamEx.of(items).groupRuns(Object::equals).toList(); System.out.println(packs); packs2 = StreamEx.of(items).runLengths().mapKeyValue((k, v) -> Triple.of(idx.getAndAdd(v.intValue()), k, v.intValue())).toList(); System.out.println(packs2); 

Awesome!

+3
source

Try it.

 List<String> items = Arrays.asList("A", "A", "B", "B", "A", "C", "C", "A", "A", "A"); List<List<String>> result = items.stream() .collect(() -> new LinkedList<List<String>>(), (list, e) -> { if (list.isEmpty() || !list.getLast().contains(e)) list.add(new LinkedList<>()); list.getLast().add(e); }, (a, b) -> {}); System.out.println(result); 

result

 [[A, A], [B, B], [A], [C, C], [A, A, A]] 

This cannot be handled using a parallel thread.

+3
source

It looks like you could create a custom collector for this:

  static class PackCollector<T> implements Collector<T, List<List<T>>, List<List<T>>> { @Override public Supplier<List<List<T>>> supplier() { return () -> { List<List<T>> list = new ArrayList<>(); list.add(new ArrayList<>()); return list; }; } @Override public BiConsumer<List<List<T>>, T> accumulator() { return (list, s) -> { int size = list.size(); List<T> inner = list.get(size - 1); int innerSize = inner.size(); if (innerSize > 0) { T last = inner.get(inner.size() - 1); if (s.equals(last)) { inner.add(s); } else { List<T> newList = new ArrayList<>(); newList.add(s); list.add(newList); } } else { inner.add(s); } }; } @Override public BinaryOperator<List<List<T>>> combiner() { return (left, right) -> { List<T> lastLeft = left.get(left.size() - 1); List<T> firstRight = right.get(0); T leftElem = lastLeft.get(lastLeft.size() - 1); T rightElem = firstRight.get(firstRight.size() - 1); if (leftElem.equals(rightElem)) { lastLeft.addAll(right.remove(0)); } left.addAll(right); return left; }; } @Override public Set<Characteristics> characteristics() { return EnumSet.of(Characteristics.IDENTITY_FINISH); } @Override public Function<List<List<T>>, List<List<T>>> finisher() { return Function.identity(); } } 

And then use it:

  List<List<String>> result = items.stream().parallel().collect(new PackCollector<>()); System.out.println(result); 
+2
source

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


All Articles