[英]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的FindIterable#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
,所以我必须重构这个方法以取“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
之间的一致性?
所以你要做的事情有几层问题。
你说“实用性不是一个论证”,这很好,但我要指出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.