Câu trả lời:
TL; DR:
Sử dụng các biểu tượng không chỉ tiết kiệm thời gian khi thực hiện so sánh, mà còn tiết kiệm bộ nhớ, vì chúng chỉ được lưu trữ một lần.
Biểu tượng Ruby là bất biến (không thể thay đổi), điều này làm cho việc tìm kiếm thứ gì đó dễ dàng hơn nhiều
Câu trả lời ngắn gọn (ish):
Sử dụng các biểu tượng không chỉ tiết kiệm thời gian khi thực hiện so sánh, mà còn tiết kiệm bộ nhớ, vì chúng chỉ được lưu trữ một lần.
Các biểu tượng trong Ruby về cơ bản là "các chuỗi bất biến" .. điều đó có nghĩa là chúng không thể thay đổi và nó ngụ ý rằng cùng một biểu tượng khi được tham chiếu nhiều lần trong mã nguồn của bạn, luôn được lưu trữ dưới dạng cùng một thực thể, ví dụ có cùng một id đối tượng .
Mặt khác, chuỗi có thể thay đổi , chúng có thể được thay đổi bất cứ lúc nào. Điều này ngụ ý rằng Ruby cần lưu trữ từng chuỗi bạn đề cập trong toàn bộ mã nguồn của mình trong thực thể riêng biệt, ví dụ: nếu bạn có một chuỗi "tên" nhiều lần được đề cập trong mã nguồn của mình, thì Ruby cần lưu trữ tất cả các chuỗi này trong các đối tượng Chuỗi riêng biệt, bởi vì chúng có thể thay đổi sau này (đó là bản chất của chuỗi Ruby).
Nếu bạn sử dụng một chuỗi làm khóa Hash, Ruby cần đánh giá chuỗi và xem nội dung của nó (và tính hàm băm trên đó) và so sánh kết quả với các giá trị (băm) của các khóa đã được lưu trong Hash .
Nếu bạn sử dụng một biểu tượng làm khóa Hash, thì nó sẽ ngầm hiểu rằng nó không thể thay đổi được, vì vậy về cơ bản, Ruby có thể chỉ cần so sánh (id hàm băm của) id đối tượng với các id đối tượng (băm) của các khóa đã được lưu trữ trong Hash. (nhanh hơn nhiều)
Nhược điểm: Mỗi biểu tượng tiêu thụ một vị trí trong bảng biểu tượng của trình thông dịch Ruby, không bao giờ được phát hành. Biểu tượng không bao giờ được thu gom rác. Vì vậy, trường hợp góc là khi bạn có một số lượng lớn các ký hiệu (ví dụ: các biểu tượng được tạo tự động). Trong trường hợp đó, bạn nên đánh giá điều này ảnh hưởng đến kích thước của trình thông dịch Ruby của bạn.
Ghi chú:
Nếu bạn thực hiện so sánh chuỗi, Ruby có thể so sánh các biểu tượng chỉ bằng id đối tượng của chúng mà không phải đánh giá chúng. Điều đó nhanh hơn nhiều so với việc so sánh các chuỗi cần được đánh giá.
Nếu bạn truy cập hàm băm, Ruby luôn áp dụng hàm băm để tính toán "khóa băm" từ bất kỳ khóa nào bạn sử dụng. Bạn có thể tưởng tượng một cái gì đó giống như MD5-hash. Và rồi Ruby so sánh những "khóa băm" đó với nhau.
Câu trả lời dài:
Lý do là hiệu quả, với nhiều mức tăng qua Chuỗi:
O(n)
cho Chuỗi và hằng cho Biểu tượng.Hơn nữa, Ruby 1.9 đã giới thiệu một cú pháp đơn giản hóa chỉ để băm với các khóa biểu tượng (ví dụ h.merge(foo: 42, bar: 6)
) và Ruby 2.0 có các đối số từ khóa chỉ hoạt động cho các khóa biểu tượng.
Ghi chú :
1) Bạn có thể ngạc nhiên khi biết rằng Ruby đối xử với String
các khóa khác với bất kỳ loại nào khác. Thật:
s = "foo"
h = {}
h[s] = "bar"
s.upcase!
h.rehash # must be called whenever a key changes!
h[s] # => nil, not "bar"
h.keys
h.keys.first.upcase! # => TypeError: can't modify frozen string
Chỉ dành cho khóa chuỗi, Ruby sẽ sử dụng bản sao được đóng băng thay vì chính đối tượng.
2) Các chữ cái "b", "a" và "r" chỉ được lưu trữ một lần cho tất cả các lần xuất hiện :bar
trong một chương trình. Trước Ruby 2.2, một ý tưởng tồi là liên tục tạo ra cái mới Symbols
không bao giờ được sử dụng lại, vì chúng sẽ tồn tại trong bảng tra cứu Biểu tượng toàn cầu mãi mãi. Ruby 2.2 sẽ thu gom rác, vì vậy không phải lo lắng.
3) Trên thực tế, việc tính toán hàm băm cho Biểu tượng không mất nhiều thời gian trong Ruby 1.8.x, vì ID đối tượng được sử dụng trực tiếp:
:bar.object_id == :bar.hash # => true in Ruby 1.8.7
Trong Ruby 1.9.x, điều này đã thay đổi khi băm thay đổi từ phiên này sang phiên khác (bao gồm cả phiên bản Symbols
):
:bar.hash # => some number that will be different next time Ruby 1.9 is ran
Re: lợi thế của việc sử dụng một chuỗi là gì?
(Rất) tìm kiếm giá trị nhanh hơn một chút vì băm một biểu tượng tương đương với băm một số nguyên so với băm một chuỗi.
Nhược điểm: tiêu thụ một vị trí trong bảng biểu tượng của chương trình không bao giờ được phát hành.
Tôi rất quan tâm đến việc theo dõi liên quan đến chuỗi đông lạnh được giới thiệu trong Ruby 2.x.
Khi bạn xử lý nhiều chuỗi đến từ một kiểu nhập văn bản (ví dụ tôi đang nghĩ đến các thông số hoặc tải trọng HTTP, thông qua Rack), cách sử dụng chuỗi ở mọi nơi dễ dàng hơn.
Khi bạn đối phó với hàng tá trong số họ nhưng họ không bao giờ thay đổi (nếu họ là "từ vựng" kinh doanh của bạn), tôi muốn nghĩ rằng việc đóng băng chúng có thể tạo ra sự khác biệt. Tôi chưa thực hiện bất kỳ điểm chuẩn nào, nhưng tôi đoán nó sẽ đóng hiệu suất biểu tượng.