У чому головна відмінність між StringBuffer
і StringBuilder
? Чи є якісь проблеми щодо ефективності при вирішенні будь-якого з них?
У чому головна відмінність між StringBuffer
і StringBuilder
? Чи є якісь проблеми щодо ефективності при вирішенні будь-якого з них?
Відповіді:
StringBuffer
синхронізовано, StringBuilder
ні.
StringBuilder
швидше, ніж StringBuffer
тому, що це не так synchronized
.
Ось простий тест на тест:
public class Main {
public static void main(String[] args) {
int N = 77777777;
long t;
{
StringBuffer sb = new StringBuffer();
t = System.currentTimeMillis();
for (int i = N; i --> 0 ;) {
sb.append("");
}
System.out.println(System.currentTimeMillis() - t);
}
{
StringBuilder sb = new StringBuilder();
t = System.currentTimeMillis();
for (int i = N; i > 0 ; i--) {
sb.append("");
}
System.out.println(System.currentTimeMillis() - t);
}
}
}
Тестовий прогін дає номера 2241 ms
для StringBuffer
проти 753 ms
для StringBuilder
.
--> 0
в циклі. Знадобився момент, щоб зрозуміти, що це означає. Це щось, що насправді використовується на практиці замість звичного ...; i > 0; i--
синтаксису?
i -->
насправді дратує синтаксис ... Спочатку я думав, що це стріла через коментарі про мистецтво ASCII.
main()
Крім того, ваш орієнтир несправедливий. Розминки немає.
В основному StringBuffer
методи синхронізуються при цьомуStringBuilder
їх немає.
Операції «майже» однакові, але використання синхронізованих методів в одному потоці є надмірним.
Це майже про це.
Цитата від API StringBuilder :
Цей клас [StringBuilder] надає API, сумісний зі StringBuffer, але без гарантії синхронізації . Цей клас розроблений для використання в якості замінного для заміни StringBuffer у місцях, де буфер рядків використовувався одним потоком (як це зазвичай буває). Там, де це можливо, рекомендується використовувати цей клас у перевазі StringBuffer, оскільки він буде швидшим у більшості реалізацій.
Тож його було зроблено замінити.
Те саме сталося з Vector
і ArrayList
.
Hashtable
і HashMap
.
Але потрібно було зрозуміти різницю за допомогою прикладу?
StringBuffer або StringBuilder
Просто використовуйте, StringBuilder
якщо ви дійсно не намагаєтеся поділити буфер між потоками. StringBuilder
- це несинхронізований (менш накладний = ефективніший) молодший брат оригінального синхронізованого StringBuffer
класу.
StringBuffer
прийшов першим. Sun заклопотаний правильністю за будь-яких умов, тому вони зробили це синхронізованим, щоб зробити його безпечним для ниток на всякий випадок.
StringBuilder
прийшли пізніше. Більшість застосуваньStringBuffer
були однопоточними і без потреби виплачували вартість синхронізації.
Так StringBuilder
як це заміна для випадання дляStringBuffer
без синхронізації, то не було б різниці між будь-якими прикладами.
Якщо будуть намагатися частки між потоками, ви можете використовувати StringBuffer
, але розглянути питання про необхідність синхронізації більш високого рівня, наприклад , може бути , замість того , щоб використовувати StringBuffer, ви повинні синхронізувати методи , які використовують StringBuilder.
Спочатку давайте побачимо схожість : і StringBuilder, і StringBuffer є змінними. Це означає, що ви можете змінити вміст у них в одному і тому ж місці.
Відмінності : StringBuffer також змінюється і синхронізується. Де StringBuilder не змінюється, але за замовчуванням не синхронізується.
Значення синхронізованого (синхронізація) : Коли якась річ синхронізована, то декілька потоків можуть отримати доступ та змінити її, усунувши будь-яку проблему чи побічний ефект. StringBuffer синхронізований, тому ви можете використовувати його з декількома потоками, щоб вирішити будь-яку проблему.
Кого використовувати, коли? StringBuilder: Коли вам потрібна рядок, яку можна змінити, і лише одна нитка має доступ до неї та змінює її. StringBuffer: коли вам потрібна рядок, яку можна змінити, і кілька потоків мають доступ до неї та змінюють її.
Примітка : Не використовуйте StringBuffer без необхідності, тобто не використовуйте його, якщо лише один потік модифікує та отримує доступ до нього, оскільки він має безліч кодів блокування та розблокування для синхронізації, що зайво займе час процесора. Не використовуйте замки, якщо цього не потрібно.
В одиночних потоках StringBuffer не є значно повільнішим за StringBuilder , завдяки оптимізаціям JVM. І в багатопотоковому режимі ви не можете безпечно використовувати StringBuilder.
Ось мій тест (не тест, а лише тест):
public static void main(String[] args) {
String withString ="";
long t0 = System.currentTimeMillis();
for (int i = 0 ; i < 100000; i++){
withString+="some string";
}
System.out.println("strings:" + (System.currentTimeMillis() - t0));
t0 = System.currentTimeMillis();
StringBuffer buf = new StringBuffer();
for (int i = 0 ; i < 100000; i++){
buf.append("some string");
}
System.out.println("Buffers : "+(System.currentTimeMillis() - t0));
t0 = System.currentTimeMillis();
StringBuilder building = new StringBuilder();
for (int i = 0 ; i < 100000; i++){
building.append("some string");
}
System.out.println("Builder : "+(System.currentTimeMillis() - t0));
}
Результати:
рядки: 319740
Буфери: 23
Builder: 7!
Таким чином, Builders швидше, ніж буфери, і WAY швидше, ніж з'єднання рядків. Тепер давайте використаємо Виконавця для декількох потоків:
public class StringsPerf {
public static void main(String[] args) {
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
//With Buffer
StringBuffer buffer = new StringBuffer();
for (int i = 0 ; i < 10; i++){
executorService.execute(new AppendableRunnable(buffer));
}
shutdownAndAwaitTermination(executorService);
System.out.println(" Thread Buffer : "+ AppendableRunnable.time);
//With Builder
AppendableRunnable.time = 0;
executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
StringBuilder builder = new StringBuilder();
for (int i = 0 ; i < 10; i++){
executorService.execute(new AppendableRunnable(builder));
}
shutdownAndAwaitTermination(executorService);
System.out.println(" Thread Builder: "+ AppendableRunnable.time);
}
static void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // code reduced from Official Javadoc for Executors
try {
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (Exception e) {}
}
}
class AppendableRunnable<T extends Appendable> implements Runnable {
static long time = 0;
T appendable;
public AppendableRunnable(T appendable){
this.appendable = appendable;
}
@Override
public void run(){
long t0 = System.currentTimeMillis();
for (int j = 0 ; j < 10000 ; j++){
try {
appendable.append("some string");
} catch (IOException e) {}
}
time+=(System.currentTimeMillis() - t0);
}
}
Тепер StringBuffers займає 157 мс для 100000 додатків. Це не той самий тест, але порівняно з попередніми 37 мс, можна сміливо припускати, що додавання StringBuffers повільніше при багатопотоковому використанні . Причина полягає в тому, що JIT / точка доступу / компілятор / щось робить оптимізацію, коли виявляє, що немає необхідності перевіряти блокування.
Але з StringBuilder у вас є java.lang.ArrayIndexOutOfBoundsException , оскільки паралельна нитка намагається додати щось там, де не слід.
Висновок полягає в тому, що вам не доведеться переслідувати StringBuffers. І де у вас є нитки, подумайте, що вони роблять, перш ніж намагатися набрати кілька наносекунд.
withString+="some string"+i+" ; ";
це не еквівалентно двом іншим петлям і тому не є справедливим порівнянням.
StringBuilder був представлений на Java 1.5, тому він не працюватиме з більш ранніми JVM.
З Javadocs :
Клас StringBuilder надає API, сумісний зі StringBuffer, але без гарантії синхронізації. Цей клас розроблений для використання в якості замінного для заміни StringBuffer у місцях, де буфер рядків використовувався одним потоком (як це зазвичай буває). Там, де це можливо, рекомендується використовувати цей клас у перевазі StringBuffer, оскільки він буде швидшим у більшості реалізацій.
StringBuilder
.
Досить гарне запитання
Ось відмінності я помітив:
StringBuffer: -
StringBuffer is synchronized
StringBuffer is thread-safe
StringBuffer is slow (try to write a sample program and execute it, it will take more time than StringBuilder)
StringBuilder: -
StringBuilder is not synchronized
StringBuilder is not thread-safe
StringBuilder performance is better than StringBuffer.
Загальна річ: -
Обидва мають однакові методи з однаковими підписами. Обидва є змінними.
StringBuffer
StringBuilder
StringBuffer
без будь-яких інших змінappend
два рази, або append
і toString
НЕ небезпечно.
StringBuilder не є безпечним для потоків. Строковий буфер є. Більше інформації тут .
EDIT: Що стосується продуктивності, після того, як гаряча точка починається, StringBuilder є переможцем. Однак для невеликих ітерацій різниця в продуктивності незначна.
StringBuilder
і StringBuffer
майже однакові. Різниця в тому, що StringBuffer
синхронізовано і StringBuilder
ні. Хоча, StringBuilder
швидше, ніж StringBuffer
різниця в продуктивності дуже мала. StringBuilder
є заміною SUN StringBuffer
. Це просто уникає синхронізації з усіх публічних методів. Замість цього їх функціональність однакова.
Приклад вдалого використання:
Якщо ваш текст буде змінено і використовується декількома нитками, тоді краще використовувати StringBuffer
. Якщо ваш текст буде змінено, але він використовується однією ниткою, тоді використовуйте StringBuilder
.
StringBuffer
StringBuffer є змінним - це означає, що можна змінити значення об'єкта. Об'єкт, створений через StringBuffer, зберігається у купі. StringBuffer має ті ж методи, що і StringBuilder, але кожен метод у StringBuffer синхронізований, тобто StringBuffer є безпечним для потоків.
через це він не дозволяє двом потокам одночасно отримати доступ до одного методу. Кожен метод може отримати доступ по одній нитці одночасно.
Але безпека для потоків має і недоліки, як і ефективність звернень StringBuffer завдяки властивості безпечного потоку. Таким чином, StringBuilder швидше, ніж StringBuffer, коли викликає однакові методи кожного класу.
Значення StringBuffer можна змінити, це означає, що воно може бути присвоєне новому значенню. В даний час найпоширенішим питанням для інтерв'ю є відмінності між вищезазначеними класами. Строковий буфер можна перетворити в рядок за допомогою методу toString ().
StringBuffer demo1 = new StringBuffer(“Hello”) ;
// The above object stored in heap and its value can be changed .
demo1=new StringBuffer(“Bye”);
// Above statement is right as it modifies the value which is allowed in the StringBuffer
StringBuilder
StringBuilder такий же, як StringBuffer, тобто він зберігає об'єкт у купі, і його також можна змінювати. Основна відмінність StringBuffer від StringBuilder полягає в тому, що StringBuilder також не є безпечним для потоків. StringBuilder швидкий, оскільки він не є безпечним для потоків.
StringBuilder demo2= new StringBuilder(“Hello”);
// The above object too is stored in the heap and its value can be modified
demo2=new StringBuilder(“Bye”);
// Above statement is right as it modifies the value which is allowed in the StringBuilder
String
є незмінним.
StringBuffer
є мутаційним та синхронізованим.
StringBuilder
також є змінним, але не синхронізований.
Javadoc пояснює різницю:
Цей клас надає API, сумісний зі StringBuffer, але без гарантії синхронізації. Цей клас розроблений для використання в якості замінного для заміни StringBuffer у місцях, де буфер рядків використовувався одним потоком (як це зазвичай буває). Там, де це можливо, рекомендується використовувати цей клас у перевазі StringBuffer, оскільки він буде швидшим у більшості реалізацій.
StringBuilder
(введено в Java 5) ідентично StringBuffer
, за винятком того, що його методи не синхронізовані. Це означає, що він має кращі показники, ніж останні, але недолік у тому, що він не є безпечним для потоків.
Прочитайте підручник для більш детальної інформації.
Проста програма, що ілюструє різницю між StringBuffer і StringBuilder:
/**
* Run this program a couple of times. We see that the StringBuilder does not
* give us reliable results because its methods are not thread-safe as compared
* to StringBuffer.
*
* For example, the single append in StringBuffer is thread-safe, i.e.
* only one thread can call append() at any time and would finish writing
* back to memory one at a time. In contrast, the append() in the StringBuilder
* class can be called concurrently by many threads, so the final size of the
* StringBuilder is sometimes less than expected.
*
*/
public class StringBufferVSStringBuilder {
public static void main(String[] args) throws InterruptedException {
int n = 10;
//*************************String Builder Test*******************************//
StringBuilder sb = new StringBuilder();
StringBuilderTest[] builderThreads = new StringBuilderTest[n];
for (int i = 0; i < n; i++) {
builderThreads[i] = new StringBuilderTest(sb);
}
for (int i = 0; i < n; i++) {
builderThreads[i].start();
}
for (int i = 0; i < n; i++) {
builderThreads[i].join();
}
System.out.println("StringBuilderTest: Expected result is 1000; got " + sb.length());
//*************************String Buffer Test*******************************//
StringBuffer sb2 = new StringBuffer();
StringBufferTest[] bufferThreads = new StringBufferTest[n];
for (int i = 0; i < n; i++) {
bufferThreads[i] = new StringBufferTest(sb2);
}
for (int i = 0; i < n; i++) {
bufferThreads[i].start();
}
for (int i = 0; i < n; i++) {
bufferThreads[i].join();
}
System.out.println("StringBufferTest: Expected result is 1000; got " + sb2.length());
}
}
// Every run would attempt to append 100 "A"s to the StringBuilder.
class StringBuilderTest extends Thread {
StringBuilder sb;
public StringBuilderTest (StringBuilder sb) {
this.sb = sb;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
sb.append("A");
}
}
}
//Every run would attempt to append 100 "A"s to the StringBuffer.
class StringBufferTest extends Thread {
StringBuffer sb2;
public StringBufferTest (StringBuffer sb2) {
this.sb2 = sb2;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
sb2.append("A");
}
}
}
StringBuffer використовується для зберігання символьних рядків, які будуть змінені (String-об'єкти неможливо змінити). Він автоматично розширюється за потреби. Суміжні класи: String, CharSequence.
StringBuilder був доданий в Java 5. Він ідентичний у всіх відношеннях до StringBuffer, за винятком того, що він не синхронізований, а це означає, що якщо кілька потоків отримують доступ до нього одночасно, можуть виникнути проблеми. Для однопотокових програм найпоширеніший випадок, уникаючи накладних витрат синхронізації, робить StringBuilder дуже трохи швидшим.
StringBuilder
зазвичай локальний для методу, де вони видно лише одному потоку.
StringBuffer
синхронізовано, але StringBuilder
це не так. Як результат, StringBuilder
швидше, ніж StringBuffer
.
StringBuffer змінюється. Він може змінюватися з точки зору тривалості та змісту. StringBuffers безпечні для потоків, тобто вони мають синхронізовані методи управління доступом, щоб лише один потік одночасно мав доступ до синхронізованого коду об'єкта StringBuffer. Таким чином, об'єкти StringBuffer, як правило, безпечні для використання у багатопотоковому середовищі, де кілька потоків можуть намагатися отримати доступ до того ж об’єкта StringBuffer одночасно.
StringBuilder Клас StringBuilder дуже схожий на StringBuffer, за винятком того, що його доступ не синхронізований, щоб він не був безпечним для потоків. Не синхронізуючись, продуктивність StringBuilder може бути кращою, ніж StringBuffer. Таким чином, якщо ви працюєте в однопотоковому середовищі, використання StringBuilder замість StringBuffer може призвести до підвищення продуктивності. Це також стосується інших ситуацій, таких як локальна змінна StringBuilder (тобто змінна в межах методу), коли лише один потік матиме доступ до об’єкта StringBuilder.
StringBuffer:
StringBuilder
String c = a + b
рівнозначно String c = new StringBuilder().append(a).append(b).toString()
, тому не швидше. Це лише те, що ви створюєте нове для кожного призначення рядків, тоді як у вас може бути лише одне ( String d = a + b; d = d + c;
це в String d = new StringBuilder().append(a).append(b).toString(); d = new StringBuilder().append(d).append(c).toString();
той час як StringBuilder sb = new StringBuilder(); sb.append(a).append(b); sb.append(c); String d = sb.toString();
збереже одну інстанцію StringBuilder).
String-Builder :
int one = 1;
String color = "red";
StringBuilder sb = new StringBuilder();
sb.append("One=").append(one).append(", Color=").append(color).append('\n');
System.out.print(sb);
// Prints "One=1, Colour=red" followed by an ASCII newline.
Рядок-буфер
StringBuffer sBuffer = new StringBuffer("test");
sBuffer.append(" String Buffer");
System.out.println(sBuffer);
Рекомендується використовувати StringBuilder, коли це можливо, оскільки це швидше, ніж StringBuffer. Однак якщо безпека потоку необхідна, найкращим варіантом є об'єкти StringBuffer.
Краще використовувати, StringBuilder
оскільки він не синхронізований, а тому забезпечує кращі показники. StringBuilder
- це заміна заміни старшого StringBuffer
.
StringBu(ff|ild)er
це локальна змінна, яка використовується лише одним потоком.
Так як StringBuffer
синхронізація, вона потребує в деяких додаткових зусиль, отже , на основі perforamance, його трохи повільно ніж StringBuilder
.
Там немає принципових відмінностей між StringBuilder
і StringBuffer
, лише кілька відмінностей між ними існують. У StringBuffer
методах синхронізовані. Це означає, що одночасно над ними може працювати лише одна нитка. Якщо є більше однієї нитки, то другий потік повинен буде зачекати, коли перший закінчить, а третій повинен буде дочекатися закінчення першого та другого тощо. Це робить процес дуже повільним, а отже, продуктивність у випадку StringBuffer
низької.
З іншого боку, StringBuilder
не синхронізований. Це означає, що одночасно на одному StringBuilder
об’єкті можуть працювати одночасно кілька потоків . Це робить процес дуже швидким і, отже, ефективність роботи StringBuilder
висока.
A String
є незмінним об'єктом, що означає, що значення не може бути змінено, тоді як StringBuffer
є змінним.
StringBuffer
Синхронізований , отже , поточно-то StringBuilder
немає і підходить тільки для однопоточних примірників.
Основна відмінність - StringBuffer
це синхронізована, але StringBuilder
це не так. Якщо вам потрібно використовувати більше ніж один потік, то рекомендується StringBuffer. Але на швидкість виконання StringBuilder
швидше StringBuffer
, тому що вона не синхронізована.
Перевірте внутрішні методи синхронізованого способу додавання StringBuffer
та несинхронізованого методу додавання StringBuilder
.
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public synchronized StringBuffer append(Object obj) {
super.append(String.valueOf(obj));
return this;
}
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
Оскільки додавання є synchronized
, StringBuffer
ефективність перевищує порівняно зі StrinbBuilder
сценарієм з багатопотоковою резьбою. Поки ви не поділяєте буфер між декількома потоками, використовуйте StringBuilder
, що швидко через відсутність synchronized
методів додавання.
Ось результат тестування продуктивності для String vs StringBuffer vs StringBuilder . Нарешті, StringBuilder виграв Тест. Нижче див. Тестовий код та результат.
Код :
private static void performanceTestStringVsStringbuffereVsStringBuilder() {
// String vs StringBiffer vs StringBuilder performance Test
int loop = 100000;
long start = 0;
// String
String str = null;
start = System.currentTimeMillis();
for (int i = 1; i <= loop; i++) {
str += i + "test";
}
System.out.println("String - " + (System.currentTimeMillis() - start) + " ms");
// String buffer
StringBuffer sbuffer = new StringBuffer();
start = System.currentTimeMillis();
for (int i = 1; i <= loop; i++) {
sbuffer.append(i).append("test");
}
System.out.println("String Buffer - " + (System.currentTimeMillis() - start) + " ms");
// String builder
start = System.currentTimeMillis();
StringBuilder sbuilder = new StringBuilder();
for (int i = 1; i <= loop; i++) {
sbuffer.append(i).append("test");
}
System.out.println("String Builder - " + (System.currentTimeMillis() - start) + " ms");
}
Результат :
100000 ітерацій для додавання одного тексту
String - 37489 ms
String Buffer - 5 ms
String Builder - 4 ms
10000 ітерацій для додавання одного тексту
String - 389 ms
String Buffer - 1 ms
String Builder - 1 ms