抛出StackOverFlowError的java.util.Sublist

时间:2010-09-24 19:09:23

标签: java error-handling stack-overflow

我们在生产中偶尔会遇到与执行SubList操作相关的StackOverFlowError错误。有没有人见过这样的东西,知道是什么原因造成的?

这是被调用的代码,用于触发错误:

  FacesContext context = FacesContext.getCurrentInstance();
    String newViewID = context.getViewRoot().getViewId();

    if (newViewID != null) {
     if (breadCrumbs.contains(newViewID)) {
      // Trims the list upon going back to allow for multiple back button requests.  
      // This is lightweight and not intended for a complex circular navigation.
      breadCrumbs = breadCrumbs.subList(0, breadCrumbs.indexOf(newViewID) + 1);
     } else {
      breadCrumbs.add(newViewID);
     }
    }

结果:

Caused By: java.lang.StackOverflowError
 at java.util.SubList$1.<init>(AbstractList.java:688)
 at java.util.SubList.listIterator(AbstractList.java:687)
 at java.util.SubList$1.<init>(AbstractList.java:688)
 at java.util.SubList.listIterator(AbstractList.java:687)
 ...

6 个答案:

答案 0 :(得分:6)

subList()方法返回由原始列表支持的视图

根据javadoc:

  

返回的列表的语义   如果这个方法变得不确定   支持列表(即此列表)是   以任何方式进行结构修改   而不是通过返回的列表。   (结构修改是那些   这会改变这个列表的大小,或者   否则就会以这种方式扰乱它   正在进行的迭代可能会产生   结果不正确。)

您正在对列表进行结构更改,因此所有投注都已关闭 - 任何都可能发生,包括无限递归,这似乎正在发生。

答案 1 :(得分:2)

使用LinkedList标准库和fastutil objectarraylist我遇到了完全相同的问题(fastutil是Java集合框架的快速有效的内存实现)。

使用

window = window.subList(index+1, window.size());

导致stackoverflow错误。我换成了

window = new LinkedList<>( window.subList(index+1, window.size()) );

一切正常。

希望它可以提供帮助

答案 2 :(得分:0)

以下是相关来源的摘录:

681    public ListIterator<E> listIterator(final int index) {
...
687        return new ListIterator<E>() {
688            private ListIterator<E> i = l.listIterator(index+offset);

StackOverflowError表示l以某种方式引用当前子列表,因此在无限循环中调用自己的listIterator()

breadCrumbs来自哪里?它的getClass()说什么?

答案 3 :(得分:0)

问题是由breadCrumbs作为LinkedList引起的 - 我们向LinkedList添加了太多项,并且调用subList暴露了这个问题。

答案 4 :(得分:0)

我不认为这是因为LinkedList。我以递归方式对同一列表调用subList时遇到了同样的错误。我认为每次调用方法subList时,它的开始/结束索引都被推入堆栈。如果该列表很大,因此调用该方法的次数太多,就会发生StackOverFlowError。

答案 5 :(得分:0)

问题在于AbstractList.java(ArrayList的基类)实现subList方法的方式。它通过父指针,偏移量和大小创建子列表(也称为视图)。 如果在这样的子列表上调用subList,则会获得指向列表的父指针,该列表本身具有父指针(等等)

子列表上的某些操作(例如添加)会递归工作。如果你有一个非常深的父指针层次结构,你会得到一个StackOverflowError。

以下代码段显示了隔离的问题:

public static void main(String[] args) {
    List<String> lst = new ArrayList<String>();
    lst.add(""); 
    for (int i = 0; i < 50000; i++) {
        lst.set(0, "test");
        lst = lst.subList(0, 1);
    }

    lst.add("test2");       
}

结论:不要像这样递归地使用subList:

breadCrumbs = breadCrumbs.subList(0, breadCrumbs.indexOf(newViewID) + 1);

而是通过从末尾删除元素来设置长度。

我的博客上更详细的分析:http://programmingtipsandtraps.blogspot.com/2013/05/javautillistsublist-stackoverflowerror.html