Cara paling efisien untuk menggabungkan string?


285

Apa cara paling efisien untuk menggabungkan string?


9
Saya ingin menempatkan peringatan yang menonjol di sini bahwa jawaban yang diterima secara signifikan tidak lengkap karena tidak membahas semua kasus yang relevan.
usr

@ usr Memang ... informasi lebih lanjut tentang StringBuilderkasus penggunaan dapat ditemukan di sini .
Tamir Vered

Favorit baru saya pada C # 6 adalah $ "Teks konstan di sini {foo} dan {bar}" ... seperti String.Formatpada steroid. Yang mana, kinerja bijaksana, sedikit lebih lambat di satu liners daripada +dan String.Concat, tetapi jauh lebih baik daripada itu, meskipun lebih lambat daripada StringBuilder, di beberapa panggilan. Secara praktis, perbedaan kinerja sedemikian rupa sehingga, jika saya harus memilih hanya satu cara untuk menggabungkan, saya akan memilih interpolasi string menggunakan $... Jika dua cara, kemudian tambahkan StringBuilderke kotak alat saya. Dengan dua cara itu Anda sudah siap.
u8it

The String.Joinjawaban di bawah ini tidak melakukan +keadilan dan, secara praktis, cara yang buruk untuk string concatenate, tetapi kinerja sangat cepat bijaksana. Jawabannya menarik. String.Concatdan String.Joinkeduanya dapat bekerja pada array, tetapi String.Joinsebenarnya lebih cepat. Rupanya, String.Joincukup canggih dan lebih dioptimalkan daripada String.Concat, sebagian karena beroperasi mirip dengan StringBuilderyang menghitung panjang string pertama dan kemudian membangun string yang diuntungkan dari pengetahuan ini menggunakan UnSafeCharBuffer.
u8it

Ok, jadi itu cepat, tetapi String.Joinjuga membutuhkan membangun sebuah array yang tampaknya tidak efisien sumber daya kan? ... Ternyata itu +dan String.Concatmembangun array untuk konstituen mereka. Akibatnya, secara manual membuat array dan memberi makan itu String.Joinrelatif lebih cepat ... namun, StringBuildermasih mengungguli String.Joindalam setiap cara praktis sementara $hanya sedikit lebih lambat dan lebih cepat di string panjang ... belum lagi bahwa itu canggung dan jelek untuk digunakan String.Joinjika Anda memiliki untuk membuat array untuk itu di tempat.
u8it

Jawaban:


154

The StringBuilder.Append()Metode jauh lebih baik daripada menggunakan +operator. Tetapi saya telah menemukan bahwa, ketika menjalankan 1000 concatenations atau kurang, String.Join()bahkan lebih efisien daripada StringBuilder.

StringBuilder sb = new StringBuilder();
sb.Append(someString);

Satu-satunya masalah dengan String.Joinadalah Anda harus menyatukan senar dengan pembatas umum.

Sunting: seperti yang ditunjukkan @ryanversaw , Anda dapat membuat pembatas string.Empty.

string key = String.Join("_", new String[] 
{ "Customers_Contacts", customerID, database, SessionID });

11
StringBuildermemiliki biaya awal yang sangat besar, hanya efisien bila digunakan dengan string yang sangat besar, atau sangat banyak rangkaian. Bukan hal sepele untuk mencari tahu situasi apa pun. Jika kinerja bermasalah, membuat profil adalah teman Anda (periksa SEMUT).
Abel

32
Ini tidak benar untuk penggabungan satu baris. Katakanlah Anda melakukan myString = "foo" + var1 + "bar" + var2 + "hello" + var3 + "world", kompiler secara otomatis mengubahnya menjadi panggilan string.concat, yang seefisien mungkin. Jawaban ini salah, ada banyak jawaban yang lebih baik untuk dipilih
csauve

2
Untuk concatentation string sepele gunakan apa yang paling mudah dibaca. string a = b + c + d; akan hampir selalu lebih cepat daripada melakukannya dengan StringBuilder, tetapi perbedaannya biasanya tidak relevan. Gunakan StringBuilder (atau opsi lain pilihan Anda) ketika berulang kali menambahkan ke string yang sama (misalnya membangun laporan) atau ketika berhadapan dengan string besar.
Swanny

5
Mengapa Anda tidak menyebutkannya string.Concat?
Venemo

271

Rico Mariani , guru .NET Performance, memiliki artikel tentang hal ini. Ini tidak sesederhana yang diduga. Saran dasar adalah ini:

Jika pola Anda terlihat seperti:

x = f1(...) + f2(...) + f3(...) + f4(...)

itu salah satu concat dan zippy, StringBuilder mungkin tidak akan membantu.

Jika pola Anda terlihat seperti:

if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)

maka Anda mungkin ingin StringBuilder.

Namun artikel lain untuk mendukung klaim ini berasal dari Eric Lippert di mana ia menggambarkan optimasi yang dilakukan pada satu baris +gabungan secara terperinci.


1
Bagaimana dengan String.Format ()?
IronSlug

85

Ada 6 jenis rangkaian string:

  1. Menggunakan plus (+ simbol ).
  2. Menggunakan string.Concat().
  3. Menggunakan string.Join().
  4. Menggunakan string.Format().
  5. Menggunakan string.Append().
  6. Menggunakan StringBuilder.

Dalam percobaan, telah terbukti bahwa itu string.Concat()adalah cara terbaik untuk mendekati jika kata-katanya kurang dari 1000 (kurang-lebih) dan jika kata-kata lebih dari 1000 makaStringBuilder harus digunakan.

Untuk informasi lebih lanjut, periksa situs ini .

string.Join () vs string.Concat ()

Metode string.Compat di sini setara dengan doa metode string.Join dengan pemisah kosong. Menambahkan string kosong cepat, tetapi tidak melakukannya bahkan lebih cepat, jadi metode string.Compat akan lebih unggul di sini.


4
Seharusnya membacanya telah terbukti string.Concat () atau + adalah cara terbaik. Ya saya bisa mendapatkan ini dari artikel tetapi saya menghemat satu klik. Jadi, + dan concat kompilasi ke dalam kode yang sama.
brumScouse

Saya menggunakan dasar ini untuk mencoba dan membuat metode tambang saya lebih efisien, di mana saya hanya perlu menggabungkan 3 string. Saya menemukan bahwa +sebenarnya 3 milidetik lebih cepat daripada string.Concat(), meskipun saya belum melihat ke dalam jumlah string yang diperlukan sebelum string.Concat()kehabisan +.
Gnemlock

59

From Chinh Do - StringBuilder tidak selalu lebih cepat :

Aturan Jempol

  • Saat menggabungkan tiga nilai string dinamis atau kurang, gunakan gabungan string tradisional.

  • Saat menggabungkan lebih dari tiga nilai string dinamis, gunakan StringBuilder.

  • Saat membuat string besar dari beberapa string literal, gunakan @string string literal atau inline +.

Sebagian besar waktu StringBuilderadalah taruhan terbaik Anda, tetapi ada kasus-kasus seperti yang ditunjukkan dalam posting bahwa Anda setidaknya harus memikirkan setiap situasi.


8
afaik @ hanya mematikan pemrosesan urutan escape. msdn.microsoft.com/en-us/library/362314fe.aspx setuju
abatishchev

12

Jika Anda beroperasi dalam satu lingkaran, StringBuildermungkin ini cara yang harus dilakukan; ini menghemat biaya pembuatan string baru secara teratur. Dalam kode yang hanya akan berjalan sekali, String.Concatmungkin saja baik-baik saja.

Namun, Rico Mariani (.NET optimisasi guru) membuat kuis di mana ia menyatakan pada akhirnya bahwa, dalam banyak kasus, ia merekomendasikan String.Format.


Saya telah merekomendasikan penggunaan string.format lebih dari string + string selama bertahun-tahun kepada orang-orang yang pernah bekerja dengan saya. Saya pikir keunggulan keterbacaan adalah keuntungan tambahan di luar manfaat kinerja.
Scott Lawrence

1
Ini adalah jawaban yang benar sebenarnya. Jawaban yang saat ini diterima untuk StringBuilder salah, karena tidak menyebutkan baris ditambahkan untuk string.concat atau + yang lebih cepat. Fakta yang tidak banyak diketahui adalah bahwa kompiler sebenarnya menerjemahkan + menjadi string.concat. Juga, untuk loop atau untuk beberapa baris concat saya menggunakan pembangun string yang dibangun khusus yang hanya menambahkan ketika. ToString disebut - mengatasi masalah buffer tak tentu yang StringBuilder miliki
csauve

2
string.Format bukan cara tercepat dalam kondisi apa pun. Saya tidak tahu bagaimana membuat kasus di mana ia datang.
usr

@ usr - perhatikan bahwa Rico secara eksplisit tidak mengatakan itu yang tercepat , hanya saja itu rekomendasinya: "Meskipun ini berkinerja terburuk, dan kami tahu bahwa jauh sebelumnya, kedua Arsitek Kinerja CLR Anda sependapat bahwa [string.Format] harus menjadi pilihan default. Dalam hal yang sangat tidak mungkin itu menjadi masalah kinerja, masalah ini mudah diatasi dengan hanya perubahan lokal sederhana. Biasanya Anda hanya menguangkan beberapa pemeliharaan yang bagus. "
Adam V

@AdamV pertanyaannya adalah tentang cara tercepat. Saya tidak setuju tentang hal itu menjadi pilihan default meskipun bukan karena alasan perf. Itu bisa sintaks yang canggung. Resharper dapat mengonversi bolak-balik sesuka hati.
usr

10

Ini adalah metode tercepat yang telah saya kembangkan selama satu dekade untuk aplikasi NLP skala besar saya. Saya memiliki variasi untuk IEnumerable<T>dan tipe input lainnya, dengan dan tanpa pemisah dari tipe yang berbeda ( Char, String), tetapi di sini saya menunjukkan kasus sederhana menggabungkan semua string dalam array menjadi string tunggal, tanpa pemisah. Versi terbaru di sini dikembangkan dan diuji unit pada C # 7 dan .NET 4.7 .

Ada dua kunci untuk kinerja yang lebih tinggi; yang pertama adalah melakukan pra-perhitungan ukuran total persis yang dibutuhkan. Langkah ini sepele ketika input adalah array seperti yang ditunjukkan di sini. Untuk penanganan IEnumerable<T>sebagai gantinya, ada baiknya mengumpulkan string menjadi array sementara untuk menghitung total (Array diperlukan untuk menghindari memanggil ToString()lebih dari sekali per elemen karena secara teknis, mengingat kemungkinan efek samping, hal itu dapat mengubah semantik yang diharapkan. dari operasi 'string join').

Selanjutnya, mengingat ukuran alokasi total string akhir, dorongan terbesar dalam kinerja diperoleh dengan membangun string hasil di tempat . Melakukan hal ini memerlukan teknik (mungkin kontroversial) untuk sementara menangguhkan ketidakmampuan baru Stringyang awalnya dialokasikan penuh dengan nol. Terlepas dari kontroversi semacam itu, bagaimanapun ...

... perhatikan bahwa ini adalah satu-satunya solusi penggabungan massal pada halaman ini yang sepenuhnya menghindari putaran alokasi dan penyalinan tambahan oleh Stringkonstruktor.

Kode lengkap:

/// <summary>
/// Concatenate the strings in 'rg', none of which may be null, into a single String.
/// </summary>
public static unsafe String StringJoin(this String[] rg)
{
    int i;
    if (rg == null || (i = rg.Length) == 0)
        return String.Empty;

    if (i == 1)
        return rg[0];

    String s, t;
    int cch = 0;
    do
        cch += rg[--i].Length;
    while (i > 0);
    if (cch == 0)
        return String.Empty;

    i = rg.Length;
    fixed (Char* _p = (s = new String(default(Char), cch)))
    {
        Char* pDst = _p + cch;
        do
            if ((t = rg[--i]).Length > 0)
                fixed (Char* pSrc = t)
                    memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1));
        while (pDst > _p);
    }
    return s;
}

[DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb);

Saya harus menyebutkan bahwa kode ini memiliki sedikit modifikasi dari apa yang saya gunakan sendiri. Dalam dokumen asli, saya memanggil instruksi cpblk IL dari C # untuk melakukan penyalinan yang sebenarnya. Untuk kesederhanaan dan portabilitas dalam kode di sini, saya menggantinya dengan P / Invoke memcpy, seperti yang Anda lihat. Untuk kinerja tertinggi pada x64 ( tapi mungkin bukan x86 ), Anda mungkin ingin menggunakan metode cpblk .


string.Joinlakukan semua hal ini untuk Anda. Tidak perlu menulisnya sendiri. Itu menghitung ukuran string terakhir, membangun string ukuran itu, dan kemudian menulis ke array karakter yang mendasarinya. Bahkan memiliki bonus menggunakan nama variabel yang dapat dibaca dalam proses.
Servy

1
@Servy Terima kasih atas komentarnya; memang String.Joinbisa efisien. Seperti yang saya sebutkan di intro, kode di sini hanyalah ilustrasi paling sederhana dari rangkaian fungsi yang saya gunakan untuk skenario yang String.Jointidak ditangani (seperti mengoptimalkan Charpemisah) atau tidak menangani dalam versi .NET sebelumnya. Saya kira saya seharusnya tidak mengambil ini sebagai contoh paling sederhana, karena ini adalah kasus yang String.Joinsudah menangani dengan baik, meskipun dengan "inefisiensi," kemungkinan tidak terukur, memproses pemisah yang kosong, yaitu. String.Empty.
Glenn Slayden

Tentu, jika Anda tidak memiliki pemisah, maka Anda harus menelepon Concat, yang juga melakukan ini dengan benar. Bagaimanapun Anda tidak perlu menulis kode sendiri.
Servy

7
@Servy Saya telah membandingkan kinerja String.Joinversus kode saya menggunakan test harness ini . Untuk 10 juta operasi penggabungan acak masing-masing hingga 100 string berukuran kata, kode yang ditunjukkan di atas secara konsisten 34% lebih cepat daripada String.Joinpada rilis rilis x64 dengan .NET 4.7 . Karena OP secara eksplisit meminta metode "paling efisien", hasilnya menunjukkan bahwa jawaban saya berlaku. Jika ini mengatasi masalah Anda, saya mengundang Anda untuk mempertimbangkan kembali downvote Anda.
Glenn Slayden

1
Saya baru-baru ini membandingkan ini pada x64 CLR 4.7.1 penuh dan menemukannya sekitar dua kali lebih cepat dari string. Bergabung dengan sekitar 25% lebih sedikit memori yang dialokasikan ( i.imgur.com/SxIpEmL.png ) saat menggunakan cpblk atau github.com/ JonHanna / Mnemosyne
quentin-starin

6

Dari artikel MSDN ini :

Ada beberapa overhead yang terkait dengan membuat objek StringBuilder, baik dalam waktu maupun memori. Pada mesin dengan memori cepat, StringBuilder menjadi berguna jika Anda melakukan sekitar lima operasi. Sebagai aturan praktis, saya akan mengatakan 10 atau lebih operasi string adalah pembenaran untuk overhead pada mesin apa pun, bahkan yang lebih lambat.

Jadi, jika Anda mempercayai MSDN, gunakan StringBuilder jika Anda harus melakukan lebih dari 10 operasi string / gabungan - jika tidak, string string sederhana dengan '+' tidak masalah.



5

Menambah jawaban lain, harap diingat bahwa StringBuilder dapat diberi tahu jumlah memori awal yang akan dialokasikan .

The kapasitas parameter mendefinisikan jumlah maksimum karakter yang dapat disimpan dalam memori yang dialokasikan oleh contoh saat. Nilainya ditugaskan ke properti Kapasitas . Jika jumlah karakter yang akan disimpan dalam instance saat ini melebihi nilai kapasitas ini , objek StringBuilder mengalokasikan memori tambahan untuk menyimpannya.

Jika kapasitas nol, kapasitas standar khusus implementasi digunakan.

Berulang kali menambahkan ke StringBuilder yang belum dialokasikan sebelumnya dapat menghasilkan banyak alokasi yang tidak perlu seperti berulang kali merangkai string biasa.

Jika Anda tahu berapa lama string terakhir akan, dapat menghitungnya secara sepele, atau dapat membuat tebakan yang berpendidikan tentang kasus umum (mengalokasikan terlalu banyak tidak selalu merupakan hal yang buruk), Anda harus memberikan informasi ini kepada konstruktor atau Properti kapasitas . Terutama ketika menjalankan tes kinerja untuk membandingkan StringBuilder dengan metode lain seperti String.Concat, yang melakukan hal yang sama secara internal. Setiap tes yang Anda lihat online yang tidak termasuk pra-alokasi StringBuilder dalam perbandingannya salah.

Jika Anda tidak dapat menebak apa pun tentang ukuran, Anda mungkin menulis fungsi utilitas yang seharusnya memiliki argumen opsional sendiri untuk mengendalikan pra-alokasi.


4

Mengikuti mungkin merupakan satu solusi alternatif untuk menggabungkan beberapa string.

String str1 = "sometext";
string str2 = "some other text";

string afterConcate = $"{str1}{str2}";

interpolasi string


1
Ini sebenarnya sangat bagus sebagai metode penggabungan umum. Ini pada dasarnya String.Formattetapi lebih mudah dibaca dan mudah digunakan. Bench-menandai itu, itu sedikit lebih lambat dari +dan String.Concatpada satu baris gabungan tetapi jauh lebih baik daripada keduanya pada panggilan berulang membuat StringBuilderkurang perlu.
u8it

2

Yang paling efisien adalah menggunakan StringBuilder, seperti:

StringBuilder sb = new StringBuilder();
sb.Append("string1");
sb.Append("string2");
...etc...
String strResult = sb.ToString();

@jonezy: String.Compat baik-baik saja jika Anda memiliki beberapa hal kecil. Tetapi jika Anda menggabungkan megabita data, program Anda kemungkinan akan bertambah.


hei apa solusi untuk megabyte data?
Neel

2

Coba 2 buah kode ini dan Anda akan menemukan solusinya.

 static void Main(string[] args)
    {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 10000000; i++)
        {
            s.Append( i.ToString());
        }
        Console.Write("End");
        Console.Read();
    }

Vs

static void Main(string[] args)
    {
        string s = "";
        for (int i = 0; i < 10000000; i++)
        {
            s += i.ToString();
        }
        Console.Write("End");
        Console.Read();
    }

Anda akan menemukan bahwa kode 1 akan berakhir sangat cepat dan memori akan dalam jumlah yang baik.

Kode kedua mungkin memori akan baik-baik saja, tetapi akan memakan waktu lebih lama ... lebih lama. Jadi jika Anda memiliki aplikasi untuk banyak pengguna dan Anda membutuhkan kecepatan, gunakan tanggal 1. Jika Anda memiliki aplikasi untuk aplikasi satu pengguna jangka pendek, mungkin Anda bisa menggunakan keduanya atau yang ke-2 akan lebih "alami" untuk pengembang.

Bersulang.


1

Untuk hanya dua string, Anda pasti tidak ingin menggunakan StringBuilder. Ada beberapa ambang di atas yang overhead StringBuilder kurang dari overhead mengalokasikan beberapa string.

Jadi, untuk lebih dari 2-3 string, gunakan kode DannySmurf . Jika tidak, cukup gunakan operator +.


1

System.String tidak dapat diubah. Ketika kita memodifikasi nilai variabel string maka memori baru dialokasikan ke nilai baru dan alokasi memori sebelumnya dirilis. System.StringBuilder dirancang untuk memiliki konsep string yang dapat berubah di mana berbagai operasi dapat dilakukan tanpa mengalokasikan lokasi memori yang terpisah untuk string yang dimodifikasi.


1

Solusi lain:

di dalam loop, gunakan List sebagai ganti string.

List<string> lst= new List<string>();

for(int i=0; i<100000; i++){
    ...........
    lst.Add(...);
}
return String.Join("", lst.ToArray());;

ini sangat sangat cepat.



0

Itu akan tergantung pada kode. StringBuilder umumnya lebih efisien, tetapi jika Anda hanya menggabungkan beberapa string dan melakukan semuanya dalam satu baris, optimasi kode kemungkinan akan mengaturnya untuk Anda. Sangat penting untuk memikirkan bagaimana kode terlihat juga: untuk set yang lebih besar, StringBuilder akan membuatnya lebih mudah dibaca, untuk yang kecil, StringBuilder hanya akan menambah kekacauan yang tidak perlu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.