Limit Break

I need to split the list into two lists according to the predicate with the limiting elements that go into the truepart.
For instance. Let's say I have a list like this: A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]and I want to split it into a predicate o -> o % 2 == 0and with a limit 3.
I want to get Map<Boolean, List<Integer>>where:

true -> [2, 4, 6] // objects by predicate and with limit (actually, order is not important)
false -> [1, 3, 5, 7, 8, 9, 10]  // All other objects

Java 8 has a collector that splits a stream by a predicate - Collectors.partitioningBy(...)but it does not support restrictions. Can this be done using java 8 streams / guava / apache, or do I need to create my own implementation of this function?

EDIT: I wrote this function. If you have any suggestions about this, feel free to tell me. MultiValuedMap is optional and can be replaced with Map.

private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(Predicate<E> predicate, List<E> src, int limit) {
    MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();
    Iterator<E> iterator = src.iterator();
    while (iterator.hasNext()) {
        E next = iterator.next();
        if (limit > 0 && predicate.test(next)) {
            result.put(true, next);
            iterator.remove();
            limit--;
        }
    }
    result.putAll(false, src);
    return result;
}
+4
4

Stream, .

, , :

private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(
                                       Predicate<E> predicate, List<E> src, int limit) {
    MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();
    for(E next: src) {
        boolean key = limit>0 && predicate.test(next);
        result.put(key, next);
        if(key) limit--;
    }
    return result;
}

, ,

private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(
                                       Predicate<E> predicate, List<E> src, int limit) {
    MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();
    for(Iterator<E> iterator = src.iterator(); iterator.hasNext(); ) {
        E next = iterator.next();
        boolean key = predicate.test(next);
        result.put(key, next);
        if(key && --limit==0) iterator.forEachRemaining(result.get(false)::add);
    }
    return result;
}

, . .

, Java 8, -

private <E> MultiValuedMap<Boolean, E> partitioningByWithLimit(
                                       Predicate<E> predicate, List<E> src, int limit) {
    MultiValuedMap<Boolean, E> result = new ArrayListValuedHashMap<>();
    result.putAll(false, src);
    List<E> pos = result.get(true);
    result.get(false).removeIf(e -> pos.size()<limit && predicate.test(e) && pos.add(e));
    return result;
}
+3

:

public static <E> Map<Boolean, List<E>> partitioningByWithLimit(
        Stream<E> stream,
        Predicate<E> predicate,
        int limit) {

    class Acc {
        Map<Boolean, List<E>> map = new HashMap<>();

        Acc() {
            map.put(true, new ArrayList<>());
            map.put(false, new ArrayList<>());
        }

        void add(E elem) {
            int size = map.get(true).size();
            boolean key = size < limit && predicate.test(elem);
            map.get(key).add(elem);
        }

        Acc combine(Acc another) {
            another.map.get(true).forEach(this::add);
            another.map.get(false).forEach(this::add);
            return this;
        }

        Map<Boolean, List<E>> finish() {
            return map;
        }
    }

    return stream.collect(
            Collector.of(Acc::new, Acc::add, Acc::combine, Acc::finish));
}

Acc, . .

Collector.of.

:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Map<Boolean, List<Integer>> map = partitioningByWithLimit(
        list.stream(), 
        n -> n % 2 == 0, 
        3);

:

{false=[1, 3, 5, 7, 8, 9, 10], true=[2, 4, 6]}

, .

+5

How about this:

List<Integer> lista = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> count = new ArrayList<>(); 
Map<Boolean, List<Integer>> collect = lista.stream().collect(Collectors.groupingBy(new Function<Integer, Boolean>() {

    private int count = 0;

    @Override
    public Boolean apply(Integer o) {
        if(o % 2 == 0 && count < 3){
            count++;
            return true;
        } else {
            return false;
        }
    }
}));
System.out.println(collect);

Fingerprints: {false = [1, 3, 5, 7, 8, 9, 10], true = [2, 4, 6]}

0
source

What about:

list.stream().collect(Collectors.partitioningBy(withLimit(i -> i % 2 == 0, 3)));

public static <E> Predicate<E> withLimit(final Predicate<E> predicate, final int limit) {
    Objects.requireNonNull(predicate);
    final AtomicInteger counter = new AtomicInteger(limit);
    return e -> predicate.test(e) && counter.decrementAndGet() > 0;
}
0
source

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


All Articles