如何在Java中使用有限的堆栈处理不可变列表?

如何在Java中使用有限的堆栈处理不可变列表?,java,recursion,immutability,Java,Recursion,Immutability,我正在使用一个API,该API为我提供了大型不可变列表,如下所示: class List<T> { final T e; final List<T> next; public List(T e, List<T> next) { this.e = e; this.next = next; } } 但是一旦我有了超过9000个元素,我就会得到StackOverflowError(将I更改为从1000

我正在使用一个API,该API为我提供了大型不可变列表,如下所示:

class List<T> {
    final T e;
    final List<T> next;
    public List(T e, List<T> next) {
        this.e = e;
        this.next = next;
    }
}
但是一旦我有了超过9000个元素,我就会得到
StackOverflowError
(将
I
更改为从10000开始,而不是
A
中的9000开始):

因此,我更改了
B
以使用不同的策略来增加列表元素:

class B {
    static List<Integer> incrementList(List<Integer> l) {
        return ListIncrementer.call(l);
    }
}

class ListIncrementer extends Thread {
    List<Integer> l;
    List<Integer> result;
    ListIncrementer(List<Integer> l) {
        this.l = l;
    }
    public void run() {
        if (l == null) {
            result = null;
            return;
        }
        result = new List<Integer>(l.e+1,call(l.next));
    }
    static List<Integer> call(List<Integer> l) {
        ListIncrementer li = new ListIncrementer(l);
        li.start();
        try { li.join(); } catch (Exception _) {}
        return li.result;
    }
}
正如预期的那样,它起作用了

但是,我仍然只能使用此方法执行大约30000个元素,这是当我在
A
中将
I
设置为50000时发生的情况:

Exception in thread "Thread-32290" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:657)
    at ListIncrementer.call(A.java:25)
    at ListIncrementer.run(A.java:21)
0 -> 1
1 -> 2
2 -> 3
Exception in thread "main" java.lang.NoSuchMethodError: main
(请注意,列表的开头已成功创建,但在结尾的某个地方,它中断并以
null
结束)


这意味着它仍然不如用于构建列表的普通迭代那么好。这让我想到也许操纵大型不可变列表是不可能的。我经常听到Java开发人员说,最佳实践是使用不可变结构,然而,除了我提到的API之外,我找不到任何实现这一点的库,而且我自己也无法让它工作。其他开发人员是如何取得这样的成就的?

以迭代方式而不是递归方式进行。(而且,“不可变列表”不必按照链接列表的方式设计。)

以迭代方式而不是递归方式进行设计。(另外,“不可变列表”不必像链表那样设计。)

不要使用递归。使用迭代循环:

class B {
    static List<Integer> incrementList(List<Integer> l) {
        ArrayList<Integer> elements = new ArrayList<Integer>();
        while (l != null) {
            elements.add(l.e + 1);
            l = l.next; 
        }
        List<Integer> result = null;
        for (int i = elements.length - 1; i >= 0; i--) {
            result = new List<Integer>(elements.get(i), result);
        }  
        return result;
    }
}
B类{
静态列表增量列表(列表l){
ArrayList元素=新的ArrayList();
while(l!=null){
增加(l.e+1);
l=l.next;
}
列表结果=空;
对于(int i=elements.length-1;i>=0;i--){
结果=新列表(elements.get(i),result);
}  
返回结果;
}
}

这就是说,这样的列表实现,特别是像标准的
java.util.List
这样的命名列表,并不是一个好主意。

不要使用递归。使用迭代循环:

class B {
    static List<Integer> incrementList(List<Integer> l) {
        ArrayList<Integer> elements = new ArrayList<Integer>();
        while (l != null) {
            elements.add(l.e + 1);
            l = l.next; 
        }
        List<Integer> result = null;
        for (int i = elements.length - 1; i >= 0; i--) {
            result = new List<Integer>(elements.get(i), result);
        }  
        return result;
    }
}
B类{
静态列表增量列表(列表l){
ArrayList元素=新的ArrayList();
while(l!=null){
增加(l.e+1);
l=l.next;
}
列表结果=空;
对于(int i=elements.length-1;i>=0;i--){
结果=新列表(elements.get(i),result);
}  
返回结果;
}
}

