简体   繁体   English

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

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

I have a method which return list of items and takes a limit (used by Stream#limit ) as parameter: 我有一个方法返回项目列表并采取限制(由Stream#limit )作为参数:

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

How to set the parameter to take all items (with no limit)? 如何设置参数以获取所有项目(没有限制)?

My attempts: 我的尝试:

    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

Passing Long.MAX_VALUE is not a solution. 传递Long.MAX_VALUE不是解决方案。

MongoDB inconsistency MongoDB不一致

For example MongoDB's FindIterable#limit can take 0 or null as no limit. 例如,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;
}

This inconsistency between methods causes incompatibility, for example one interface with method List<Integer> getItems(Long limit) and two implementations: in memory and MongoDB. 方法之间的这种不一致导致不兼容,例如一个接口与方法List<Integer> getItems(Long limit)和两个实现:在内存和MongoDB中。

Consistency in methods Stream#skip and FindIterable#skip is preserved. 方法的一致性保留Stream#skipFindIterable#skip

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

Refactor method with Stream#limit 使用Stream#limit重构方法

I guess there is no way to pass "no limit" parameter to Stream#limit , so I must refactor this method to takes "limit" and 0 or null or -1 as "no 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());
    }
}

Or: 要么:

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());
}

There is a better way to achieve consistency between methods limit ? 有一种更好的方法来实现方法limit之间的一致性?

So there's several layers of problems with what you're trying to do. 所以你要做的事情有几层问题。

You say "practicality isn't an argument" and that's fine, but let me just point out that Long.MAX_VALUE does exceed the amount of atoms on earth, so the probability that you're getting more entries than that from a database is really small. 你说“实用性不是一个论证”,这很好,但我要指出Long.MAX_VALUE确实超过了地球上的原子数量,所以你获得的数据量大于数据库的数量小。 Not to mention that you go on collecting that data into a list so you might run into memory issues in your own application as well. 更不用说你继续将这些数据收集到一个列表中,这样你就可能在自己的应用程序中遇到内存问题。

So the second thing is that the semantics of limit() is that it imposes a fixed limit on the number of entries and "infinity" is not a fixed limit; 所以第二件事是limit()的语义是它对条目数量施加固定限制,而“无穷大”不是固定限制; hence limit() just isn't what you're looking for. 因此limit()不是你想要的。

Third, you seem to be looking for a way around that so there us a pattern you can use, and that's maintaining your own counter. 第三,你似乎正在寻找一种方法,所以我们有一个你可以使用的模式,那就是维持自己的计数器。 What you want is something like an AtomicBigInteger which doesn't exist in the JDK but is shown here . 你想要的是类似于AtomicBigInteger东西,它在JDK中不存在但是在这里显示

So what you'd do is create a Predicate like this 所以你要做的就是像这样创建一个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;
    }
}

and then you can use it in your stream with (Java 8) 然后你可以在你的流中使用它(Java 8)

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

Note however that filter is not a short-circuiting operation, so if you have an infinite stream, this will not terminate (and be very inefficient if your stream is much longer than the limit size). 但请注意, filter 不是短路操作,因此如果您有无限流,则不会终止(如果您的流比限制大小长得多,则效率非常低)。

Java 9's takeWhile is short-circuiting, so you can substitute that for filter in the above example. Java 9的takeWhile是短路的,因此您可以在上面的示例中将其替换为filter

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

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