+2

Tản mạn bài toán cộng chuỗi trong C#

Chuyện là hôm vừa rồi rảnh rỗi thế là ngồi review lại các dự án mình đang join thì mình "bắt quả tang" một thói quen rất phổ biến mà chắc hẳn nhiều anh em dev (thú thật là tính cả mình ngày xưa 😅) đều từng mắc phải: Nối chuỗi bằng toán tử += trong vòng lặp.

Ví dụ bài toán đơn giản thế này: Có một List khoảng 500 cái tên, nhiệm vụ là nối chúng lại thành một chuỗi. Thói quen thường thấy là: foreach từng phần tử rồi string += item (anh em dev đọc chắc chắn hiểu đoạn code này 😁 ).

Ngày trước mình cũng hay "hồn nhiên" code như vậy. Sau này biết đến StringBuilder thì mình chuyển sang dùng Append(item) vì "nghe nói" nó tốt hơn (sau này tìm hiểu lại mới rõ 😁). Và nếu là mình thì trong bài toán trên mình sẽ dùng StringBuilder xong Append(item)

Nhưng lúc này trong đầu mình có câu hỏi, "Liệu cách này đã ổn chưa? Chênh lệch thực tế là bao nhiêu?" Vậy thì BenchMark ngay cho nóng👏 , Các ứng viên tham gia hiện có:

  1. StringPlus: Dùng += trong vòng lặp.
  2. StringBuilder: Dùng StringBuilder.Append().
  3. ListJoin: Dùng string.Join("", list). (Ta có thêm nhân tố bí ẩn này nữa)

Bài toán mình test là : Input là 1 chuỗi string từ "1" -> "500", Output là nối các chữ số này lại thành chuỗi mới "123...500", Sau đây là đoạn code của mình:

//Tạo List chuỗi từ "1" -> "500"
List<string> lists = Enumerable.Range(1, 500)
    .Select(x => x.ToString())
    .ToList();

[Benchmark]
public string StringPlus()
{
    string s = "";
    foreach (var item in lists)
    {
        s += item;
    }
    return s;
}

[Benchmark]
public string StringBuilder()
{
    var sb = new StringBuilder();
    foreach (var item in lists)
    {
        sb.Append(item);
    }

    return sb.ToString();
}

[Benchmark]
public string ListJoin()
{
    return string.Join("", lists);
}

Rất đơn giản vậy thôi mà kết quả làm mình khá sốc:

  • Về tốc độ (Mean Time): Cách dùng StringPlus chậm gấp 20 lần so với StringBuilder. Chậm gấp 13 lần so với string.Join.
  • Về bộ nhớ (Allocated) - Đây mới là thứ đáng sợ: Cách dùng StringPlus ngốn bộ nhớ gấp 90 lần StringBuilder. Và gấp gần 240 lần so với string.Join.

Con số cấp phát bộ nhớ (Allocated) này cực kỳ quan trọng vì nó ảnh hưởng trực tiếp đến việc GC (Garbage Collector) phải "thở oxy" dọn rác hay không. Đây mới chỉ là 500 phần tử thôi nhé. Nếu con số là vài nghìn, vài chục nghìn hay cả triệu thì ... mình không dám nghĩ 😁 Tại sao lại có kết quả như thế thì chắc hẹn mọi người ở những bài viết kế tiếp nha.

Đến đây thì mọi người đã có những cái nhìn khác về cách sử dụng của mỗi hàm. Thực ra mỗi hàm sẽ có những tác dụng khác nhau cho những nghiệp vụ khác nhau đấy nên đừng vì ví dụ lần này mà ghét += anh em nhé , += chưa hẳn đã xấu đâu, có lúc ta rất cần nó mà 😁

Mọi người đi qua cho mình ý kiến góp ý nha, về độ chính xác code ví dụ nè, về độ chính xác bài viết nè, để mỗi bài viết sau càng chất lượng hơn nhé. Cảm ơn mọi người rất nhiều vì đã đọc bài viết.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí