* Динамични низове (StringBuffer, StringBuilder и StringJoiner)
Публикувано на 27 септември 2009 в раздел ПИК3 Java.
Недостатъците породени от непроменимост на низовете от тип String, за които споменахме в миналата статия, все пак могат да бъдат преодоляни. За целта съществува обект наречен StringBuffer. Обекти от този тип са вече истински динамични масиви от символи и поради тази причина можем директно да ги променяме без да има нужда от копиране на целия обект в нов буфер.
За добавяне на низ в края на низ създаден със StringBuffer се използва метод append():
StringBuffer strb = new StringBuffer(); strb.append("Hello"); strb.append(" World!"); System.out.println(strb);
При създаването на StringBuffer се заделя първоначално място за големина на масива. При достигане до края на масива той се разширява автоматично. При такава операция обаче има потенциален проблем - възможно е да има друг обект в паметта, който е с адрес близък до разширявания StringBuffer. В такъв случай обекта StringBuffer ще трябва да бъде преместен на ново място. Можем да се справим с този проблем когато при дефинирането му заделим достатъчна големина. Например ако смятаме, че ще запишем до 200 символа, то ще създадем обекта по следния начин:
StringBuffer strb1 = new StringBuffer(); System.out.println(strb1.capacity()); StringBuffer strb2 = new StringBuffer(200); System.out.println(strb2.capacity());
Трябва да обърнем внимание на разликата между методите length() и capacity(). Length() връща броят символи, които са записани в StringBuffer, а capacity() връща общата му дължина (включително празните символи, които сме резервирали).
Възможно е и промяна на размера на StringBuffer и след инициализирането му. Това става чрез метод setLength(). Трябва да се знае, че ако сложим размер по-малък от текущия брой записани символи, то края на текста ще бъде отрязан. Следният пример ще изкара резултат "Philip P":
StringBuffer strb = new StringBuffer(); strb.append("Philip Petrov"); strb.setLength(8); System.out.println(strb);
StringBuffer ни позволява и да вмъкваме текст вътре в съществуващ низ. Това се получава с метод insert(). Примерът по-долу вмъква String в StringBuffer на индекс 6:
StringBuffer strb = new StringBuffer(); strb.append("Philip Petrov"); strb.insert(6, " Petrov"); System.out.println(strb);
Ако желаем да променим символ, то използваме метод setCharAt(). Той приема за аргументи индекс и нов символ. Следният пример заменя всички цифри със звездички:
StringBuffer strb = new StringBuffer(); strb.append("Philip Petrov, gsm: 0899 488 368, cc: 4111 1111 1111"); for (int i=0; i<strb.length(); i++){ if (strb.charAt(i)>='0' && strb.charAt(i)<='9'){ strb.setCharAt(i, '*'); } } System.out.println(strb);
Ако желаем да изтрием подниз, то използваме метод delete(). Следният пример връща резултат "Hellrld":
StringBuffer strb = new StringBuffer(); strb.append("Hello World"); strb.delete(4,8); System.out.println(strb);
StringBuffer има и готов метод за обръщане на низ в обратен ред:
StringBuffer strb = new StringBuffer(); strb.append("Hello World"); strb.reverse(); System.out.println(strb);
StringBuffer притежава и трите метода налични и при String - substring(), indexOf() и lastIndexOf(). За тях няма да даваме примери - напълно аналогични са на тези на обект от тип String. Накрая за да превърнем един StringBuffer в String използваме метод toString():
StringBuffer strb = new StringBuffer(); strb.append("Hello World"); String str = strb.toString();
Сега обикновено всеки си задава въпроса "защо не използваме само StringBuffer след като има толкова предимства спрямо String?". Отговорът е в бързодействието на програмата. Истината е, че в реални програми на нас ни се налага рядко да работим активно с динамични низове. Те от своя страна изискват много повече процесорен ресурс, за да бъдат поддържани. Една от основните причини за това е, че в StringBuffer е реализирана и функционалност за синхронизиране, т.е. ако две или повече нишки се опитат да променят един и същи StringBuffer по едно и също време, то те ще трябва да се изчакват.
Междинен вариант за решение между String и StringBuffer се нарича сравнително новия за Java обект StringBuilder. Методите които този обект притежава са същите, както при StringBuffer, т.е. той също е динамичен масив от символи. Работата с него обаче е значително по-бърза, защото StringBuilder не извършва синхронизация. Затова той е изключително подходящ при програми, които не са многонишкови. За нишки (threads) ще говорим в по-следваща статия. Няма да даваме примери за StringBuilder, защото са напълно аналогични на показаните по-горе (просто заместете думата "StringBuffer" със "StringBuilder").
Добавено през декември 2015
В Java 8 е добавен и още един клас, който добавя удобни функционалности - java.util.StringJoiner. Както подсказва името му, той се използва за конкатенация на низове. При създаване на обекта се обозначава какъв ще е разделителя:
StringJoiner strj = new StringJoiner(", "); strj.add("Petar"); strj.add("Ivan").add("Maria"); // Petar, Ivan, Maria System.out.println(strj.toString());
Можете да добавите представка и надставка като втори и трети параметър:
StringJoiner strj = new StringJoiner(",", "{", "}"); strj.add("1"); strj.add("2").add("2").add("3"); // {1,2,2,3} System.out.println(strj.toString());
Интересен е метод merge, който ни позволява да слеем два StringJoiner обекта. При него представката и надставката на първия обект взимат превес, а разделителите се запазват такива, каквито са били:
StringJoiner strj1 = new StringJoiner(",", "{", "}"); strj1.add("1").add("2").add("3"); StringJoiner strj2 = new StringJoiner(":", "(", ")"); strj2.add("4").add("5").add("6"); StringJoiner mergeStrj1and2 = strj1.merge(strj2); // {1,2,3,4:5:6} System.out.println(mergeStrj1and2.toString());
Добави коментар