powered by simpleCommunicator - 2.0.54     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Оптимальный спосок склейки IEnumerable<string> в строку
4 сообщений из 4, страница 1 из 1
Оптимальный спосок склейки IEnumerable<string> в строку
    #38920754
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Встала задача склеить IEnumerable<string> в одну строку с разделителем, но с небольшим условием: иеется параметр alias (string), если он пустой, или null, строку в IEnumerable оставляем как есть, если не пустой - склеиваем его со строкой, разделив точкой. Варианты решения задачи: Enumerable.Aggregate, string.Concat, string.Join, StringBuilder. Доп. варианты для склеивания алиаса, строки[, и разделителя]: string.Format, string.Concat, конкатениция через операцию сложения.
Набросал вот такие тесты
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
namespace test
{
  class ConcatTest
  {
    delegate string Concat(IEnumerable<string> values, string separator, string alias);
    public static void Test(string[] args)
    {
      int n;
      if (!args.Any() || !int.TryParse(args[0], out n))
        throw new ArgumentException("invalid arguments");
      Concat c;
      switch(n)
      {
        case 1:
          c = EnumerableX.Concat1;
          break;
        case 2:
          c = EnumerableX.Concat2;
          break;
        case 3:
          c = EnumerableX.Concat3;
          break;
        case 4:
          c = EnumerableX.Concat4;
          break;
        case 5:
          c = EnumerableX.Concat5;
          break;
        case 6:
          c = EnumerableX.Concat6;
          break;
        case 7:
          c = EnumerableX.Concat7;
          break;
        case 8:
          c = EnumerableX.Concat8;
          break;
        default:
          throw new ArgumentException(string.Format("invalid argument ({0})", n));
      }
      Console.WriteLine(Test(1000000, c));
    }

    static IEnumerable<string> Values
    {
      get
      {
        var rnd = new Random();
        return Enumerable.Range(1, 100).Select(n => rnd.Next()).Select(n => n.ToString(CultureInfo.InvariantCulture));
      }
    }

    static long Test(int count, Concat func)
    {
      var sw = new Stopwatch();
      sw.Start();
      for(var i=0;i<count;i++)
        func(Values, "a", ",");
      sw.Stop();
      return sw.ElapsedTicks;
    }
  }

  static class EnumerableX
  {
// ReSharper disable UnusedParameter.Local
    static void CheckParameters<T>(IEnumerable<T> values, string separator)
// ReSharper restore UnusedParameter.Local
    {
      if (values == null)
        throw new ArgumentException("values is null");
      if (!values.Any())
        throw new ArgumentException("values is empty");
      if (separator==null)
        throw new ArgumentException("separator is null");
    }

    static string GetAlias(string alias)
    {
      return string.IsNullOrEmpty(alias) ? string.Empty : string.Concat(alias, ".");
    }

    // ReSharper disable PossibleMultipleEnumeration
    public static string Concat1<T>(this IEnumerable<T> values, string separator, string alias)
    {
      CheckParameters(values, separator);
      var lastIdndex = values.Count() - 1;
      var a = GetAlias(alias);
      return string.Concat(values.Select((value, i) => string.Concat(a, value, i == lastIdndex ? string.Empty : separator)));
    }

    public static string Concat2<T>(this IEnumerable<T> values, string separator, string alias)
    {
      CheckParameters(values, separator);
      var a1 = GetAlias(alias);
      return values.Select(v => string.Concat(a1, v))
        .Aggregate((a, b) => string.Concat(a, separator, b));
    }

    public static string Concat3<T>(this IEnumerable<T> values, string separator, string alias)
    {
      CheckParameters(values, separator);
      var a = GetAlias(alias);
      var sb = new StringBuilder();
      foreach(var v in values)
        sb.Append(string.Concat(a, v, separator));
      return sb.ToString(0, sb.Length - separator.Length);
    }

    public static string Concat4<T>(this IEnumerable<T> values, string separator, string alias)
    {
      CheckParameters(values, separator);
      var a = GetAlias(alias);
      var sb = new StringBuilder();
      foreach (var v in values)
        sb.Append(string.Format("{0}{1}{2}",a, v, separator));
      return sb.ToString(0, sb.Length - separator.Length);
    }

    public static string Concat5<T>(this IEnumerable<T> values, string separator, string alias)
    {
      CheckParameters(values, separator);
      var a = GetAlias(alias);
      var sb = new StringBuilder();
      foreach (var v in values)
        sb.AppendFormat("{0}{1}{2}", a, v, separator);
      return sb.ToString(0, sb.Length - separator.Length);
    }
    
    public static string Concat6<T>(this IEnumerable<T> values, string separator, string alias)
    {
      CheckParameters(values, separator);
      var a = GetAlias(alias);
      var sb = new StringBuilder();
      foreach (var v in values)
        sb.Append(a+v+separator);
      return sb.ToString(0, sb.Length - separator.Length);
    }