这就是说,这样的列表实现,特别是像标准的
java.util.List
,命名为List,并不是一个好主意。

将递归函数转换为循环(伪代码):


通常,不需要反转结果列表,因为顺序无关紧要,或者您还有另一个转换要做。

将递归函数转换为循环(伪代码):


通常,不需要反转结果列表,因为顺序无关紧要,或者您还有其他转换要做。

使用它非常不愉快(如OP的问题所示),它在公共属性中公开其内部,并使用一个已经在3个Java类中的2个类中使用的名称来引用另一个类,那么,如何创建一个易于使用的不可变列表呢?让我们在这里忽略“问题”这个名称,因为重命名它很简单。如果这些字段是不可变的,你为什么关心将它们私有化呢?我会使用Guava的ImutableList,而不是重新发明轮子。将字段公开,即使不会使类可变,也不可能更改其实现。这就是封装的全部要点:能够在不更改公共接口的情况下更改内部私有实现。我知道Guava,但这不是我想要的,它似乎只是提供了一个封装在类中的数组。我想从技术上来说,你可以把数组称为列表。。。我想要的是一个不可变的列表,即带有O(1)prepend操作,我的实现实现了这一点。使用它非常不愉快(正如OP的问题所示),它在公共属性中公开了它的内部,并且它使用了一个已经在三分之二的Java类中使用的名称来引用另一个,那么,如何创建一个易于使用的不可变列表呢?让我们在这里忽略“问题”这个名称,因为重命名它很简单。如果这些字段是不可变的,你为什么关心将它们私有化呢?我会使用Guava的ImutableList,而不是重新发明轮子。将字段公开,即使不会使类可变,也不可能更改其实现。这就是封装的全部要点:能够在不更改公共接口的情况下更改内部私有实现。我知道Guava,但这不是我想要的,它似乎只是提供了一个封装在类中的数组。我想从技术上来说,你可以把数组称为列表。。。我想要的是一个不可变的列表,即带有O(1)前置操作的列表,这是我的实现所实现的。
class B {
    static List<Integer> incrementList(List<Integer> l) {
        return ListIncrementer.call(l);
    }
}

class ListIncrementer extends Thread {
    List<Integer> l;
    List<Integer> result;
    ListIncrementer(List<Integer> l) {
        this.l = l;
    }
    public void run() {
        if (l == null) {
            result = null;
            return;
        }
        result = new List<Integer>(l.e+1,call(l.next));
    }
    static List<Integer> call(List<Integer> l) {
        ListIncrementer li = new ListIncrementer(l);
        li.start();
        try { li.join(); } catch (Exception _) {}
        return li.result;
    }
}
javac A.java && java A
0 -> 1
1 -> 2
2 -> 3
Exception in thread "main" java.lang.NoSuchMethodError: main
Exception in thread "Thread-32290" java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method)
    at java.lang.Thread.start(Thread.java:657)
    at ListIncrementer.call(A.java:25)
    at ListIncrementer.run(A.java:21)
0 -> 1
1 -> 2
2 -> 3
Exception in thread "main" java.lang.NoSuchMethodError: main
class B {
    static List<Integer> incrementList(List<Integer> l) {
        ArrayList<Integer> elements = new ArrayList<Integer>();
        while (l != null) {
            elements.add(l.e + 1);
            l = l.next; 
        }
        List<Integer> result = null;
        for (int i = elements.length - 1; i >= 0; i--) {
            result = new List<Integer>(elements.get(i), result);
        }  
        return result;
    }
}
List increment(List in) {
    List out = new empty list;

    while (in is not empty) {
         out = new out(in.e+1, out);
         in = in.next;
    }
    return need it in same order as before? reverse(out) : out;
}