实战小技巧:
List.subList使用不当StackOverflowError
相信每个小伙伴都使用过List.subList
来获取子列表,日常使用可能没啥问题,但是,请注意,它的使用,很可能一不小心就可能导致oom
1. subList
场景复现,如基于list实现一个小顶堆
1 | public List<Integer> minStack(List<Integer> list, int value, int stackSzie) { |
上面这个执行完毕之后,居然出现栈溢出
1 | // .... |
从实现来看,感觉也没啥问题啊, 我们稍微改一下上面的返回
1 | public List<Integer> minStack(List<Integer> list, int value, int stackSzie) { |
再次执行,却没有异常;所以关键点就在与
- list.subList的使用上
2. StackOverflowError分析
接下来我们主要看一下list.subList
的实现
1 | public List<E> subList(int fromIndex, int toIndex) { |
上面返回的子列表是ArrayList的一个内部类SubList
,它拥有一个指向父列表的成员parrent
也就是说,从源头的ArryList开始,后面每次调用subList
,这个指代关系就深一层
然后它的add方法也很有意思
1 | public void add(int index, E e) { |
重点看 parent.add(parentOffset + index, e);
,添加的数据实际上是加在最源头的ArrayList上的,也就是说,虽然你现在拿到的SubList,只有几个元素,但是它对应的数组,可能超乎你的想象
当然上面这个异常主要是以为调用栈溢出(一直往上找parent)
这里反应的另外一个重要问题则是内存泄漏,就不继续说了
如果需要解决上面这个问题,改造方法如下
1 | public List<E> subList(int fromIndex, int toIndex) { |
3. 小结
jdk提供的原生方法虽然非常好用,但是在使用的时候,也需要多家注意,一不小心就可能掉进坑里;这也告诉我们多看源码是有必要的
最后一句关键知识点小结:
ArrayList.subList
返回的是内部类,与原ArrayList公用一个数组,只是限定了这个数组的起始下标和结束下标而已- 在使用
subList
,请注意是否会存在内存泄露和栈溢出的问题
系列博文
- 实战小技巧1:字符串占位替换-JDK版
- 实战小技巧2:数组与list互转
- 实战小技巧 3:字符串与容器互转
- 实战小技巧4:优雅的实现字符串拼接
- 实战小技巧5:驼峰与下划线互转
- 实战小技巧6:枚举的特殊用法
- 实战小技巧7:排序比较需慎重
II. 其他
1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840
3. 扫描关注
一灰灰blog