繁体   English   中英

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

[英]Java stream parameter limit with no limit (MongoDB inconsistency)

我有一个方法返回项目列表并采取限制(由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 ,所以我必须重构这个方法以取“limit”和0null-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之间的一致性?

所以你要做的事情有几层问题。

你说“实用性不是一个论证”,这很好,但我要指出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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM