Предел параметра потока Java без ограничения (несоответствие MongoDB) - PullRequest
0 голосов
/ 24 марта 2019

У меня есть метод, который возвращает список элементов и принимает ограничение (используется Stream#limit) в качестве параметра:

public List<Integer> getItems(Long limit) {
    return IntStream.range(1, 10)
            .limit(limit)
            .boxed()
            .collect(Collectors.toList());
}  

Как установить параметр для приема всех элементов(без ограничений)?

Мои попытки:

    Long limit5 = 5L;
    System.out.println("With limit 5:" + getItems(limit5));
    // works fine: 5 items

    Long noLimitZero = 0L;
    System.out.println("Without limit (zero): " + getItems(noLimitZero));
    // why 0 mean "no items" instead of "all items"

   Long noLimitNegative = -1L;
    System.out.println("Without limit (negative number): " + getItems(noLimitNegative));
    // IllegalArgumentException

    Long noLimitNull = null;
    System.out.println("Without limit (null): " + getItems(noLimitNull));
    // NullPointerException

Передача Long.MAX_VALUE не является решением.

Несоответствие MongoDB

Например, MongoDBFindIterable#limit может принимать 0 или null в качестве неограниченного значения.

public List<Integer> getItems(Long limit) {
    MongoDatabase mongo = new MongoClient().getDatabase("example");
    MongoCollection<Document> documents = mongo.getCollection("items");
    FindIterable<Document> founded = documents.find();
    List<Integer> items = new ArrayList<>();
    for (Document doc : founded.limit(limit.intValue())) {
        items.add(doc.getInteger("number"));
    }
    return items;
}

Это несоответствие между методами приводит к несовместимости, например, один интерфейс с методом List<Integer> getItems(Long limit) и две реализации: в памяти и MongoDB.

Согласованность в методах Stream#skip и FindIterable#skip сохраняется.

          --------------------------
          | Java       | Mongo     |
------------------------------------
limit = 0 | none items | all items |
------------------------------------
skip = 0  | none skip  | none skip |
------------------------------------

Метод рефакторинга с Stream # limit

Я думаю, что естьнет способа передать параметр «без ограничений» в Stream#limit, поэтому я должен рефакторинг этого метода, чтобы получить «предел» и 0 или null или -1 как «без ограничений».

public static List<Integer> getItems(Long limit) {
    if (limit == null || limit == 0 || limit == -1) {
        return IntStream.range(1, 10)
                .boxed()
                .collect(Collectors.toList());
    } else {
        return IntStream.range(1, 10)
                .limit(limit)
                .boxed()
                .collect(Collectors.toList());
    }
}

Или:

public static List<Integer> getItems(Long limit) {
    IntStream items = IntStream.range(1, 10);
    if (limit != null && limit != 0 && limit != -1) {
        items = items.limit(limit);
    }
    return items.boxed()
            .collect(Collectors.toList());
}

Существует лучший способ достижения согласованности между методами limit?

1 Ответ

0 голосов
/ 25 марта 2019

Итак, есть несколько уровней проблем с тем, что вы пытаетесь сделать.

Вы говорите: «практичность - не аргумент», и это нормально, но позвольте мне отметить, что Long.MAX_VALUE превышаетколичество атомов на земле, так что вероятность того, что вы получите больше записей, чем из базы данных, очень мала.Не говоря уже о том, что вы продолжаете собирать эти данные в список, чтобы вы могли столкнуться с проблемами памяти и в своем собственном приложении.

Итак, во-вторых, семантика limit() заключается в том, что она налагаетфиксированный лимит на количество записей и «бесконечность» не является фиксированным лимитом;следовательно, limit() просто не то, что вы ищете.

В-третьих, вы, кажется, ищете способ обойти это, поэтому у нас есть шаблон, который вы можете использовать, и это поддерживает ваш собственный счетчик.То, что вы хотите, это что-то вроде AtomicBigInteger, которого нет в JDK , но показано здесь .

Итак, вы бы создали Predicate, как это

class BelowValue<T> implements Predicate<T> {
    BigInteger limit = BigInteger.ZERO;
    AtomicBigInteger counter = new AtomicBigInteger();

    public BelowValue(BigInteger limit) {
        this.limit = limit;
    }        
    public BelowValue() {}

    public boolean test(T ignored) {
        // short circuit on zero
        if (BigInteger.ZERO.compareTo(limit) == 0) { return true; }

        // check actual condition
        return  counter.incrementAndGet().compareTo(limit) > 0;
    }
}

и затем вы можете использовать его в своем потоке с (Java 8)

Predicate<T> filter = new BelowValue<>(limit);
return stream
    .filter(filter)
    .boxed()
    .collect(Collectors.toList());

Обратите внимание, однако, что filter является , а не операцией короткого замыкания, так что если у вас есть бесконечный поток, это не прекратится (и будет очень неэффективным, если ваш поток намного длиннее, чем предельный размер).

Java 9 takeWhile имеет короткое замыкание, поэтому вы можете заменитьчто для filter в приведенном выше примере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...