9. String.Join

מדי פעם אנחנו רואים קוד כזה

1
2
3
4
5
6
7
8
string numbers = string.Empty;
for (int i = 0; i < 1000; i++)
{
numbers += string.Format("(number = {0}) OR ", i);
}
numbers = numbers.Remove(numbers.Length - 4);

בו משרשרים הרבה מחרוזות עם איזשהו מפריד ביניהן.

בדרך כלל האיטרציה היא על איזשהו IEnumerable ולאו דווקא כפי שעשיתי.

הבעיה בסיפור הזה היא שהסיבוכיות כאן היא $ o(n^2) $, כי כל איטרציה אנחנו יוצרים אובייקט חדש ובעצם מעתיקים את כל האובייקט הקודם כפי שהוא! (הרי ידוע שstring הוא Immutable)

בנוסף עצם העובדה שבסוף אנחנו צריכים להסיר את האובייקט האחרון רק מכבידה על העניין.

יבואו הצדיקים ויאמרו, השתמש בStringBuilder. כך נראה הקוד עם StringBuilder:

1
2
3
4
5
6
7
8
StringBuilder numbers = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
numbers.AppendFormat("(number = {0}) OR ", i);
}
numbers = numbers.Remove(numbers.Length - 4, 4);

כעת הסיבוכיות היא $ o(n) $, עם זאת עדיין משהו פה לא כל כך יפה.

הפתרון: השתמשו בstring.Join:

1
2
3
4
5
6
7
8
List<string> numbersList = new List<string>(1000);
for (int i = 0; i < 1000; i++)
{
numbersList.Add(string.Format("(number = {0})", i));
}
string numbers = string.Join(" OR ", numbersList.ToArray());

הערה:

גם פה יש משהו שהוא קצת לא יפה, כיוון שצריך לעשות ToArray בשביל הפונקציה string.Join. אישית אני לא חושב שזה כל כך נורא, וזה יותר קריא משתי הדוגמאות הקודמות (שם לא כל כך ברור מיידית מתי צריך להסיר את השרשור האחרון).

אפשר לפתור את הבעיה אם אתם משתמשים בFramework 4, אז יש overloadים חדשים לstring.Join. ביניהם יש גם overload שמקבלIEnumerable<T>,המבצע פשוט Join של הToString() של כל אובייקט בIEnumerable.

כך יראה הקוד בnet 4:

1
2
3
4
5
6
7
8
List<string> numbersList = new List<string>(1000);
for (int i = 0; i < 1000; i++)
{
numbersList.Add(string.Format("(number = {0})", i));
}
string numbers = string.Join(" OR ", numbersList);

או כך, אם אתם מעדיפים לכתוב בLINQ

1
2
3
4
5
IEnumerable<string> numbersEnumerable =
from number in Enumerable.Range(0, 1000)
select string.Format("(number = {0})", number);
string numbers = string.Join(" OR ", numbersEnumerable);

(ניתן לכתוב את הגרסה של LINQ בnet 3.5, אבל תצטרכו לקרוא לJoin עם הToArray של IEnumerable<T>)

שתף