    public static string Concat7<T>(this IEnumerable<T> values, string separator, string alias)
    {
      CheckParameters(values, separator);
      var a = GetAlias(alias);
      return string.Join(separator, values.Select(value => string.Concat(a, value)));
    }

    public static string Concat8<T>(this IEnumerable<T> values, string separator, string alias)
    {
      CheckParameters(values, separator);
      var a = GetAlias(alias);
      return string.Join(separator, values.Select(value => a+value));
    }

    // ReSharper restore PossibleMultipleEnumeration
  }
}


Результаты:

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
D:\Projects\.Net\_tests\test\test\bin\Release>test.exe 1 // string.Concat(IEnumerable<T>) и string.Concat(object, object, object)
119745105
done

D:\Projects\.Net\_tests\test\test\bin\Release>test.exe 2 // Enumerable.Aggregate, string.Concat(object, object), и string.Concat(object, object, object)
105312336
done

D:\Projects\.Net\_tests\test\test\bin\Release>test.exe 3 // StringBuilder и string.Concat(object, object, object)
68522000
done

D:\Projects\.Net\_tests\test\test\bin\Release>test.exe 4 // StringBuilder.Append(string.Format)
104899143
done

D:\Projects\.Net\_tests\test\test\bin\Release>test.exe 5 //StringBuilder.AppendFormat
87703698
done

D:\Projects\.Net\_tests\test\test\bin\Release>test.exe 6 // StringBuilder.Append(string+T+string)
69146363
done

D:\Projects\.Net\_tests\test\test\bin\Release>test.exe 7 // string.Join и string.Concat(object, object)
70814538
done

D:\Projects\.Net\_tests\test\test\bin\Release>test.exe 8 // string.Join и string+T
70045715
done


Что удивило - это то, что вариант 3 (StringBuilder и string.Concat(object, object, object)) оказался быстрее варианта 1 (string.Concat(IEnumerable<T>) и string.Concat(object, object, object)) - ведь реализация string.Concat(IEnumerable<T>) тоже внутри использует StringBuilder:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
public static String Concat<T>(IEnumerable<T> values) {
  if (values == null)
    throw new ArgumentNullException("values");
  Contract.Ensures(Contract.Result<String>() != null);
  Contract.EndContractBlock();

  StringBuilder result = StringBuilderCache.Acquire();
  using(IEnumerator<T> en = values.GetEnumerator()) {
    while (en.MoveNext()) {
      if (en.Current != null) {
        // handle the case that the enumeration has null entries
        // and the case where their ToString() override is broken
        string value = en.Current.ToString();
        if (value != null)
          result.Append(value);
      }
    }     
  }
  return StringBuilderCache.GetStringAndRelease(result);
}


Ну, и где-то тут уже пробегало, что для коротких строк и небольшого количества конкатенаций склеивание строк операцией + бывает эффективнее, чем использование string.Format (вариант 6, StringBuilder.Append(string+T+string)).
Есть ли какой-нибудь более оптимальный метод, чем вариант 3 (StringBuilder и string.Concat(object, object, object))?
...
Рейтинг: 0 / 0
Оптимальный спосок склейки IEnumerable<string> в строку
    #38920789
petalvik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WinterGraveyard,
под оптимальностью понимается только время? Количество потребляемой памяти и количество сборок мусора не важны?


Что дальше делается со склееной строкой? Она нужна для обработки в коде или пишется куда-то в поток?
Можно сразу писать в цикле в поток без склейки.


А если вместо
Код: c#
1.
sb.Append(a+v+separator);

написать
Код: c#
1.
sb.Append(a).Append(v).Append(separator);
...
Рейтинг: 0 / 0
Оптимальный спосок склейки IEnumerable<string> в строку
    #38920811
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
petalvikWinterGraveyard,
под оптимальностью понимается только время? Количество потребляемой памяти и количество сборок мусора не важны?
На самом деле в контексте решаемой задачи и время роли не играет - сцепляемые последовальности не особенно длинные (на больше 100 элементов). Просто я решил этот кейс разобрать в теоретическом плане - в дальнейшем может пригодиться. Ну, и просто интересно. А поскольку интерес теоретический, то я решил привязаться хотя бы ко времени отработки.
petalvikЧто дальше делается со склееной строкой? Она нужна для обработки в коде или пишется куда-то в поток?
Можно сразу писать в цикле в поток без склейки.
Нет, сцепленная строчка просто отображается в UI.
petalvikА если вместо
Код: c#
1.
sb.Append(a+v+separator);

написать
Код: c#
1.
sb.Append(a).Append(v).Append(separator);


Да, так еще лучше - 61029789 тиков. Пока самый оптимальный вариант.
...
Рейтинг: 0 / 0
Оптимальный спосок склейки IEnumerable<string> в строку
    #38920927
mikron
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
WinterGraveyard,

sb.Append(string.Concat(a, v, separator));

Замени на

sb.Append(a).Append(v).Append(separator);
...
Рейтинг: 0 / 0
4 сообщений из 4, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / Оптимальный спосок склейки IEnumerable<string> в строку
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]