我有一个关于java集合的问题,比如Set或List。更普遍的是,可以在for-each循环中使用的对象。是否有任何要求它们的元素实际上必须存储在数据结构中的某个位置,或者是否可以仅根据某种需求对它们进行描述,并在您需要它们时进行动态计算?我觉得这应该是可以做到的,但是我没有看到任何java标准集合类做这样的事情。我有违反任何形式的合同吗?
我考虑使用这些工具主要是为了数学。比方说,我想要一个集合来表示所有小于1,000,000的素数。将它们保存在内存中可能不是一个好主意,而是使用一个方法来检查集合中是否存在特定的数字。
我也不是java streams方面的专家,但我觉得这些在java 8 streams中应该是可用的,因为对象的状态非常简单(集合中的对象甚至不存在,直到您尝试遍历它们或检查集合中是否存在特定的对象)。
是否有可能拥有几乎无限多个元素的集合或迭代器,例如“6*k+1表上的所有数字”、“10以上的所有素数”或“所有以此为基的向量”?我正在考虑的另一件事是将两个集合组合在一起,比如所有小于1000000的素数和形式为2^n-1的所有整数的并集,并列出低于1000000的梅森素数。我觉得,如果以这种方式来推断某些数学对象,并且直到实际需要时才显式地创建元素,那么推断某些数学对象会更容易。也许我错了。
下面是我写的两个模型类,用来说明我想要做什么。它们的行为并不完全像我所期望的那样(参见输出),这让我认为我在这里破坏了一些关于iterable接口的约定,或者错误地实现了它。如果你看到了,或者这种代码在集合框架下是允许的,请随时指出我在这里做错了什么。
import java.util.AbstractSet;
import java.util.Iterator;
public class PrimesBelow extends AbstractSet<Integer>{
int max;
int size;
public PrimesBelow(int max) {
this.max = max;
}
@Override
public Iterator<Integer> iterator() {
return new SetIterator<Integer>(this);
}
@Override
public int size() {
if(this.size == -1){
System.out.println("Calculating size");
size = calculateSize();
}else{
System.out.println("Accessing calculated size");
}
return size;
}
private int calculateSize() {
int c = 0;
for(Integer p: this)
c++;
return c;
}
public static void main(String[] args){
PrimesBelow primesBelow10 = new PrimesBelow(10);
for(int i: primesBelow10)
System.out.println(i);
System.out.println(primesBelow10);
}
}。
import java.util.Iterator;
import java.util.NoSuchElementException;
public class SetIterator<T> implements Iterator<Integer> {
int max;
int current;
public SetIterator(PrimesBelow pb) {
this.max= pb.max;
current = 1;
}
@Override
public boolean hasNext() {
if(current < max) return true;
else return false;
}
@Override
public Integer next() {
while(hasNext()){
current++;
if(isPrime(current)){
System.out.println("returning "+current);
return current;
}
}
throw new NoSuchElementException();
}
private boolean isPrime(int a) {
if(a<2) return false;
for(int i = 2; i < a; i++) if((a%i)==0) return false;
return true;
}
}
Main function gives the output
returning 2
2
returning 3
3
returning 5
5
returning 7
7
Exception in thread "main" java.util.NoSuchElementException
at SetIterator.next(SetIterator.java:27)
at SetIterator.next(SetIterator.java:1)
at PrimesBelow.main(PrimesBelow.java:38)编辑:在next()方法中发现错误。更正了它并将输出更改为新的输出。
发布于 2017-08-18 03:57:43
正如您在您的(现在已修复的)示例中看到的,您可以使用Iterables/Iterators轻松完成此操作。与其有一个后备的集合,这个例子可能会更好,只有一个Iterable,它接受你想要计算素数的最大值。您只需要确保正确处理hasNext()方法,这样就不必从next()抛出不必要的异常。
如今,使用Java8Streams可以更容易地执行这些事情,但您没有理由不能拥有一个“虚拟集合”,它只是一个Iterable。如果你开始实现Collection就会变得更难,但即使这样也不是完全不可能的,这取决于用例:例如,你可以实现检查素数的contains(),但你必须计算它,而且对于大数来说它会很慢。
一个(有点令人费解的)半无限奇数集的例子,它是不可变的,并且不存储任何值。
public class OddSet implements Set<Integer> {
public boolean contains(Integer o) {
return o % 2 == 1;
}
public int size() {
return Integer.MAX_VALUE;
}
public boolean add(Integer i) {
throw new OperationNotSupportedException();
}
public boolean equals(Object o) {
return o instanceof OddSet;
}
// etc. etc.
}发布于 2017-08-19 04:12:48
正如DwB所说的,这是不可能用实现的,因为每个元素都必须存储在内存中。然而,还有另一种选择:这正是Java的Stream API被实现的原因!
除非您显式地将它们收集到Collection中,否则Stream允许您遍历不存储在内存中的无限数量的对象。
来自IntStream#iterate的documentation
返回通过将函数f迭代应用于初始元素seed而产生的无限顺序有序IntStream,产生由seed、f(seed)、f(Seed)等组成的流。
IntStream中的第一个元素(位置0)将是提供的种子。对于n > 0,位置n的元素将是将函数f应用于位置n- 1的元素的结果。
以下是您在问题中提出的一些示例:
public class Test {
public static void main(String[] args) {
IntStream.iterate(1, k -> 6 * k + 1);
IntStream.iterate(10, i -> i + 1).filter(Test::isPrime);
IntStream.iterate(1, n -> 2 * n - 1).filter(i -> i < 1_000_000);
}
private boolean isPrime(int a) {
if (a < 2) {
return false;
}
for(int i = 2; i < a; i++) {
if ((a % i) == 0) {
return false;
}
return true;
}
}
}https://stackoverflow.com/questions/45743690
复制相似问题