C, PHP, VB, .NET

Дневникът на Филип Петров


* Динамични низове (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());

 



Добави коментар

Адресът на електронната поща няма да се публикува


*