JavaRush /Java 博客 /Random-ZH /子字符串(..)困扰着我
IgorBrest
第 33 级

子字符串(..)困扰着我

已在 Random-ZH 群组中发布
实际上,由于主题的原因,我允许自己深入研究 String.substring(..)。我可能得到了意想不到的结果,我决定与你们分享,亲爱的 Javorashovites。可以这么说,将其呈现给您的判断。所以就是这样。有一种说法是,使用 substring(..) 方法创建的字符串使用原始字符串的字符数组。这里特别摘录自最近阅读的文章“Java Reference.Static Strings”,作者是受人尊敬的文章
关于 substring 方法有一个注释 - 返回的字符串使用与原始字符串相同的字节数组
当然,还有 Javarash 讲座。以下是 12 月 22 日的引述:
当我们使用 substring 方法创建子字符串时,就会创建一个新的 String 对象。但是,该对象不是存储对具有新字符集的数组的引用,而是存储对旧字符数组的引用,同时存储两个变量,用于确定原始字符数组的哪一部分属于它。...创建子字符串时,字符数组不会复制到新的 String 对象。相反,两个对象都存储对同一字符数组的引用。但!第二个对象存储另外两个变量,其中包含要写入该数组的哪些字符以及多少个字符。...因此,如果你取一个 10,000 个字符长的字符串并从中生成 10,000 个任意长度的子字符串,那么这些“子字符串”将占用很少的内存,因为 字符数组不重复。应该占用大量空间的字符串只会占用几个字节。
一切都写得很清楚,甚至咀嚼过。但是,由于我正在努力提高我的英语知识,我经常查阅官方文档,但不知何故我找不到这个事实的确认......将这归因于我的粗心,我仍然查看了源代码substring()(感谢 IDEA 允许您通过单击按钮来完成此操作)。 public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen); } 出于好奇,我更进一步: * Allocates a new {@code String} that contains characters from a subarray * of the character array argument. The {@code offset} argument is the * index of the first character of the subarray and the {@code count} * argument specifies the length of the subarray. The contents of the * subarray are copied; subsequent modification of the character array does * not affect the newly created string. public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); } 其中 Arrays.copyOfRange 是一个本机方法,它从 char 返回数组的副本...相当简单的代码,对我来说很明显,简单地创建了带有一组新字符的新行。或者我没有考虑到某些事情......所以,不完全相信我的结论,我决定以某种方式测试这个 substring() ,依靠讲座中的一个短语:
因此,如果你取一个 10,000 个字符长的字符串并从中生成 10,000 个任意长度的子字符串,那么这些“子字符串”将占用很少的内存......
只是我们会立即获得 100_000_000,而不是 10_000,为什么要在琐事上浪费时间呢。我很快输入了以下代码: 这就是发生的事情: 即 每次使用 bigString.substring(..) 创建新的子字符串时,字符数组都会重复。我们还能如何解释内存消耗的这种增加?从此以后,我个人对 String.substsring() 方法的操作不再有任何疑问,你呢? public class Test { public static void main(String[] args) { System.out.println("Начинаем:"); print(); System.out.println("********************************"); char[]big=new char[100_000_000];//создаем нормальный такой массив int j=0;//и заполняем этот массив всякой ерундой for (int k=0;k list=new ArrayList<>();//здесь будут ссылки на строки, что бы сборщик мусора не удалял //не используемые, по его мнению, строки. System.out.println("************************************"); System.out.println("Теперь будем создавть подстроки с помощью substring(..) и наблюдать," + "что же происходит с памятью"); for (int i = 2; i <10; i++) { //создаем подстроку, используя метод String.substring(..) String sub= bigString.substring(1,bigString.length()-1); //если этот метод не создает fully новый массив символов, а только пользуется //исходным из bigString // то при создании новой строки sub мы не будем наблюдать ощутипый расход памяти list.add(sub);//эти ссылки мы должны где нибудь хранить, иначе сборщик мусора //избавится от неипользуемых объктов String System.out.print(String.format("Создаем %d-ую подстроку, при этом ", i - 1)); print(); } System.out.println("***************************************"); print(); } static void print(){ System.out.println("Памяти используется "+(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/1024/1024 + " mb"); } } Начинаем: Памяти используется 0 mb ******************************** создал большую строку bigString на основе массива big. Теперь: Памяти используется 382 mb ************************************ Теперь будем создавть подстроки с помощью substring(..) и наблюдать,что же происходит с памятью Добавляем 1-ую подстроку, при этом Памяти используется 573 mb Добавляем 2-ую подстроку, при этом Памяти используется 763 mb Добавляем 3-ую подстроку, при этом Памяти используется 954 mb Добавляем 4-ую подстроку, при этом Памяти используется 1145 mb Добавляем 5-ую подстроку, при этом Памяти используется 1336 mb Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Arrays.java:3658) at java.lang.String. (String.java:201) at java.lang.String.substring(String.java:1956) at com.javarush.test.tests.Test.main(Test.java:42) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) Process finished with exit code 1
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION