JavaRush /Blog Java /Random-PL /podciąg(..) nie dawał mi spokoju
IgorBrest
Poziom 33

podciąg(..) nie dawał mi spokoju

Opublikowano w grupie Random-PL
Właściwie ze względu na temat pozwoliłem sobie zagłębić się w String.substring(..). I prawdopodobnie doszedłem do nieoczekiwanych rezultatów, którymi postanowiłem się z wami podzielić, drodzy Jaworaszowici. Przedstaw to swojemu osądowi, że tak powiem. Więc oto jest. Istnieje instrukcja, że ​​ciąg utworzony za pomocą metody substring(..) wykorzystuje tablicę znaków oryginalnego ciągu. W szczególności tutaj znajduje się fragment niedawno przeczytanego artykułu „Java Reference. Static Strings” autorstwa szanowanych artykułów :
Jest uwaga na temat metody substring - zwrócony ciąg używa tej samej tablicy bajtów, co oryginalny
I oczywiście wykłady Javarasha. Oto cytaty z 22 grudnia:
Kiedy tworzymy podciąg przy użyciu metody substring, tworzony jest nowy obiekt String. Zamiast jednak przechowywać odniesienie do tablicy z nowym zestawem znaków, obiekt ten przechowuje odniesienie do starej tablicy znaków i jednocześnie przechowuje dwie zmienne, za pomocą których określa, która część oryginalnej tablicy znaków do niego należy. ... Kiedy tworzony jest podciąg, tablica znaków nie jest kopiowana do nowego obiektu String. Zamiast tego oba obiekty przechowują odwołanie do tej samej tablicy znaków. Ale! Drugi obiekt przechowuje jeszcze dwie zmienne, które określają, jakie i ile znaków tej tablicy zostanie do niego zapisanych. ... Dlatego jeśli weźmiesz ciąg o długości 10 000 znaków i utworzysz z niego 10 000 podciągów o dowolnej długości, to te „podciągi” zajmą bardzo mało pamięci, ponieważ tablica znaków nie jest powielana. Ciągi znaków, które powinny zajmować mnóstwo miejsca, zajmą tylko kilka bajtów.
wszystko jest wyraźnie napisane, nawet przeżute. Ponieważ jednak staram się doskonalić swoją znajomość języka angielskiego, często sięgam do oficjalnej dokumentacji i jakoś nie mogę znaleźć potwierdzenia tego faktu... Tłumacząc to swoją nieostrożnością, wciąż zaglądałem do kodu źródłowego programu substring() (dzięki IDEA pozwala to zrobić jednym kliknięciem przycisku). 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); } Zaintrygowany poszedłem dalej: * 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); } gdzie Arrays.copyOfRange to natywna metoda zwracająca kopię tablicy z char... Kod dość trywialny, a dla mnie wydawało się oczywiste, że po prostu tworzony jest nowy wiersz z nowym zestawem znaków. albo czegoś nie wziąłem pod uwagę... Więc nie do końca wierząc we własne wnioski, postanowiłem jakoś przetestować ten podciąg(), opierając się na frazie z wykładu:
Dlatego jeśli weźmiesz ciąg o długości 10 000 znaków i utworzysz z niego 10 000 podciągów o dowolnej długości, wówczas te „podciągi” zajmą bardzo mało pamięci...
tylko zamiast 10_000 od razu zrobimy 100_000_000, po co tracić czas na drobiazgi. Szybko wrzuciłem następujący kod: i tak się stało: tj. Za każdym razem, gdy tworzysz nowy podciąg za pomocą bigString.substring(..), tablica znaków jest DUPLIKOWANA. Jak inaczej wytłumaczyć taki wzrost zużycia pamięci? Po tym osobiście nie miałem już żadnych wątpliwości co do działania metody String.substsring().A Ty? 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); //если этот метод не создает w pełni новый массив символов, а только пользуется //исходным из 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
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION