Short answer: it depends.

Long answer: if you already have an array of strings to concatenate together (with a delimiter), String.Join is the fastest way of doing it.

String.Join can look through all of the strings to work out the exact length it needs, then go again and copy all the data. This means there will be no extra copying involved. The only downside is that it has to go through the strings twice, which means potentially blowing the memory cache more times than necessary.

If you don't have the strings as an array beforehand, it's probably faster to use StringBuilder - but there will be situations where it isn't. If using a StringBuilder means doing lots and lots of copies, then building an array and then calling String.Join may well be faster.

EDIT: This is in terms of a single call to String.Join vs a bunch of calls to StringBuilder.Append. In the original question, we had two different levels of String.Join calls, so each of the nested calls would have created an intermediate string. In other words, it's even more complex and harder to guess about. I would be surprised to see either way "win" significantly (in complexity terms) with typical data.

EDIT: When I'm at home, I'll write up a benchmark which is as painful as possibly for StringBuilder. Basically if you have an array where each element is about twice the size of the previous one, and you get it just right, you should be able to force a copy for every append (of elements, not of the delimiter, although that needs to be taken into account too). At that point it's nearly as bad as simple string concatenation - but String.Join will have no problems.

Answer from Jon Skeet on Stack Overflow
Top answer
1 of 5
145

Short answer: it depends.

Long answer: if you already have an array of strings to concatenate together (with a delimiter), String.Join is the fastest way of doing it.

String.Join can look through all of the strings to work out the exact length it needs, then go again and copy all the data. This means there will be no extra copying involved. The only downside is that it has to go through the strings twice, which means potentially blowing the memory cache more times than necessary.

If you don't have the strings as an array beforehand, it's probably faster to use StringBuilder - but there will be situations where it isn't. If using a StringBuilder means doing lots and lots of copies, then building an array and then calling String.Join may well be faster.

EDIT: This is in terms of a single call to String.Join vs a bunch of calls to StringBuilder.Append. In the original question, we had two different levels of String.Join calls, so each of the nested calls would have created an intermediate string. In other words, it's even more complex and harder to guess about. I would be surprised to see either way "win" significantly (in complexity terms) with typical data.

EDIT: When I'm at home, I'll write up a benchmark which is as painful as possibly for StringBuilder. Basically if you have an array where each element is about twice the size of the previous one, and you get it just right, you should be able to force a copy for every append (of elements, not of the delimiter, although that needs to be taken into account too). At that point it's nearly as bad as simple string concatenation - but String.Join will have no problems.

2 of 5
37

Here's my test rig, using int[][] for simplicity; results first:

Join: 9420ms (chk: 210710000
OneBuilder: 9021ms (chk: 210710000

(update for double results:)

Join: 11635ms (chk: 210710000
OneBuilder: 11385ms (chk: 210710000

(update re 2048 * 64 * 150)

Join: 11620ms (chk: 206409600
OneBuilder: 11132ms (chk: 206409600

and with OptimizeForTesting enabled:

Join: 11180ms (chk: 206409600
OneBuilder: 10784ms (chk: 206409600

So faster, but not massively so; rig (run at console, in release mode, etc):

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Collect()
        {
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.WaitForPendingFinalizers();
        }
        static void Main(string[] args)
        {
            const int ROWS = 500, COLS = 20, LOOPS = 2000;
            int[][] data = new int[ROWS][];
            Random rand = new Random(123456);
            for (int row = 0; row < ROWS; row++)
            {
                int[] cells = new int[COLS];
                for (int col = 0; col < COLS; col++)
                {
                    cells[col] = rand.Next();
                }
                data[row] = cells;
            }
            Collect();
            int chksum = 0;
            Stopwatch watch = Stopwatch.StartNew();
            for (int i = 0; i < LOOPS; i++)
            {
                chksum += Join(data).Length;
            }
            watch.Stop();
            Console.WriteLine("Join: {0}ms (chk: {1}", watch.ElapsedMilliseconds, chksum);

            Collect();
            chksum = 0;
            watch = Stopwatch.StartNew();
            for (int i = 0; i < LOOPS; i++)
            {
                chksum += OneBuilder(data).Length;
            }
            watch.Stop();
            Console.WriteLine("OneBuilder: {0}ms (chk: {1}", watch.ElapsedMilliseconds, chksum);

            Console.WriteLine("done");
            Console.ReadLine();
        }
        public static string Join(int[][] array)
        {
            return String.Join(Environment.NewLine,
                    Array.ConvertAll(array,
                      row => String.Join(",",
                        Array.ConvertAll(row, x => x.ToString()))));
        }
        public static string OneBuilder(IEnumerable<int[]> source)
        {
            StringBuilder sb = new StringBuilder();
            bool firstRow = true;
            foreach (var row in source)
            {
                if (firstRow)
                {
                    firstRow = false;
                }
                else
                {
                    sb.AppendLine();
                }
                if (row.Length > 0)
                {
                    sb.Append(row[0]);
                    for (int i = 1; i < row.Length; i++)
                    {
                        sb.Append(',').Append(row[i]);
                    }
                }
            }
            return sb.ToString();
        }
    }
}
🌐
Peineary Development
peinearydevelopment.azurewebsites.net › stringbuilder-vs-string-join
StringBuilder vs. string.join - Peineary Development
March 14, 2017 - StringBuilder dateList = new StringBuilder(); foreach (string date in lstSelected.Items) { dateList.AppendFormat("{0},", date); } return dateList.ToString().TrimEnd(','); Looking at the code, I had to take a moment to understand what it was doing(if it doesn't jump out at you, it is creating a comma delimited list). I had commented that I would prefer using string.Join(",", lstSelected.Items) as it is more concise and readable to me.
Discussions

java - String.join() vs other string concatenation operations - Stack Overflow
So performance-wise it's much the same as using a StringBuilder and appending to it, or using a chain of + (which nowadays are converted to StringBuilder operations by the compiler). But the significance of String.join is not as a general replacement for + or String.concat, but in being the ... More on stackoverflow.com
🌐 stackoverflow.com
Why doesn't C#'s String.Join use StringBuilder at one point? - Stack Overflow
As can be seen here, one of String.Join's overloads works with raw pointers and uses something called UnSafeCharBuffer. Why is this? Is it a performance optimization? More on stackoverflow.com
🌐 stackoverflow.com
January 17, 2019
Discovered comparison of Performance Of String Concatenation
I’d look at StringBuilder. It provides a much more performance way to achieve what you have described. More on reddit.com
🌐 r/csharp
55
73
September 15, 2021
In Java 8 we can finally join strings
is this much different than a stringbuilder? More on reddit.com
🌐 r/java
51
147
March 22, 2014
🌐
InfoWorld
infoworld.com › home › blogs › .net programming
When to use String vs. StringBuilder in .NET Core | InfoWorld
April 27, 2021 - As you can see in Figure 3, using the Append method of the StringBuilder class to extract a string is both faster and consumes fewer resources than using Substring. When you’re joining strings, use StringBuilder.AppendJoin in lieu of String.Join for reduced allocations.
🌐
Jennifer++
jenniferplusplus.com › stringbuilder
Maybe don't use StringBuilder - Jennifer++
June 13, 2019 - Basically, if you have large strings ... If you're optimizing for memory, then Join has excellent memory efficiency for small strings, and comparable to StringBuilder for large ones....
🌐
Baeldung
baeldung.com › home › java › java string › performance comparison between different java string concatenation methods
Performance Comparison Between Different Java String Concatenation Methods | Baeldung
January 8, 2024 - On the other hand, StringJoiner exhibits a slightly slower performance than StringBuilderbecause it needs to append the delimiter each time it concatenates. Let’s go through some methods that allow concatenating more than 2 strings in one go in the mutable category. The example below illustrates a single String.join() with 5 concatenations:
🌐
Codemia
codemia.io › home › knowledge hub › string.join vs. stringbuilder which is faster?
String.Join vs. StringBuilder which is faster? | Codemia
September 24, 2025 - If you are building text incrementally with loops, conditionals, and mixed types, StringBuilder is usually the better fit. string.Join is specialized for joining an existing sequence into one string.
Find elsewhere
🌐
Microsoft Learn
learn.microsoft.com › en-us › dotnet › csharp › fundamentals › strings › common-tasks › concatenate
Concatenate strings in C# - C# | Microsoft Learn
June 26, 2026 - string.Join is the right tool whenever you need delimited output, such as comma-separated values or space-separated words. Each + or += operation creates a new string, because strings are immutable.
🌐
Reddit
reddit.com › r/csharp › discovered comparison of performance of string concatenation
r/csharp on Reddit: Discovered comparison of Performance Of String Concatenation
September 15, 2021 -

After waiting for 55 minutes using text+= 137k times in a loop, I have googled c# performance one string vs multiple string variables. Although I have not found the answer, this article made me think that I should first try another method before creating a lot of temp variables:

https://dotnetcoretutorials.com/2020/02/06/performance-of-string-concatenation-in-c/

Update: I have just replaced all string+= with StringBuilder.Append. It is now all done in 1.243 second. Yay. Thanks to all recommending StringBuilder

🌐
Pinary Development
blog.pinarydevelopment.com › c-sharp › string-builder-vs-string-join
Pinary Development - StringBuilder vs. string.join
March 14, 2017 - As can be seen from the output of TestMethod1, the method utilizing string.Join, appears as though it uses StringBuilder under the covers.
🌐
ByteHide
bytehide.com › blog › how to join strings in c#: tutorial
How to Join Strings in C#: Tutorial (2026)
December 28, 2023 - Here, StringBuilder shines by handling the same operation much more efficiently, thanks to its mutable capabilities that allow it to modify the original string instead of creating new ones.
🌐
Blogger
javarevisited.blogspot.com › 2016 › 06 › 10-examples-of-joining-string-in-java-8.html
10 Examples of Joining String in Java - String.join vs StringJoiner
May 11, 2023 - A JavaScript-like Array.join() method or join() method of Android's TextUtils class is what you need in this situation. Still, you won't find any such method on String, StringBuffer, StringBuilder, Arrays, or Collections class until Java 8. Now, you have a class called StringJoiner which allows you to join multiple String by a delimiter.
🌐
Master From Us
masterfromus.wordpress.com › 2020 › 02 › 17 › stringjoiner-vs-stringbuffer-vs-collectors-joining-performance-test
StringJoiner vs StringBuffer vs Collectors.Joining() Performance Test – Master From Us
February 21, 2020 - Time taken by StringJoiner : 49 Time taken by StringBuffer : 2 Time taken by Collectors.joining() : 4 Time taken by StringBuilder : 3
🌐
Jonskeet
jonskeet.uk › csharp › stringbuilder.html
Concatenating Strings Efficiently - Jon Skeet
(If you have an array of things to concatenate, consider calling String.Concat explicitly - or String.Join if you need a delimiter.) Don't be afraid to break literals up into several concatenated bits - the result will be the same. You can aid readability by breaking a long literal into several lines, for instance, with no harm to performance. If you need the intermediate results of the concatenation for something other than feeding the next iteration of concatenation, StringBuilder isn't going to help you.
🌐
Reddit
reddit.com › r/csharp › stringbuilder vs basic string concatenation
r/csharp on Reddit: StringBuilder vs basic string Concatenation
March 2, 2015 -

So I always hear that StringBuilder is the better, more efficient option... but is that true? How much more efficient is it? Is it worth it?

So lets say I have a method that returns a greeting. With old-school concatenation I can write something simple, and clear:

string _myName = "Bob";
public string GetGreeting(personName)
{
    return "Hello " + personName+ ", my name is " + _myName + "!";
}

With StringBuilder I have to do something much more space-consuming, which in turn is less clear.

string _myName = "Bob";
public string GetGreeting(personName)
{
    StringBuilding myString = new StringBuilder("Hello ");
    myString.Append(personName);
    myString.Append(", my name is ");
    myString.Append(myName);
    myString.Append("!");

    return myString.ToString();
}

To me, the second example is the worse code. It's harder to read, it took up more space, and it looks like it does far more work -- considering I had to convert myString back to .ToString() (although looks can be decieving) -- so is it always best to use StringBuilder?

(I'm a lone programmer at my shop so I don't have anyone to gather opinions from. Just web articles and you guys... really appreciate the help. I'm trying to be a better developer)

EDIT: Thanks everybody, as always you guys are a big help and a great source of information!

🌐
DEV Community
dev.to › this-is-learning › performance-benchmarking-string-and-string-builder-3bid
Performance Benchmarking: String and String Builder - DEV Community
August 13, 2022 - Using strings concatenation, new memory is allocated each time, while using StringBuilder the buffer approximatively doubles in size when needed. ... Software engineer building products and leading teams.
🌐
How to do in Java
howtodoinjava.com › home › string › java stringjoiner: join strings with a delimiter and prefix
Java StringJoiner: Join Strings With a Delimiter and Prefix
February 7, 2023 - String.join() uses its own internal code, which computes the final size first. A StringBuilder can do everything a StringJoiner does, but we write the delimiter logic ourselves, often with a check for the first element.
🌐
Reneschwietzke
reneschwietzke.de › java › the-stringbuilder-advise-is-dead-or-isnt-it.html
The StringBuilder advice is dead, or isn&#8217;t it? - Performance, Java, Testing, and Things
June 23, 2023 - Long story short, using StringBuilder instead of String concatenation is just an old myth. In most cases, it is not true anymore. You can safely do a String1 + String2 in your code, and you won’t notice any difference.