• 避免subList/subString陷阱


    避免subList/subString陷阱

    • java.util.List 接口提供了一个实例方法 List<E> subList(int fromIndex, int toIndex), 用于截取 原List 的一个以 fromIndex 为起始索引,以 toIndex 为终止索引(不包括)的子列表。
    • subString 类似于 subList ,对 String 进行截取。

    subList/subString陷阱

    subString在本文涉及到的方面都类似于subList,不再赘述

    关于函数返回值使用的陷阱

    subList的实现代码在AbstractList类里,返回AbstractList的一个子类SubList

    class SubList<E> extends AbstractList<E>{
    	private AbstractList<E> l;
    	private int offset;
    	private int size;
    
    	//SubList类的构造方法
    	SubList(AbstractList list, int fromIndex, int toIndex){
    		//...
    		l = list;
    		offset = fromIndex;
    		size = toIndex - fromIndex;
    		//...
    	}
    
    //...
    

    根据以上代码,我们知道,SubList将原来的list赋值给了l,只是改变了偏移量offset。也就是说,subList函数返回了原始list对象的引用

    这也就意味着,对原list和返回的list做的“非结构性修改”(指不涉及list大小改变的修改),都会影响到彼此。

    //对返回的list排序,观察原list
    public static void main(String[] args)
    {
    	Scanner sc = new Scanner(System.in);
    	List<Integer> list = new ArrayList<>();
    	while(sc.hasNextInt())
    	{
    		list.add(sc.nextInt());
    	}
    	List<Integer> subList = new ArrayList<>();
    	subList = list.subList(0, list.size());
    	subList.sort(null);
    	for(int i = 0; i < list.size(); i++)
    		System.out.print(list.get(i) + " ");
    	sc.close();
    }
    
    //输入:
    1 3 2 5 4
    //输出:
    1 2 3 4 5 
    

    OOM问题

    看到这里读者可能会产生疑问,为什么要用原list的引用作为返回值?
    原因在于,这种实现可以提高运行时的性能;
    如果创建一个新的list,然后添加所需的内容,这种实现方法无论从内存消耗还是从运行效率来看都远不如直接引用原始list、设置offset和size的方法。

    然而,这种返回引用的方法可能会引起一个更加麻烦且难发现的错误:out of memory

    在SubList中,保存了原list的强引用,这意味着,在jvm进行垃圾回收时,只要有一个这样的SubList没有被回收,原List就不会被回收,从而占用这内存空间。当我们只需要长时间保留原List中的一小段数据,却用了subList,而除了SubList中的引用之外,没有其他的引用指向原List,这种情况下,就会造成严重的内存空间的浪费。

    可想而知,当这样的subList足够多,jvm没办法及时回收,这些subList就会吃掉所有内存。

    如何避免陷阱

    正确使用subString/subList

    • subList/subString适合处理局部的List/String时,比如删除局部的List :

      list.subList(begin, end).clear();
      
    • subList/subString的一个明显优势在于提高了运行的性能,不用进行冗长的复制过程。所以,如果对程序的性能要求高,可以考虑用subString/subList。

    避免使用subList/subString

    如果有截取整体中较小的一段来做操作而不改变原整体的需要,或有截取一小段长期保存在内存中的需要

    • 以subList为例:

      //...
      List<Integer> newList = new ArrayList<>();
      for(int i = begin; i < end; i++)
      {
      	//原List为list
      	newList.add(list.get(i));
      }
      //...
      
    • 另外,对于String,可以用new String()来构造一个新的String来复制需要的部分。

    update by 2017/3/30 19:33

    by 一棵球

  • 相关阅读:
    Officer Anany Collecting String Subsequences(ECPC2019)
    P3530 [POI2012]FESFestival
    L3011 直捣黄龙 (天梯赛)
    树与图上的计数专题
    P4657 [CEOI2017]Chase
    Steadily Growing Steam
    图论复习
    Tree Infection(CF)
    推导部分和(蓝桥杯)
    Baby Ehab's Whining Chance
  • 原文地址:https://www.cnblogs.com/chunzhulovefeiyue/p/6648104.html
Copyright © 2020-2023  润新知