Java流参数限制无限制(MongoDB不一致)

时间:2019-03-24 20:16:25

标签: java mongodb java-stream

我有一个方法,该方法返回项目列表并以一个限制(由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不一致

例如,MongoDB的FindIterable#limit可以无限制地使用0null

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#skipFindIterable#skip的一致性得以保留。

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

具有Stream#limit的重构方法

我想没有办法将“无限制”参数传递给Stream#limit,所以我必须重构此方法以采用“限制”和0null或{{1 }}为“无限制”。

-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()); } 之间的一致性吗?

1 个答案:

答案 0 :(得分:0)

因此,您尝试执行的操作存在几层问题。

您说“实用性不是一个论点”,这很好,但是让我指出,Long.MAX_VALUE确实超过了地球上原子的数量,因此您获得更多条目的可能性来自数据库的确很小。更不用说继续将这些数据收集到一个列表中,因此您也可能在自己的应用程序中遇到内存问题。

因此,第二件事是limit()的语义是它对条目的数量施加了固定的限制,而“ infinity”不是固定的限制;因此limit()并不是您想要的。

第三,您似乎正在寻找一种解决方法,因此我们可以使用一种模式来维持自己的计数器。您想要的是类似AtomicBigInteger的东西,它在JDK but is shown here中不存在。

所以您要做的是像这样创建一个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代替它。