Какой самый быстрый способ объединить две строки в Java?
Какой самый быстрый способ объединить две строки в Java?
i.e
String ccyPair = ccy1 + ccy2;
Я использую cyPair
как ключ в HashMap
, и он вызывает в очень сжатом цикле для получения значений.
Когда я просматриваю профиль, это узкое место
java.lang.StringBuilder.append(StringBuilder.java:119)
java.lang.StringBuilder.(StringBuilder.java:93)
Ответы
Ответ 1
Причина, по которой эти процедуры отображаются в бенчмарке, заключается в том, что именно так компилятор реализует ваш "+" под прикрытием.
Если вам действительно нужна объединенная строка, вы должны позволить компилятору сделать свою магию с помощью "+". Если вам всем нужен ключ для поиска на карте, класс ключей, содержащий обе строки с подходящими реализациями equals
и hashMap
может быть хорошей идеей, поскольку он избегает шага копирования.
Ответ 2
Много теории - время для некоторой практики!
private final String s1 = new String("1234567890");
private final String s2 = new String("1234567890");
Использование plain для петель 10 000 000, на разогретой 64-битной Hotspot, 1.6.0_22 на Intel Mac OS.
например,
@Test public void testConcatenation() {
for (int i = 0; i < COUNT; i++) {
String s3 = s1 + s2;
}
}
Со следующими инструкциями в циклах
String s3 = s1 + s2;
1.33s
String s3 = new StringBuilder(s1).append(s2).toString();
1.28s
String s3 = new StringBuffer(s1).append(s2).toString();
1.92s
String s3 = s1.concat(s2);
0.70s
String s3 = "1234567890" + "1234567890";
0,0
Итак, concat - явный победитель, если у вас нет статических строк, и в этом случае компилятор уже позаботился о вас.
Ответ 3
Я считаю, что ответ, возможно, уже был определен, но я отправляю сообщение для совместного использования кода.
Короткий ответ, если чисто конкатенация - это все, что вы ищете, это: String.concat(...)
Вывод:
ITERATION_LIMIT1: 1
ITERATION_LIMIT2: 10000000
s1: STRING1-1111111111111111111111
s2: STRING2-2222222222222222222222
iteration: 1
null: 1.7 nanos
s1.concat(s2): 106.1 nanos
s1 + s2: 251.7 nanos
new StringBuilder(s1).append(s2).toString(): 246.6 nanos
new StringBuffer(s1).append(s2).toString(): 404.7 nanos
String.format("%s%s", s1, s2): 3276.0 nanos
Tests complete
Пример кода:
package net.fosdal.scratch;
public class StringConcatenationPerformance {
private static final int ITERATION_LIMIT1 = 1;
private static final int ITERATION_LIMIT2 = 10000000;
public static void main(String[] args) {
String s1 = "STRING1-1111111111111111111111";
String s2 = "STRING2-2222222222222222222222";
String methodName;
long startNanos, durationNanos;
int iteration2;
System.out.println("ITERATION_LIMIT1: " + ITERATION_LIMIT1);
System.out.println("ITERATION_LIMIT2: " + ITERATION_LIMIT2);
System.out.println("s1: " + s1);
System.out.println("s2: " + s2);
int iteration1 = 0;
while (iteration1++ < ITERATION_LIMIT1) {
System.out.println();
System.out.println("iteration: " + iteration1);
// method #0
methodName = "null";
iteration2 = 0;
startNanos = System.nanoTime();
while (iteration2++ < ITERATION_LIMIT2) {
method0(s1, s2);
}
durationNanos = System.nanoTime() - startNanos;
System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));
// method #1
methodName = "s1.concat(s2)";
iteration2 = 0;
startNanos = System.nanoTime();
while (iteration2++ < ITERATION_LIMIT2) {
method1(s1, s2);
}
durationNanos = System.nanoTime() - startNanos;
System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));
// method #2
iteration2 = 0;
startNanos = System.nanoTime();
methodName = "s1 + s2";
while (iteration2++ < ITERATION_LIMIT2) {
method2(s1, s2);
}
durationNanos = System.nanoTime() - startNanos;
System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));
// method #3
iteration2 = 0;
startNanos = System.nanoTime();
methodName = "new StringBuilder(s1).append(s2).toString()";
while (iteration2++ < ITERATION_LIMIT2) {
method3(s1, s2);
}
durationNanos = System.nanoTime() - startNanos;
System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));
// method #4
iteration2 = 0;
startNanos = System.nanoTime();
methodName = "new StringBuffer(s1).append(s2).toString()";
while (iteration2++ < ITERATION_LIMIT2) {
method4(s1, s2);
}
durationNanos = System.nanoTime() - startNanos;
System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));
// method #5
iteration2 = 0;
startNanos = System.nanoTime();
methodName = "String.format(\"%s%s\", s1, s2)";
while (iteration2++ < ITERATION_LIMIT2) {
method5(s1, s2);
}
durationNanos = System.nanoTime() - startNanos;
System.out.println(String.format("%50s: %6.1f nanos", methodName, ((double) durationNanos) / ITERATION_LIMIT2));
}
System.out.println();
System.out.println("Tests complete");
}
public static String method0(String s1, String s2) {
return "";
}
public static String method1(String s1, String s2) {
return s1.concat(s2);
}
public static String method2(String s1, String s2) {
return s1 + s2;
}
public static String method3(String s1, String s2) {
return new StringBuilder(s1).append(s2).toString();
}
public static String method4(String s1, String s2) {
return new StringBuffer(s1).append(s2).toString();
}
public static String method5(String s1, String s2) {
return String.format("%s%s", s1, s2);
}
}
Ответ 4
Вы должны протестировать с помощью String, сгенерированной во время выполнения (например, UUID.randomUUID(). toString()) не во время компиляции (например, "моя строка" ). Мои результаты
plus: 118 ns
concat: 52 ns
builder1: 102 ns
builder2: 66 ns
buffer1: 119 ns
buffer2: 87 ns
с этой реализацией:
private static long COUNT = 10000000;
public static void main(String[] args) throws Exception {
String s1 = UUID.randomUUID().toString();
String s2 = UUID.randomUUID().toString();
for(String methodName : new String[] {
"none", "plus", "concat", "builder1", "builder2", "buffer1", "buffer2"
}) {
Method method = ConcatPerformanceTest.class.getMethod(methodName, String.class, String.class);
long time = System.nanoTime();
for(int i = 0; i < COUNT; i++) {
method.invoke((Object) null, s1, s2);
}
System.out.println(methodName + ": " + (System.nanoTime() - time)/COUNT + " ns");
}
}
public static String none(String s1, String s2) {
return null;
}
public static String plus(String s1, String s2) {
return s1 + s2;
}
public static String concat(String s1, String s2) {
return s1.concat(s2);
}
public static String builder1(String s1, String s2) {
return new StringBuilder(s1).append(s2).toString();
}
public static String builder2(String s1, String s2) {
return new StringBuilder(s1.length() + s2.length()).append(s1).append(s2).toString();
}
public static String buffer1(String s1, String s2) {
return new StringBuffer(s1).append(s2).toString();
}
public static String buffer2(String s1, String s2) {
return new StringBuffer(s1.length() + s2.length()).append(s1).append(s2).toString();
}
Ответ 5
Для вопроса в заголовке: String.concat
обычно будет самым быстрым способом конкатцирования двух String
(но заметьте null
s). Нет промежуточного буфера или другого объекта. Странно +
скомпилируется в относительно неэффективный код с участием StringBuilder
.
Однако, тело вашего вопроса указывает на другие проблемы. Конкатенация строк для создания ключей для карты является общей "анти-идиомой". Это взлом и склонность к ошибкам. Вы уверены, что сгенерированный ключ уникален? Будет ли он оставаться уникальным после того, как ваш код будет сохранен для какого-то еще неизвестного требования? Наилучший подход - создать неизменяемый класс значений для ключа. Использование класса List
и generic tuple - небрежный взлом.
Ответ 6
Для меня метод concat3, как показано ниже, является самым быстрым способом после выполнения теста на моих окнах и удаленной Linux-машине:
Хотя я считаю, что производительность concat1 - это реализация JVM и оптимизация, зависящая и может лучше работать в будущей версии
public class StringConcat {
public static void main(String[] args) {
int run = 100 * 100 * 1000;
long startTime, total = 0;
final String a = "a";
final String b = "assdfsaf";
final String c = "aasfasfsaf";
final String d = "afafafdaa";
final String e = "afdassadf";
startTime = System.currentTimeMillis();
concat1(run, a, b, c, d, e);
total = System.currentTimeMillis() - startTime;
System.out.println(total);
startTime = System.currentTimeMillis();
concat2(run, a, b, c, d, e);
total = System.currentTimeMillis() - startTime;
System.out.println(total);
startTime = System.currentTimeMillis();
concat3(run, a, b, c, d, e);
total = System.currentTimeMillis() - startTime;
System.out.println(total);
}
private static void concat3(int run, String a, String b, String c, String d, String e) {
for (int i = 0; i < run; i++) {
String str = new StringBuilder(a.length() + b.length() + c.length() + d.length() + e.length()).append(a)
.append(b).append(c).append(d).append(e).toString();
}
}
private static void concat2(int run, String a, String b, String c, String d, String e) {
for (int i = 0; i < run; i++) {
String str = new StringBuilder(a).append(b).append(c).append(d).append(e).toString();
}
}
private static void concat1(int run, String a, String b, String c, String d, String e) {
for (int i = 0; i < run; i++) {
String str = a + b + c + d + e;
}
}
}
Ответ 7
Возможно, вместо конкатенации вы должны создать класс Pair?
public class Pair<T1, T2> {
private T1 first;
private T2 second;
public static <U1,U2> Pair<U1,U2> create(U1 first, U2 second) {
return new Pair<U1,U2>(U1,U2);
}
public Pair( ) {}
public Pair( T1 first, T2 second ) {
this.first = first;
this.second = second;
}
public T1 getFirst( ) {
return first;
}
public void setFirst( T1 first ) {
this.first = first;
}
public T2 getSecond( ) {
return second;
}
public void setSecond( T2 second ) {
this.second = second;
}
@Override
public String toString( ) {
return "Pair [first=" + first + ", second=" + second + "]";
}
@Override
public int hashCode( ) {
final int prime = 31;
int result = 1;
result = prime * result + ((first == null)?0:first.hashCode());
result = prime * result + ((second == null)?0:second.hashCode());
return result;
}
@Override
public boolean equals( Object obj ) {
if ( this == obj )
return true;
if ( obj == null )
return false;
if ( getClass() != obj.getClass() )
return false;
Pair<?, ?> other = (Pair<?, ?>) obj;
if ( first == null ) {
if ( other.first != null )
return false;
}
else if ( !first.equals(other.first) )
return false;
if ( second == null ) {
if ( other.second != null )
return false;
}
else if ( !second.equals(other.second) )
return false;
return true;
}
}
И используйте это как свой ключ в своем HashMap
Вместо HashMap<String,Whatever>
используйте HashMap<Pair<String,String>,Whatever>
В вашем жестком цикле вместо map.get( str1 + str2 )
вы должны использовать map.get( Pair.create(str1,str2) )
.
Ответ 8
Я бы рекомендовал попробовать предложение Thorbjørn Ravn Andersens.
Если вам нужны конкатенированные строки, в зависимости от длины двух частей, может быть немного лучше создать экземпляр StringBuilder с требуемым размером, чтобы избежать перераспределения. По умолчанию конструктор StringBuilder сохраняет 16 символов в текущей реализации - по крайней мере, на моей машине. Итак, если конкатенированная строка длиннее начального размера буфера, StringBuilder должен перераспределить.
Попробуйте это и сообщите нам, что ваш профайлер должен сказать об этом:
StringBuilder ccyPair = new StringBuilder(ccy1.length()+ccy2.length());
ccyPair.append(ccy1);
ccyPair.append(ccy2);
Ответ 9
StringBuffer ccyPair = new StringBuffer();
ccyPair.append("ccy1").append("ccy2");
Вы пытались использовать String Buffer, а затем использовать профилировщик, чтобы проверить, где находится узкое место. Попробуйте и посмотрите, что произойдет.
Ответ 10
Ответ @Duncan McGregor дает некоторые контрольные номера для одного конкретного примера (размеры входной строки) и одной версии JVM. В этом случае, похоже, что String.concat()
является победителем значительным фактором. Этот результат может или не может быть обобщен.
Помимо этого меня это удивляет! Я бы подумал, что авторы компилятора предпочли бы использовать String.concat в тех случаях, когда он, вероятно, будет быстрее. Объяснение заключается в оценке этого отчета об ошибках... и уходит корнями в определение оператора конкатенации строк.
(Если операнд с строковым типом +
равен null
, JLS заявляет, что на его месте используется String "null"
. Это не сработает, если они генерируют код s + s2
как s.concat(s2)
и s
или s2
оказалось null
, вы получите NPE. И случай s == null
означает, что альтернативная версия concat
не решает проблему NPE.)
Однако ответ @unwind дал мне идею альтернативного решения, которое позволяет избежать необходимости конкатенации строк.
Если конкатенации ccy1
и ccy2
выполняются только для соединения двух ключей, возможно, вы можете получить более высокую производительность, указав специальный класс хеш-таблицы, который использует два ключа вместо одного. Он будет иметь такие операции, как:
public Object get(String key1, String key2) ...
public void put(String key1, String key2, Object value) ...
Эффект будет похож на Map<Pair<String, String>, Object>
(см. ответ @KitsuneYMG), за исключением того, что вам не нужно создавать объекты Pair<String, String>
каждый раз, когда вы хотите сделать get
или put
. Недостатком является:
- вам нужно реализовать новый класс хеш-таблицы с нуля и
- новый класс не будет соответствовать интерфейсу
Map
.
Обычно я бы не рекомендовал это делать. Однако, если конкатенация строк и поиск карт действительно являются критическим узким местом, пользовательская хэш-таблица с несколькими ключами может дать вам значительное ускорение.
Ответ 11
Возможно, вы можете решить проблему, вычислив хэши двух строк по отдельности, а затем объединив их, возможно, с помощью отдельной хэш-функции, которая работает с целыми числами?
Что-то вроде:
int h1 = ccy1.hashCode(), h2 = ccy2.hashCode(), h = h1 ^ h2;
Это может быть быстрее, поскольку объединение строк только для вычисления хеша конкатенации кажется расточительным.
Обратите внимание, что приведенное выше объединяет два хэша с двоичным-XOR (оператором ^
), который часто работает, но вы можете изучить это далее.
Ответ 12
Хорошо, так в чем ваш вопрос?
Нечего делать: если вам нужно конкатенировать строки, просто сделайте это. Хорошо, что вы профилировали свой код. Теперь вы можете видеть, что оператор конкатенации строк + автоматически использует метод StringBuilder append(), поэтому используя
StringBuilder ccyPair = new StringBuilder(ccy1)
ccyPair.append(ccy2);
не дает вам серьезных преимуществ.
Единственный серьезный способ оптимизации вашего кода - это, вероятно, изменить ваш дизайн, чтобы вообще не включать конкатенацию. Но делайте это только в том случае, если вам это действительно нужно, т.е. Конкатенация занимает значительную часть времени процессора.
Ответ 13
Здесь это полная реализация линейно-зондовой карты с двойными ключами, одно значение. Он также должен превзойти java.util.HashMap.
Предупреждение, оно написано в самые ранние часы дня с нуля, поэтому оно может содержать ошибки. Не стесняйтесь редактировать его.
Решение должно бить любую обертку, concat в любой момент. Отсутствие выделения на get/put также делает его быстрой общей картой цели.
Надеюсь, что это решает проблему. (Код приходит с некоторыми простыми тестами, которые не нужны)
package bestsss.util;
@SuppressWarnings("unchecked")
public class DoubleKeyMap<K1, K2, V> {
private static final int MAX_CAPACITY = 1<<29;
private static final Object TOMBSTONE = new String("TOMBSTONE");
Object[] kvs;
int[] hashes;
int count = 0;
final int rehashOnProbes;
public DoubleKeyMap(){
this(8, 5);
}
public DoubleKeyMap(int capacity, int rehashOnProbes){
capacity = nextCapacity(Math.max(2, capacity-1));
if (rehashOnProbes>capacity){
throw new IllegalArgumentException("rehashOnProbes too high");
}
hashes = new int[capacity];
kvs = new Object[kvsIndex(capacity)];
count = 0;
this.rehashOnProbes = rehashOnProbes;
}
private static int nextCapacity(int c) {
int n = Integer.highestOneBit(c)<<1;
if (n<0 || n>MAX_CAPACITY){
throw new Error("map too large");
}
return n;
}
//alternatively this method can become non-static, protected and overriden, the perfoamnce can drop a little
//but if better spread of the lowest bit is possible, all good and proper
private static<K1, K2> int hash(K1 key1, K2 key2){
//spread more, if need be
int h1 = key1.hashCode();
int h2 = key2.hashCode();
return h1+ (h2<<4) + h2; //h1+h2*17
}
private static int kvsIndex(int baseIdx){
int idx = baseIdx;
idx+=idx<<1;//idx*3
return idx;
}
private int baseIdx(int hash){
return hash & (hashes.length-1);
}
public V get(K1 key1, K2 key2){
final int hash = hash(key1, key2);
final int[] hashes = this.hashes;
final Object[] kvs = this.kvs;
final int mask = hashes.length-1;
for(int base = baseIdx(hash);;base=(base+1)&mask){
int k = kvsIndex(base);
K1 k1 = (K1) kvs[k];
if (k1==null)
return null;//null met; no such value
Object value;
if (hashes[base]!=hash || TOMBSTONE==(value=kvs[k+2]))
continue;//next
K2 k2 = (K2) kvs[k+1];
if ( (key1==k1 || key1.equals(k1)) && (key2==k2 || key2.equals(k2)) ){
return (V) value;
}
}
}
public boolean contains(K1 key1, K2 key2){
return get(key1, key2)!=null;
}
public boolean containsValue(final V value){
final Object[] kvs = this.kvs;
if (value==null)
return false;
for(int i=0;i<kvs.length;i+=3){
Object v = kvs[2];
if (v==null || v==TOMBSTONE)
continue;
if (value==v || value.equals(v))
return true;
}
return false;
}
public V put(K1 key1, K2 key2, V value){
int hash = hash(key1, key2);
return doPut(key1, key2, value, hash);
}
public V remove(K1 key1, K2 key2){
int hash = hash(key1, key2);
return doPut(key1, key2, null, hash);
}
//note, instead of remove a TOMBSTONE is used to mark the deletion
//this may leak keys but deletion doesn't need to shift the array like in Knuth 6.4
protected V doPut(final K1 key1, final K2 key2, Object value, final int hash){
//null value -> remove
int probes = 0;
final int[] hashes = this.hashes;
final Object[] kvs = this.kvs;
final int mask = hashes.length-1;
//conservative resize: when too many probes and the count is greater than the half of the capacity
for(int base = baseIdx(hash);probes<rehashOnProbes || count<(mask>>1);base=(base+1)&mask, probes++){
final int k = kvsIndex(base);
K1 k1 = (K1) kvs[k];
K2 k2;
//find a gap, or resize
Object old = kvs[k+2];
final boolean emptySlot = k1==null || (value!=null && old==TOMBSTONE);
if (emptySlot || (
hashes[base] == hash &&
(k1==key1 || k1.equals(key1)) &&
((k2=(K2) kvs[k+1])==key2 || k2.equals(key2)))
){
if (value==null){//remove()
if (emptySlot)
return null;//not found, and no value ->nothing to do
value = TOMBSTONE;
count-=2;//offset the ++later
}
if (emptySlot){//new entry, update keys
hashes[base] = hash;
kvs[k] = key1;
kvs[k+1] = key2;
}//else -> keys and hash are equal
if (old==TOMBSTONE)
old=null;
kvs[k+2] = value;
count++;
return (V) old;
}
}
resize();
return doPut(key1, key2, value, hash);//hack w/ recursion, after the resize
}
//optimized version during resize, doesn't check equals which is the slowest part
protected void doPutForResize(K1 key1, K2 key2, V value, final int hash){
final int[] hashes = this.hashes;
final Object[] kvs = this.kvs;
final int mask = hashes.length-1;
//find the 1st gap and insert there
for(int base = baseIdx(hash);;base=(base+1)&mask){//it ensured, no equal keys exist, so skip equals part
final int k = kvsIndex(base);
K1 k1 = (K1) kvs[k];
if (k1!=null)
continue;
hashes[base] = hash;
kvs[k] = key1;
kvs[k+1] = key2;
kvs[k+2] = value;
return;
}
}
//resizes the map by doubling the capacity,
//the method uses altervative varian of put that doesn't check equality, or probes; just inserts at a gap
protected void resize(){
final int[] hashes = this.hashes;
final Object[] kvs = this.kvs;
final int capacity = nextCapacity(hashes.length);
this.hashes = new int[capacity];
this.kvs = new Object[kvsIndex(capacity)];
for (int i=0;i<hashes.length; i++){
int k = kvsIndex(i);
K1 key1 = (K1) kvs[k];
Object value = kvs[k+2];
if (key1!=null && TOMBSTONE!=value){
K2 key2 = (K2) kvs[k+1];
doPutForResize(key1, key2, (V) value, hashes[i]);
}
}
}
public static void main(String[] args) {
DoubleKeyMap<String, String, Integer> map = new DoubleKeyMap<String, String, Integer>(4,2);
map.put("eur/usd", "usd/jpy", 1);
map.put("eur/usd", "usd/jpy", 2);
map.put("eur/jpy", "usd/jpy", 3);
System.out.println(map.get("eur/jpy", "usd/jpy"));
System.out.println(map.get("eur/usd", "usd/jpy"));
System.out.println("======");
map.remove("eur/usd", "usd/jpy");
System.out.println(map.get("eur/jpy", "usd/jpy"));
System.out.println(map.get("eur/usd", "usd/jpy"));
System.out.println("======");
testResize();
}
static void testResize(){
DoubleKeyMap<String, Integer, Integer> map = new DoubleKeyMap<String, Integer, Integer>(18, 17);
long s = 0;
String pref="xxx";
for (int i=0;i<14000;i++){
map.put(pref+i, i, i);
if ((i&1)==1)
map.remove(pref+i, i);
else
s+=i;
}
System.out.println("sum: "+s);
long sum = 0;
for (int i=0;i<14000;i++){
Integer n = map.get(pref+i, i);
if (n!=null && n!=i){
throw new AssertionError();
}
if (n!=null){
System.out.println(n);
sum+=n;
}
}
System.out.println("1st sum: "+s);
System.out.println("2nd sum: "+sum);
}
}
Ответ 14
Имейте в виду, что если вы объединяете миллионы строк, то string.concat, скорее всего, сгенерирует миллионы новых ссылок на строковые объекты. Это увеличит нагрузку на процессор.
Ответ 15
Согласно спецификации Java (и начиная с самой первой версии Java) в разделе "Оператор конкатенации строк +" сказано, что:
Чтобы повысить производительность многократной конкатенации строк, компилятор Java может использовать класс StringBuffer или аналогичный метод для уменьшения количества промежуточных объектов String, которые создаются путем вычисления выражения
Таким образом, в основном, использование + operator
или StringBuilder.append
для переменных в основном то же самое.
Другое дело, я знаю, что в своем вопросе вы упомянули добавление только 2 строк, но имейте в виду, что добавление 3 или более строк приведет к другим результатам:
Я использовал слегка модифицированный пример @Duncan McGregor. У меня есть 5 методов, объединяющих от 2 до 6 строк, используя concat, и 5 методов, объединяющих от 2 до 6 строк, используя StringBuilder:
// Initialization
private final String s1 = new String("1234567890");
private final String s2 = new String("1234567890");
private final String s3 = new String("1234567890");
private final String s4 = new String("1234567890");
private final String s5 = new String("1234567890");
private final String s6 = new String("1234567890");
// testing the concat
public void testConcatenation2stringsConcat(int count) {
for (int i = 0; i < count; i++) {
String s100 = s1.concat(s2);
}
}
public void testConcatenation3stringsConcat(int count) {
for (int i = 0; i < count; i++) {
String s100 = s1.concat(s2).concat(s3);
}
}
public void testConcatenation4stringsConcat(int count) {
for (int i = 0; i < count; i++) {
String s100 = s1.concat(s2).concat(s3).concat(s4);
}
}
public void testConcatenation5stringsConcat(int count) {
for (int i = 0; i < count; i++) {
String s100 = s1.concat(s2).concat(s3).concat(s4).concat(s5);
}
}
public void testConcatenation6stringsConcat(int count) {
for (int i = 0; i < count; i++) {
String s100 = s1.concat(s2).concat(s3).concat(s4).concat(s5).concat(s6);
}
}
//testing the StringBuilder
public void testConcatenation2stringsSB(int count) {
for (int i = 0; i < count; i++) {
String s100 = new StringBuilder(s1).append(s2).toString();
}
}
public void testConcatenation3stringsSB(int count) {
for (int i = 0; i < count; i++) {
String s100 = new StringBuilder(s1).append(s2).append(s3).toString();
}
}
public void testConcatenation4stringsSB(int count) {
for (int i = 0; i < count; i++) {
String s100 = new StringBuilder(s1).append(s2).append(s3).append(s4).toString();
}
}
public void testConcatenation5stringsSB(int count) {
for (int i = 0; i < count; i++) {
String s100 = new StringBuilder(s1).append(s2).append(s3).append(s4).append(s5).toString();
}
}
public void testConcatenation6stringsSB(int count) {
for (int i = 0; i < count; i++) {
String s100 = new StringBuilder(s1).append(s2).append(s3).append(s4).append(s5).append(s6).toString();
}
}
Я получил эти результаты (в секундах):
testConcatenation2stringsConcat: 0.018 |||||||||||||||| testConcatenation2stringsSB: 0.2 testConcatenation3stringsConcat: 0.35 ||||||||||||||||| testConcatenation3stringsSB: 0.25 testConcatenation4stringsConcat: 0.5 ||||||||||||||||||||| testConcatenation4stringsSB: 0.3 testConcatenation5stringsConcat: 0.67 ||||||||||||||||| testConcatenation5stringsSB: 0.38 testConcatenation5stringsConcat: 0.9 ||||||||||||||||||||| testConcatenation5stringsSB: 0.43
- Вы можете видеть, что concat работает быстрее, чем StringBuilder, только при объединении только двух строк.
- Обратите внимание, что при добавлении все большего и большего количества строк результирующее время StringBuilder увеличивается медленнее, чем при использовании concat
- Обратите внимание, что разница будет более существенной, если строки очень длинные