Làm thế nào để nối chuỗi hiệu quả trong đi


727

Trong Go, a stringlà một kiểu nguyên thủy, có nghĩa là nó chỉ đọc và mọi thao tác của nó sẽ tạo ra một chuỗi mới.

Vì vậy, nếu tôi muốn nối chuỗi nhiều lần mà không biết độ dài của chuỗi kết quả, cách tốt nhất để làm điều đó là gì?

Cách ngây thơ sẽ là:

s := ""
for i := 0; i < 1000; i++ {
    s += getShortStringFromSomewhere()
}
return s

nhưng điều đó có vẻ không hiệu quả lắm.


7
Thêm một băng ghế
Ivan Black

1
Lưu ý: Câu hỏi này và hầu hết các câu trả lời dường như đã được viết trước khi append()đi vào ngôn ngữ, đây là một giải pháp tốt cho việc này. Nó sẽ thực hiện nhanh như thế copy()nhưng sẽ phát triển lát đầu tiên ngay cả khi điều đó có nghĩa là phân bổ một mảng sao lưu mới nếu dung lượng không đủ. bytes.Buffervẫn có ý nghĩa nếu bạn muốn các phương thức tiện lợi bổ sung của nó hoặc nếu gói bạn đang sử dụng mong đợi nó.
thomasrutter

7
Nó không chỉ "có vẻ rất không hiệu quả"; nó có một vấn đề cụ thể mà mọi thuê mướn phi CS mới mà chúng tôi từng gặp phải trong vài tuần đầu tiên làm việc. Đó là bậc hai - O (n * n). Hãy nghĩ về chuỗi số : 1 + 2 + 3 + 4 + .... Đó là n*(n+1)/2, diện tích của một tam giác cơ sở n. Bạn phân bổ kích thước 1, sau đó kích thước 2, sau đó kích thước 3, v.v. khi bạn nối các chuỗi bất biến trong một vòng lặp. Tiêu thụ tài nguyên bậc hai này thể hiện theo nhiều cách hơn là chỉ này.
Cướp

Câu trả lời:


856

Cách mới:

Từ Go 1.10 có một strings.Builderloại, vui lòng xem câu trả lời này để biết thêm chi tiết .

Cách cũ:

Sử dụng bytesgói. Nó có một Bufferloại mà thực hiện io.Writer.

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 1000; i++ {
        buffer.WriteString("a")
    }

    fmt.Println(buffer.String())
}

Điều này làm điều đó trong thời gian O (n).


24
thay vì println (chuỗi (buffer.Bytes ())); sử dụng chỉ có thể làm println (buffer.String ())
FigmentEngine

26
Thay vì buffer := bytes.NewBufferString("") , bạn có thể làm var buffer bytes.Buffer. Bạn cũng không cần bất kỳ dấu chấm phẩy nào :).
crazy2be

66
Cực kỳ nhanh Tạo một số chuỗi "+" ngây thơ trong chương trình của tôi đi từ 3 phút đến 1,3 giây .
Malcolm

10
+1 cho "O (n) thời gian"; Tôi nghĩ điều quan trọng là phải đưa ra nhiều nhận xét như thế này.
mâu thuẫn

8
Go 1.10 thêm strings.Builder , mà là giống như bytes.Buffer nhưng nhanh hơn khi mục tiêu cuối cùng của bạn là một chuỗi.
Josh Bleecher Snyder

272

Cách hiệu quả nhất để nối chuỗi là sử dụng hàm dựng sẵn copy. Trong các thử nghiệm của tôi, cách tiếp cận đó nhanh hơn ~ 3 lần so với sử dụng bytes.Buffervà nhanh hơn nhiều (~ 12.000x) so với sử dụng toán tử +. Ngoài ra, nó sử dụng ít bộ nhớ hơn.

Tôi đã tạo một trường hợp thử nghiệm để chứng minh điều này và đây là kết quả:

BenchmarkConcat  1000000    64497 ns/op   502018 B/op   0 allocs/op
BenchmarkBuffer  100000000  15.5  ns/op   2 B/op        0 allocs/op
BenchmarkCopy    500000000  5.39  ns/op   0 B/op        0 allocs/op

Dưới đây là mã để thử nghiệm:

package main

import (
    "bytes"
    "strings"
    "testing"
)

func BenchmarkConcat(b *testing.B) {
    var str string
    for n := 0; n < b.N; n++ {
        str += "x"
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); str != s {
        b.Errorf("unexpected result; got=%s, want=%s", str, s)
    }
}

func BenchmarkBuffer(b *testing.B) {
    var buffer bytes.Buffer
    for n := 0; n < b.N; n++ {
        buffer.WriteString("x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); buffer.String() != s {
        b.Errorf("unexpected result; got=%s, want=%s", buffer.String(), s)
    }
}

func BenchmarkCopy(b *testing.B) {
    bs := make([]byte, b.N)
    bl := 0

    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        bl += copy(bs[bl:], "x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); string(bs) != s {
        b.Errorf("unexpected result; got=%s, want=%s", string(bs), s)
    }
}

// Go 1.10
func BenchmarkStringBuilder(b *testing.B) {
    var strBuilder strings.Builder

    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        strBuilder.WriteString("x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); strBuilder.String() != s {
        b.Errorf("unexpected result; got=%s, want=%s", strBuilder.String(), s)
    }
}

6
Các byte.Buffer về cơ bản sẽ giống như bản sao (với một số sổ sách bổ sung tôi đoán) và tốc độ không khác nhau. Vì vậy, tôi sẽ sử dụng nó :). Sự khác biệt là bộ đệm bắt đầu bằng 0 byte nên nó phải phân bổ lại (điều này làm cho nó có vẻ chậm hơn một chút tôi đoán). Dễ sử dụng hơn, mặc dù.
Aktau

5
buffer.Write(byte) nhanh hơn 30% so với buffer.WriteString. [hữu ích nếu bạn có thể lấy dữ liệu dưới dạng []byte]
Dani-Br

34
Lưu ý rằng kết quả điểm chuẩn bị sai lệch và không xác thực. Các hàm điểm chuẩn khác nhau sẽ được gọi với các giá trị khác nhau b.Nvà vì vậy bạn không so sánh thời gian thực hiện của cùng một tác vụ (ví dụ: một hàm có thể nối thêm 1,000chuỗi, một hàm khác có 10,000thể tạo ra sự khác biệt lớn trong trung bình thời gian của 1 phụ lục, BenchmarkConcat()ví dụ). Bạn nên sử dụng cùng một số phụ lục trong mỗi trường hợp (chắc chắn là không b.N) và thực hiện tất cả các phép nối bên trong phần thân của forphạm vi b.N(nghĩa là, 2 forvòng lặp được nhúng).
icza

18
Ngoài ra, điểm chuẩn sao chép bị sai lệch bằng cách bỏ qua rõ ràng thời gian phân bổ mất, được bao gồm trong các điểm chuẩn khác.
gha.st

6
Ngoài ra, điểm chuẩn sao chép phụ thuộc vào việc biết độ dài của chuỗi kết quả.
Skarllot

227

Trong Go 1.10+ có strings.Builder, ở đây .

Một Builder được sử dụng để xây dựng một chuỗi hiệu quả bằng các phương thức Write. Nó giảm thiểu việc sao chép bộ nhớ. Giá trị 0 đã sẵn sàng để sử dụng.


Thí dụ

Nó gần giống với bytes.Buffer.

package main

import (
    "strings"
    "fmt"
)

func main() {
    // ZERO-VALUE:
    //
    // It's ready to use from the get-go.
    // You don't need to initialize it.
    var str strings.Builder

    for i := 0; i < 1000; i++ {
        str.WriteString("a")
    }

    fmt.Println(str.String())
}

Nhấn vào đây để xem điều này trên sân chơi .


Ghi chú

  • Không sao chép giá trị StringBuilder vì nó lưu trữ dữ liệu cơ bản.
  • Nếu bạn muốn chia sẻ giá trị StringBuilder, hãy sử dụng một con trỏ tới nó.

Giao diện được hỗ trợ

Các phương thức của StringBuilder đang được triển khai với các giao diện hiện có trong tâm trí. Để bạn có thể dễ dàng chuyển sang loại Builder mới trong mã của mình.


Sự khác biệt từ byte.Buffer

  • Nó chỉ có thể phát triển hoặc thiết lập lại.

  • Nó có một cơ chế copyCheck tích hợp để ngăn chặn việc sao chép ngẫu nhiên:

    func (b *Builder) copyCheck() { ... }

  • Trong bytes.Buffer, người ta có thể truy cập các byte cơ bản như thế này : (*Buffer).Bytes().

    • strings.Builder ngăn chặn vấn đề này
    • Đôi khi, đây không phải là một vấn đề và mong muốn thay thế.
    • Ví dụ: Đối với hành vi nhìn trộm khi các byte được truyền cho một io.Readerv.v.

Kiểm tra mã nguồn của nó để biết thêm chi tiết, ở đây .


5
'Thoát' nghĩa là gì? Bạn có nghĩa là thoát trong chuỗi, hoặc chỉ các byte bên dưới có thể bị lộ?
makhdumi

1
@makhdumi Có, thứ 2, tiếp xúc với các byte cơ bản.
Inanc Gumus

Đáng chú ý strings.Builderthực hiện các phương thức của nó bằng cách sử dụng một bộ nhận con trỏ, điều đó đã ném tôi trong giây lát. Kết quả là, tôi có thể sẽ tạo một cái bằng cách sử dụng new.
Duncan Jones

@DuncanJones Tôi đã thêm một ghi chú, tuy nhiên, vì nó được sử dụng chủ yếu cho bộ nhớ đệm dữ liệu, nên sử dụng một con trỏ tới nó khi chia sẻ nó qua funcs, v.v ... Trong cùng một func, bạn cũng có thể sử dụng nó như một con trỏ.
Inanc Gumus

130

Có một chức năng thư viện trong gói chuỗi được gọi là Join: http://golang.org/pkg/strings/#Join

Nhìn vào mã Joinhiển thị một cách tiếp cận tương tự với chức năng chắp thêm Kinopiko đã viết: https://golang.org/src/strings/strings.go#L420

Sử dụng:

import (
    "fmt";
    "strings";
)

func main() {
    s := []string{"this", "is", "a", "joined", "string\n"};
    fmt.Printf(strings.Join(s, " "));
}

$ ./test.bin
this is a joined string

21
Không hoạt động khi bạn phải lặp qua một chuỗi không phải là chuỗi [].
Malcolm

42

Tôi chỉ điểm chuẩn câu trả lời hàng đầu được đăng ở trên trong mã của riêng tôi (một cây đi bộ đệ quy) và toán tử concat đơn giản thực sự nhanh hơn BufferString.

func (r *record) String() string {
    buffer := bytes.NewBufferString("");
    fmt.Fprint(buffer,"(",r.name,"[")
    for i := 0; i < len(r.subs); i++ {
        fmt.Fprint(buffer,"\t",r.subs[i])
    }
    fmt.Fprint(buffer,"]",r.size,")\n")
    return buffer.String()
}

Quá trình này mất 0,81 giây, trong khi mã sau:

func (r *record) String() string {
    s := "(\"" + r.name + "\" ["
    for i := 0; i < len(r.subs); i++ {
        s += r.subs[i].String()
    }
    s += "] " + strconv.FormatInt(r.size,10) + ")\n"
    return s
} 

chỉ mất 0,61 giây. Điều này có lẽ là do chi phí tạo ra cái mớiBufferString .

Cập nhật: Tôi cũng đã điểm chuẩn joinchức năng và nó chạy trong 0,54 giây.

func (r *record) String() string {
    var parts []string
    parts = append(parts, "(\"", r.name, "\" [" )
    for i := 0; i < len(r.subs); i++ {
        parts = append(parts, r.subs[i].String())
    }
    parts = append(parts, strconv.FormatInt(r.size,10), ")\n")
    return strings.Join(parts,"")
}

5
Tôi tin rằng OP quan tâm nhiều hơn đến độ phức tạp của bộ nhớ hơn là độ phức tạp của thời gian chạy, vì thực tế là các phép nối chuỗi ngây thơ dẫn đến việc phân bổ bộ nhớ mới mỗi lần.
galaktor

15
Tốc độ chậm của điều này có thể liên quan đến việc sử dụng fmt.Fprint thay vì buffer.WriteString("\t"); buffer.WriteString(subs[i]);
Robert Jack Will

Tôi vui mừng khi biết rằng phương pháp ưa thích của tôi (strings.Join)chạy như nhanh nhất trong khi từ này nói rằng (bytes.Buffer)là người chiến thắng!
Chetabahana

23

Bạn có thể tạo một lát byte lớn và sao chép các byte của chuỗi ngắn vào đó bằng các lát chuỗi. Có một chức năng được đưa ra trong "Đi hiệu quả":

func Append(slice, data[]byte) []byte {
    l := len(slice);
    if l + len(data) > cap(slice) { // reallocate
        // Allocate double what's needed, for future growth.
        newSlice := make([]byte, (l+len(data))*2);
        // Copy data (could use bytes.Copy()).
        for i, c := range slice {
            newSlice[i] = c
        }
        slice = newSlice;
    }
    slice = slice[0:l+len(data)];
    for i, c := range data {
        slice[l+i] = c
    }
    return slice;
}

Sau đó, khi các hoạt động kết thúc, sử dụng string ( )trên lát byte lớn để chuyển đổi nó thành một chuỗi một lần nữa.


Thật thú vị khi có rất nhiều cách để làm điều này trong Go.
Yitzhak

11
Trong hiệu quả, nó cũng nói rằng ý tưởng này rất hữu ích, nó đã được nắm bắt trong một nội trang. Vì vậy, bạn có thể thay thế chức năng của bạn với append(slice, byte...), dường như.
Aktau

23

Đây là giải pháp nhanh nhất không yêu cầu bạn phải biết hoặc tính toán kích thước bộ đệm tổng thể trước tiên:

var data []byte
for i := 0; i < 1000; i++ {
    data = append(data, getShortStringFromSomewhere()...)
}
return string(data)

Theo điểm chuẩn của tôi , nó chậm hơn 20% so với giải pháp sao chép (8.1ns cho mỗi lần nối thay vì 6,72ns) nhưng vẫn nhanh hơn 55% so với sử dụng byte.Buffer.


23
package main

import (
  "fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    out := fmt.Sprintf("%s %s ",str1, str2)
    fmt.Println(out)
}

2
Chào mừng bạn đến với Stack Overflow! Dành một chút thời gian để đọc qua trợ giúp chỉnh sửa trong trung tâm trợ giúp. Định dạng trên Stack Overflow khác với các trang web khác.
Rizier123

2
Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn. Xin vui lòng cố gắng không làm đông mã của bạn với các bình luận giải thích, điều này làm giảm khả năng đọc của cả mã và các giải thích!
Rizier123

Giải pháp đơn giản
Finn

22

Lưu ý thêm vào năm 2018

Từ Go 1.10 có một strings.Builderloại, vui lòng xem câu trả lời này để biết thêm chi tiết .

Trả lời trước 201x

Mã điểm chuẩn của @ cd1 và các câu trả lời khác là sai. b.Nkhông được thiết lập trong chức năng điểm chuẩn. Nó được thiết lập bởi công cụ kiểm tra cờ vây để xác định xem thời gian thực hiện kiểm tra có ổn định không.

Một hàm điểm chuẩn sẽ chạy cùng b.Nthời gian kiểm tra và kiểm tra bên trong vòng lặp sẽ giống nhau cho mỗi lần lặp. Vì vậy, tôi sửa nó bằng cách thêm một vòng lặp bên trong. Tôi cũng thêm điểm chuẩn cho một số giải pháp khác:

package main

import (
    "bytes"
    "strings"
    "testing"
)

const (
    sss = "xfoasneobfasieongasbg"
    cnt = 10000
)

var (
    bbb      = []byte(sss)
    expected = strings.Repeat(sss, cnt)
)

func BenchmarkCopyPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        bs := make([]byte, cnt*len(sss))
        bl := 0
        for i := 0; i < cnt; i++ {
            bl += copy(bs[bl:], sss)
        }
        result = string(bs)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkAppendPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, cnt*len(sss))
        for i := 0; i < cnt; i++ {
            data = append(data, sss...)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        buf := bytes.NewBuffer(make([]byte, 0, cnt*len(sss)))
        for i := 0; i < cnt; i++ {
            buf.WriteString(sss)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkCopy(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
        for i := 0; i < cnt; i++ {
            off := len(data)
            if off+len(sss) > cap(data) {
                temp := make([]byte, 2*cap(data)+len(sss))
                copy(temp, data)
                data = temp
            }
            data = data[0 : off+len(sss)]
            copy(data[off:], sss)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkAppend(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, 64)
        for i := 0; i < cnt; i++ {
            data = append(data, sss...)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferWrite(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var buf bytes.Buffer
        for i := 0; i < cnt; i++ {
            buf.Write(bbb)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferWriteString(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var buf bytes.Buffer
        for i := 0; i < cnt; i++ {
            buf.WriteString(sss)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkConcat(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var str string
        for i := 0; i < cnt; i++ {
            str += sss
        }
        result = str
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

Môi trường là OS X 10.11.6, Intel Core i7 2.2 GHz

Kết quả kiểm tra:

BenchmarkCopyPreAllocate-8         20000             84208 ns/op          425984 B/op          2 allocs/op
BenchmarkAppendPreAllocate-8       10000            102859 ns/op          425984 B/op          2 allocs/op
BenchmarkBufferPreAllocate-8       10000            166407 ns/op          426096 B/op          3 allocs/op
BenchmarkCopy-8                    10000            160923 ns/op          933152 B/op         13 allocs/op
BenchmarkAppend-8                  10000            175508 ns/op         1332096 B/op         24 allocs/op
BenchmarkBufferWrite-8             10000            239886 ns/op          933266 B/op         14 allocs/op
BenchmarkBufferWriteString-8       10000            236432 ns/op          933266 B/op         14 allocs/op
BenchmarkConcat-8                     10         105603419 ns/op        1086685168 B/op    10000 allocs/op

Phần kết luận:

  1. CopyPreAllocate là cách nhanh nhất; AppendPreAllocatekhá gần với số 1, nhưng việc viết mã dễ dàng hơn.
  2. Concatcó hiệu suất thực sự xấu cả về tốc độ và sử dụng bộ nhớ. Đừng sử dụng nó.
  3. Buffer#WriteBuffer#WriteStringvề cơ bản là giống nhau về tốc độ, trái với những gì @ Dani-Br đã nói trong bình luận. Xem xét stringthực sự là []bytetrong Go, nó có ý nghĩa.
  4. byte.Buffer về cơ bản sử dụng cùng một giải pháp như Copy với việc giữ thêm sách và các công cụ khác.
  5. CopyAppend sử dụng kích thước bootstrap là 64, giống như byte.Buffer
  6. Appendsử dụng nhiều bộ nhớ và allocs hơn, tôi nghĩ nó liên quan đến thuật toán phát triển mà nó sử dụng. Nó không tăng bộ nhớ nhanh như byte.Buffer

Gợi ý:

  1. Đối với tác vụ đơn giản như những gì OP muốn, tôi sẽ sử dụng AppendhoặcAppendPreAllocate . Nó đủ nhanh và dễ sử dụng.
  2. Nếu cần đọc và ghi bộ đệm cùng một lúc, sử dụng bytes.Buffertất nhiên. Đó là những gì nó được thiết kế cho.

13

Đề nghị ban đầu của tôi là

s12 := fmt.Sprint(s1,s2)

Nhưng câu trả lời ở trên sử dụng byte.Buffer - WriteString () là cách hiệu quả nhất.

Đề nghị ban đầu của tôi sử dụng sự phản chiếu và một loại chuyển đổi. Xem (p *pp) doPrint(p *pp) printArg
Không có giao diện Stringer () phổ quát cho các loại cơ bản, như tôi đã nghĩ một cách ngây thơ.

Ít nhất, Sprint () bên trong sử dụng byte.Buffer. Như vậy

`s12 := fmt.Sprint(s1,s2,s3,s4,...,s1000)`

được chấp nhận về mặt phân bổ bộ nhớ.

=> Kết nối Sprint () có thể được sử dụng cho đầu ra gỡ lỗi nhanh.
=> Nếu không thì sử dụng byte.Buffer ... WriteString


8
Nó không được xây dựng và nó không hiệu quả.
peterSO

Nhập một gói (như fmt) có nghĩa là nó không được tích hợp. Nó ở trong thư viện tiêu chuẩn.
Malcolm

Nó chỉ chậm vì nó sử dụng sự phản chiếu trên các đối số của nó. Đó là hiệu quả.
Mặt

11

Mở rộng trên câu trả lời của cd1: Bạn có thể sử dụng append () thay vì copy (). append () làm cho các điều khoản tạm ứng lớn hơn bao giờ hết, tốn thêm một ít bộ nhớ, nhưng tiết kiệm thời gian. Tôi đã thêm hai điểm chuẩn ở đầu của bạn. Chạy cục bộ với

go test -bench=. -benchtime=100ms

Trên thinkpad T400s của tôi, nó mang lại:

BenchmarkAppendEmpty    50000000         5.0 ns/op
BenchmarkAppendPrealloc 50000000         3.5 ns/op
BenchmarkCopy           20000000        10.2 ns/op

4

Đây là phiên bản thực tế của điểm chuẩn được cung cấp bởi @ cd1 ( Go 1.8, linux x86_64) với các bản sửa lỗi được đề cập bởi @icza và @PickBoy.

Bytes.Bufferchỉ 7nhanh hơn so với nối chuỗi trực tiếp thông qua +toán tử.

package performance_test

import (
    "bytes"
    "fmt"
    "testing"
)

const (
    concatSteps = 100
)

func BenchmarkConcat(b *testing.B) {
    for n := 0; n < b.N; n++ {
        var str string
        for i := 0; i < concatSteps; i++ {
            str += "x"
        }
    }
}

func BenchmarkBuffer(b *testing.B) {
    for n := 0; n < b.N; n++ {
        var buffer bytes.Buffer
        for i := 0; i < concatSteps; i++ {
            buffer.WriteString("x")
        }
    }
}

Thời gian:

BenchmarkConcat-4                             300000          6869 ns/op
BenchmarkBuffer-4                            1000000          1186 ns/op

Tôi không nghĩ cài đặt thủ công bN là cách phù hợp để sử dụng các chức năng điểm chuẩn của gói thử nghiệm
PickBoy

@PickBoy, vui lòng chứng minh quan điểm của bạn. Tại sao bạn nghĩ b.Nlà một biến công cộng?
Vitaly Isaev

1
bN không được thiết lập trong chức năng điểm chuẩn. Nó được thiết lập bởi công cụ kiểm tra đi một cách linh hoạt. Một hàm điểm chuẩn sẽ chạy cùng một lần kiểm tra bN, nhưng trong mã của bạn (cũng như mã của @ cd1), mọi kiểm tra trong vòng lặp là một thử nghiệm khác nhau (vì độ dài của chuỗi đang tăng lên)
PickBoy

@PickBoy, nếu bạn để công cụ kiểm tra đi được thiết lập linh hoạt b.N, bạn sẽ kết thúc với một chuỗi có độ dài khác nhau trong các trường hợp kiểm tra khác nhau. Xem bình luận
Vitaly Isaev

Đó là lý do tại sao bạn nên thêm một vòng lặp bên trong của một số lần lặp cố định, như 10000, bên trong vòng lặp bN.
PickBoy

3

goutils.JoinB between

 func JoinBetween(in []string, separator string, startIndex, endIndex int) string {
    if in == nil {
        return ""
    }

    noOfItems := endIndex - startIndex

    if noOfItems <= 0 {
        return EMPTY
    }

    var builder strings.Builder

    for i := startIndex; i < endIndex; i++ {
        if i > startIndex {
            builder.WriteString(separator)
        }
        builder.WriteString(in[i])
    }
    return builder.String()
}

1

Tôi làm điều đó bằng cách sử dụng như sau: -

package main

import (
    "fmt"
    "strings"
)

func main (){
    concatenation:= strings.Join([]string{"a","b","c"},"") //where second parameter is a separator. 
    fmt.Println(concatenation) //abc
}

Điều này không giải quyết vấn đề của OP về việc xây dựng một chuỗi thông qua một loạt các lần lặp, với một vòng lặp for.
codeforester

1
package main

import (
"fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    result := make([]byte, 0)
    result = append(result, []byte(str1)...)
    result = append(result, []byte(str2)...)
    result = append(result, []byte(str1)...)
    result = append(result, []byte(str2)...)

    fmt.Println(string(result))
}

3
Xin vui lòng không gửi mã chỉ câu trả lời. Vui lòng giải thích những gì mã này làm và tại sao nó là giải pháp.
Korashen

-1

kết quả điểm chuẩn với thống kê phân bổ bộ nhớ. kiểm tra mã điểm chuẩn tại github .

sử dụng chuỗi.Builder để tối ưu hóa hiệu suất.

go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/hechen0/goexp/exps
BenchmarkConcat-8                1000000             60213 ns/op          503992 B/op          1 allocs/op
BenchmarkBuffer-8               100000000               11.3 ns/op             2 B/op          0 allocs/op
BenchmarkCopy-8                 300000000                4.76 ns/op            0 B/op          0 allocs/op
BenchmarkStringBuilder-8        1000000000               4.14 ns/op            6 B/op          0 allocs/op
PASS
ok      github.com/hechen0/goexp/exps   70.071s

vui lòng cung cấp tín dụng cho @ cd1 cho các trường hợp thử nghiệm ban đầu bạn đang xây dựng tại đây.
colm.anseo

-2
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))

5
Đây là giải pháp rất chậm, vì nó sử dụng sự phản chiếu, nó phân tích chuỗi định dạng và nó tạo một bản sao của dữ liệu để []byte(s1)chuyển đổi. So sánh nó với các giải pháp khác được đăng, bạn có thể đặt tên cho một lợi thế duy nhất của giải pháp của bạn?
pts

-5

strings.Join() từ gói "chuỗi"

Nếu bạn có kiểu không khớp (như nếu bạn đang cố gắng tham gia một chuỗi int và chuỗi), bạn thực hiện RANDOMTYPE (điều bạn muốn thay đổi)

VÍ DỤ:

package main

import (
    "fmt"
    "strings"
)

var intEX = 0
var stringEX = "hello all you "
var stringEX2 = "people in here"


func main() {
    s := []string{stringEX, stringEX2}
    fmt.Println(strings.Join(s, ""))
}

Đầu ra:

hello all you people in here

4
Mã này thậm chí không biên dịch: strings.Join()chỉ mất 2 tham số: một lát và dấu phân cách string.
icza

điều này không thể giúp được
Anshu

thêm một số thay đổi ở đây.
Anshu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.