String.Replace () против StringBuilder.Replace () - PullRequest
73 голосов
/ 29 июня 2011

У меня есть строка, в которой мне нужно заменить маркеры значениями из словаря.Это должно быть максимально эффективно.Выполнение цикла с помощью string.replace просто потребляет память (помните, что строки неизменяемы).Будет ли StringBuilder.Replace () лучше, так как он предназначен для работы со строковыми манипуляциями?

Я надеялся избежать расходов на RegEx, но если это будет более эффективным, то пусть будет так,

Примечание. Меня не волнует сложность кода, а только скорость его выполнения и используемая память.

Средняя статистика: 255-1024 символа, 15-30 ключей в словаре.

Ответы [ 9 ]

67 голосов
/ 29 июня 2011

Использование RedGate Profiler с использованием следующего кода

class Program
    {
        static string data = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
        static Dictionary<string, string> values;

        static void Main(string[] args)
        {
            Console.WriteLine("Data length: " + data.Length);
            values = new Dictionary<string, string>()
            {
                { "ab", "aa" },
                { "jk", "jj" },
                { "lm", "ll" },
                { "yz", "zz" },
                { "ef", "ff" },
                { "st", "uu" },
                { "op", "pp" },
                { "x", "y" }
            };

            StringReplace(data);
            StringBuilderReplace1(data);
            StringBuilderReplace2(new StringBuilder(data, data.Length * 2));

            Console.ReadKey();
        }

        private static void StringReplace(string data)
        {
            foreach(string k in values.Keys)
            {
                data = data.Replace(k, values[k]);
            }
        }

        private static void StringBuilderReplace1(string data)
        {
            StringBuilder sb = new StringBuilder(data, data.Length * 2);
            foreach (string k in values.Keys)
            {
                sb.Replace(k, values[k]);
            }
        }

        private static void StringBuilderReplace2(StringBuilder data)
        {
            foreach (string k in values.Keys)
            {
                data.Replace(k, values[k]);
            }
        }
    }
  • String.Replace = 5.843ms
  • StringBuilder.Replace # 1 = 4,059 мс
  • Stringbuilder.Replace # 2 = 0,461ms

Длина строки = 1456

stringbuilder # 1 создает string Builder в методе, в то время как # 2 - нет, поэтому разница в производительности, скорее всего, будет той же самой, так как вы просто удаляете эту работу из метода. Если вы начинаете со строителя строк вместо строки, то вместо этого можно использовать # 2.

Что касается памяти, то с помощью профилировщика RedGateMemory не о чем беспокоиться, пока вы не приступите к МНОГИМ операциям замены, в которых строковый построитель в целом выиграет.

9 голосов
/ 29 июня 2011

Это может быть полезно:

http://blogs.msdn.com/b/debuggingtoolbox/archive/2008/04/02/comparing-regex-replace-string-replace-and-stringbuilder-replace-which-has-better-performance.aspx

Короткий ответ, по-видимому, заключается в том, что String.Replace работает быстрее, хотя может оказать большее влияние на объем памяти / мусорсбор накладных.

6 голосов
/ 29 июня 2011

Да, StringBuilder даст вам прирост скорости и памяти (в основном потому, что он не будет создавать экземпляр строки каждый раз, когда вы будете манипулировать ею - StringBuilder всегда работает с одним и тем же объектом),Вот ссылка MSDN с некоторыми подробностями.

5 голосов
/ 29 июня 2011

Будет ли stringbuilder.replace лучше [String.Replace]

Да, намного лучше.И если вы можете оценить верхнюю границу для новой строки (похоже, что вы можете), то, вероятно, это будет достаточно быстро.

Когда вы создаете его следующим образом:

  var sb = new StringBuilder(inputString, pessimisticEstimate);

, тогда StringBuilder не придется перераспределять свой буфер.

1 голос
/ 12 мая 2016

Проблема с ответом @DustinDavis заключается в том, что он рекурсивно работает с той же строкой.Если вы не планируете выполнять манипуляции с возвратно-поступательным движением, вам действительно нужно иметь отдельные объекты для каждого случая манипуляции в этом виде теста.

Я решил создать свой собственный тест, потому что нашел некоторыепротиворечивые ответы по всей сети, и я хотел быть полностью уверен.Программа, над которой я работаю, имеет дело с большим количеством текста (файлы с десятками тысяч строк в некоторых случаях).

Итак, вот быстрый метод, который вы можете скопировать, вставить и увидеть сами, что быстрее.Вам, возможно, придется создать свой собственный текстовый файл для тестирования, но вы можете легко скопировать и вставить текст из любого места и сделать достаточно большой файл для себя:

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows;

void StringReplace_vs_StringBuilderReplace( string file, string word1, string word2 )
{
    using( FileStream fileStream = new FileStream( file, FileMode.Open, FileAccess.Read ) )
    using( StreamReader streamReader = new StreamReader( fileStream, Encoding.UTF8 ) )
    {
        string text = streamReader.ReadToEnd(),
               @string = text;
        StringBuilder @StringBuilder = new StringBuilder( text );
        int iterations = 10000;

        Stopwatch watch1 = new Stopwatch.StartNew();
        for( int i = 0; i < iterations; i++ )
            if( i % 2 == 0 ) @string = @string.Replace( word1, word2 );
            else @string = @string.Replace( word2, word1 );
        watch1.Stop();
        double stringMilliseconds = watch1.ElapsedMilliseconds;

        Stopwatch watch2 = new Stopwatch.StartNew();
        for( int i = 0; i < iterations; i++ )
            if( i % 2 == 0 ) @StringBuilder = @StringBuilder .Replace( word1, word2 );
            else @StringBuilder = @StringBuilder .Replace( word2, word1 );
        watch2.Stop();
        double StringBuilderMilliseconds = watch1.ElapsedMilliseconds;

        MessageBox.Show( string.Format( "string.Replace: {0}\nStringBuilder.Replace: {1}",
                                        stringMilliseconds, StringBuilderMilliseconds ) );
    }
}

Я получил эту строку.примерно на 20% каждый раз, заменяя 8-10 буквенных слов.Попробуйте сами, если вам нужны собственные эмпирические доказательства.

1 голос
/ 03 декабря 2015

Мои два цента здесь, я только что написал пару строк кода, чтобы проверить, как работает каждый метод, и, как и ожидалось, результат «зависит».

Для более длинных строк Regex, кажется, выполняетлучше, для более коротких строк, String.Replace это так.Я вижу, что использование StringBuilder.Replace не очень полезно, и если его неправильно использовать, это может привести к летальному исходу с точки зрения GC (я попытался поделиться одним экземпляром StringBuilder).

Проверьте мои StringReplaceTests GitHub repo .

1 голос
/ 29 июня 2011

Это будет зависеть от того, сколько маркеров присутствует в данной строке в среднем.

Производительность поиска ключа, вероятно, будет одинаковой между StringBuilder и String, но StringBuilder победит, если вам придется заменить много маркеров в одной строке.

Если вы ожидаете тольков среднем два маркера на строку, а ваш словарь небольшой, я бы просто выбрал String.Replace.

Если имеется много маркеров, вы можете определить собственный синтаксис для идентификации маркеров - например, заключить вфигурные скобки с подходящим правилом экранирования для буквальной фигурной скобки.Затем вы можете реализовать алгоритм синтаксического анализа, который итерирует символы строки один раз, распознавая и заменяя каждый найденный маркер.Или используйте регулярное выражение.

1 голос
/ 29 июня 2011

Вместо выполнения 15-30 операций замены для всей строки может быть более эффективно использовать что-то вроде структуры данных trie для хранения вашего словаря. Затем вы можете перебрать введенную строку один раз, чтобы выполнить поиск / замену.

1 голос
/ 29 июня 2011

Преобразование данных из String в StringBuilder и обратно займет некоторое время. Если кто-то выполняет только одну операцию замены, это время может не окупиться повышением эффективности, присущим StringBuilder. С другой стороны, если кто-то преобразует строку в StringBuilder, затем выполняет над ней много операций Replace и преобразует ее обратно в конец, подход StringBuilder будет быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...