Click here to Skip to main content
11,637,415 members (67,461 online)
Click here to Skip to main content

Efficient string concatenation algorithm implements String.Join() method

, 23 Sep 2012 CPOL 14.3K 13
Rate this:
Please Sign up or sign in to vote.
Alternative solution that demonstrates performance improvement using String.Join() (extends original one:"StringBuilderPlus Improves Upon StringBuilder").

Introduction

This article describes an alternative algorithm of string concatenation using the String.Join() method instead of the StringBuilder object Append() method and provides performance estimates of both algorithms.

Background

The following code snippets implements two string concatenation algorithms:

  1. Using the StringBuilder object Append() method.
  2. Using the String.Join() static method.

In both test settings, the array of strings (1,000,000 items) was pre-populated, thus the stopwatch counts only the elapsed time spent on the actual string concatenation (please note that in the original article the loop includes a i.ToString() conversion, which is included in the elapsed time estimates, thus affecting the result and distorting the performance statistics). Also, a stopwatch object provides much more accurate time measurement (it is a part of the System.Diagnostics object library).

The Insert() method addressing the Prefixing/Suffixing issue as described in the original article is off consideration provided that "in-real-life" scenarios it's possible to just reverse the order of operation if necessary and then apply the Append() method, which is more efficient than Insert() (based on the performance estimates included in the original article).

The main goal of the article and corresponding test settings is to introduce more efficient solution for string concatenation utilizing String.Join() static method instead of StringBuilder Append() method, and to provide empirically obtained performance estimates.

Using the code

Both algorithms apply the following sample string delimiter:

", " 
Listing 1. Sample string concatenation using StringBuilder Append() method
#region String Concatenation Using StringBuilder.Append() method
/// <summary>
/// StringBuilder.Append() method performance estimate
/// </summary>
/// <param name="LoopCounter">Int64</param>
/// <returns>double</returns>
public static double ConcatenateUsingAppend(Int64 LoopCounter)
{
    StringBuilder _sb = new StringBuilder();

    // array of string to concatenate
    string[] _arTemp = new string[LoopCounter];

    // populate array with sample strings
    for (Int64 i = 0; i < LoopCounter; i++)
    {
        _arTemp[i] = i.ToString();
    }

    // stopwatch obj (ref: System.Diagnostics)
    Stopwatch _sw = new Stopwatch();

    // start count ticks
    _sw.Start();

    // concatenate using Append()
    for (Int64 i = 0; i < LoopCounter; i++)
    {
        _sb.Append(_arTemp[i]);
        _sb.Append(", ");
    }

    // result string
    string _result = _sb.ToString();

    // stop count
    _sw.Stop();

    // duration measured in ticks
    Int64 _ticks = _sw.ElapsedTicks;

    // stopwatch frequency in kHZ
    double _frequency = Stopwatch.Frequency;

    // time per tick
    double _secPerTick = 1 / _frequency;

    // duration in msec (rounded)
    return Math.Round((1000 * _ticks * _secPerTick), 1);
}
#endregion
Listing 2. Sample string concatenation using String.Join() method
/// <summary>
/// String.Join() method performance estimate
/// </summary>
/// <param name="LoopCounter">Int64</param>
/// <returns>double</returns>
public static double StringJoin(Int64 LoopCounter)
{
            
    // array of string to concatenate
    string[] _arTemp = new string[LoopCounter];

    // populate array with sample strings
    for (Int64 i = 0; i < LoopCounter; i++)
    {
        _arTemp[i] = i.ToString();
    }

    // stopwatch obj (ref: System.Diagnostics)
    Stopwatch _sw = new Stopwatch();

    // start count ticks
    _sw.Start();

    // get result string using String.Join()
    string _result = String.Join(", ", _arTemp);

    // stop count
    _sw.Stop();

    // duration measured in ticks
    Int64 _ticks = _sw.ElapsedTicks;

    // stopwatch frequency in kHZ
    double _frequency = Stopwatch.Frequency;

    // time per tick
    double _secPerTick = 1 / _frequency;

    // duration in msec (rounded)
    return Math.Round((1000 * _ticks * _secPerTick),1);
}
Listing 3. Test project implemented as Win Form contains single Form, Button1 and two labels; Button1 click event
private void button1_Click(object sender, EventArgs e)
{
    Int64 _iterations = 1000000; // 1 million
    label1.Text = "Duration using Append(), msec: " + ConcatenateUsingAppend(_iterations).ToString();
    label2.Text = "Duration using String.Join(), msec: " + StringJoin(_iterations).ToString();
}

Points of interest

Empirically obtained results demonstrate almost three times performance improvement by utilizing the static String.Join() method (elapsed time about 36 msec for 1,000,000 strings) vs. StringBuilder object Append() method (took about 100 msec for the same test settings).

String.Join and String.Split methods (.NET)

Thanks to reader's input and thoughtful comments, I found it relevant to elaborate further on the usage of String.Join and also, String.Split, as some of our fellow programmers may be not quite familiar with these methods.

The static method String.Join has been a part of .NET Framework since version 1.1, though in most current forms it was introduced in V.2.0 (see ref [1]) as shown below (C# syntax):

public static string Join (string separator, string[] value)

Responding to the comments: separator (the first parameter) can be an empty string, thus pertinent to the example in Listing 2, the concatenation line can be written like the following (joining all strings together):

string _result = String.Join(String.Empty, _arTemp);

but even more efficient way pertinent to this just concatenation case (without delimiters) will be to use the String.Concat method as described later in the article.

Serialization/De-serialization

The String.Join method can be used, for example, in order to serialize the array of strings converting it to a single delimited one. Another operator String.Split does exactly the opposite (see ref [2]), returning the array of strings extracted from a single delimited one. Important to mention that if the original array of strings contain empty strings or blank spaces, then the String.Join() method will still add specified delimiters, so the reverse operation based on String.Split() will return the array of the same structure and content.

It's relevant to mention (even though this is typically a quite known fact) that the "+" operator when applied to strings is very inefficient performance-wise, thus the Append() method is recommended in general for that type of string operations. But for a special purpose of concatenating strings array with a specified delimiter added as described in the article, the String.Join() method outperforms the "+" operator and the Append() method as well.

Prefixing/Suffixing is simple with the String.Join() method

If prefixing (which is semantically equal to "inserting a delimiter before") is required for all elements of the string array, then the specified prefix should be added only once in front of just the first array's element (index of 0) before running the String.Join() concatenation; the rest of the code remains the same. Correspondingly, the suffix should be added (if necessary) to the last element of the string array before applying the String.Join() method.

String.Concat method

As discussed above, the String.Join() method provides the ability to concatenate an array of strings with an added prefix, suffix, or both (they can all be included in a separator string along with the actual delimiting char). But in case strings delimiting is not required, then another static method namely: String.Concat() [3] can do the job even more efficiently than String.Join(): the additional performance boost was calculated as approx. 20% vs. String.Join() applying the same test settings (run time about 29 ms vs. 36 ms), and just replacing a single line of code with the following one:

string _result = String.Concat(_arTemp);

History

  • Posted on: Sep. 22, 2012.
  • Updated on Sep. 23, 2012.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

DrABELL
President Infosoft International Inc
United States United States
Dr. A. Bell has 20+ years of Software and Electrical Engineering experience: Win/Web veteran, published 300+ articles and authored 37 inventions, credited for 10+ Enterprise level projects (>250k code lines); currently focused on .NET/WPF/C#, Javascript/jQuery, 'Big Data', AI, IoT and Mobile apps. Participated in App Innovation Contest (AIC 2102/2013) with several winning submissions. Sample projects/pubs follow:
  1. WebTV Project: Embedded YouTube Player (Goog #1 YouTube API for ASP.NET)
  2. Edumatter M12: School Math Calculators and Equation Solvers (contest winner)
  3. Engineering Calculator VOLTA-2013 (contest winner)
  4. Online 3 Fractions Calculator (#1 on Goog)
  5. Engineering Calculator VOLTA-814 for Windows
  6. Real-time NY Bus monitoring app
  7. Inflation Calculator
  8. PaydayNY-2015 Payroll Tax Calculator (Win)
  9. Multilingual Geocoder with Interactive Map
  10. Semantic Analyzer (Concordance Calculator)
  11. Prime Factoring Calculator

You may also be interested in...

Comments and Discussions

 
GeneralMy vote of 5 Pin
Espen Harlinn16-Oct-12 21:46
mvpEspen Harlinn16-Oct-12 21:46 
GeneralRe: My vote of 5 Pin
DrABELL17-Oct-12 1:55
memberDrABELL17-Oct-12 1:55 
QuestionYou should mention that Pin
FatCatProgrammer23-Sep-12 8:15
memberFatCatProgrammer23-Sep-12 8:15 
AnswerRe: You should mention that Pin
DrABELL23-Sep-12 10:51
memberDrABELL23-Sep-12 10:51 
GeneralRe: You should mention that Pin
FatCatProgrammer24-Sep-12 5:07
memberFatCatProgrammer24-Sep-12 5:07 
GeneralRe: You should mention that Pin
DrABELL24-Sep-12 5:47
memberDrABELL24-Sep-12 5:47 
GeneralMy vote of 5 Pin
Akram El Assas23-Sep-12 7:10
memberAkram El Assas23-Sep-12 7:10 
GeneralRe: My vote of 5 Pin
DrABELL23-Sep-12 9:06
memberDrABELL23-Sep-12 9:06 
SuggestionRe: My vote of 5 Pin
Dan Randolph23-Sep-12 11:04
memberDan Randolph23-Sep-12 11:04 
GeneralRe: My vote of 5 Pin
DrABELL23-Sep-12 11:42
memberDrABELL23-Sep-12 11:42 
GeneralMy vote of 5 Pin
Dan Randolph23-Sep-12 6:30
memberDan Randolph23-Sep-12 6:30 
GeneralRe: My vote of 5 Pin
DrABELL23-Sep-12 9:13
memberDrABELL23-Sep-12 9:13 
GeneralMy vote of 4 Pin
cjb11023-Sep-12 0:52
membercjb11023-Sep-12 0:52 
GeneralRe: My vote of 4 Pin
DrABELL23-Sep-12 2:13
memberDrABELL23-Sep-12 2:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150728.1 | Last Updated 23 Sep 2012
Article Copyright 2012 by DrABELL
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid