2012-03-03 31 views
56

thể trùng lặp:
Why can't strings be mutable in Java and .NET?
Why .NET String is immutable?Tại sao các chuỗi không thay đổi được bằng nhiều ngôn ngữ lập trình?

Một số ngôn ngữ đã chọn cho điều này, chẳng hạn như C#, Java, và Python. Nếu nó được thiết kế để tiết kiệm bộ nhớ hoặc đạt được hiệu quả cho các hoạt động như so sánh, nó có tác dụng gì đối với việc ghép nối và các hoạt động sửa đổi khác?

+1

http://stackoverflow.com/questions/2365272/why-net-string-is-immutable – Mehrdad

+17

C++ 's std :: string' là * not * không thay đổi. – fredoverflow

Trả lời

71

loại bất di bất dịch là một điều tốt thường: (! Bạn không cần phải khóa cái gì đó không thể thay đổi)

  • Họ làm việc tốt hơn cho đồng thời
  • Họ giảm lỗi: đối tượng có thể thay đổi dễ bị tổn thương để được thay đổi khi bạn không mong đợi nó có thể giới thiệu tất cả các loại lỗi lạ ("hành động ở khoảng cách")
  • Chúng có thể được chia sẻ an toàn (nghĩa là nhiều tham chiếu đến cùng một đối tượng) có thể giảm tiêu thụ bộ nhớ và cải thiện sử dụng bộ nhớ cache.
  • Chia sẻ cũng giúp sao chép một hoạt động O (1) rất rẻ khi nó sẽ là O (n) nếu bạn phải lấy một bản sao phòng thủ của một đối tượng có thể thay đổi. Đây là một vấn đề lớn bởi vì sao chép là một hoạt động cực kỳ phổ biến (ví dụ: bất cứ khi nào bạn muốn chuyển các thông số xung quanh ....)

Kết quả là thiết kế ngôn ngữ khá không hợp lý.

Một số ngôn ngữ (đặc biệt là các ngôn ngữ chức năng như Haskell và Clojure) đi xa hơn và làm cho mọi thứ trở nên bất biến. Điều này enlightening video là rất đáng xem nếu bạn quan tâm đến những lợi ích của bất biến.

Có một vài nhược điểm nhỏ với nhiều loại bất biến:

  • Operations tạo ra một chuỗi thay đổi như nối đắt hơn bởi vì bạn cần phải xây dựng các đối tượng mới. Thông thường chi phí là O (n + m) để ghép hai chuỗi bất biến, mặc dù nó có thể đi thấp như O (log (m + n)) nếu bạn sử dụng một cấu trúc dữ liệu chuỗi dựa trên cây như một Rope. Ngoài ra, bạn luôn có thể sử dụng các công cụ đặc biệt như Java của StringBuilder nếu bạn thực sự cần phải nối Chuỗi hiệu quả.
  • Một thay đổi nhỏ trên một chuỗi lớn có thể dẫn đến sự cần thiết phải xây dựng một bản sao hoàn toàn mới của chuỗi lớn, rõ ràng là làm tăng mức tiêu thụ bộ nhớ. Tuy nhiên, lưu ý rằng đây không phải là vấn đề lớn trong các ngôn ngữ được thu gom rác vì bản sao cũ sẽ bị thu gom rác một cách nhanh chóng nếu bạn không giữ một tham chiếu đến nó.

Nhìn chung, lợi thế của bất biến bao la lớn hơn nhiều bất lợi nhỏ. Ngay cả khi bạn chỉ quan tâm đến hiệu suất, các lợi thế đồng thời và tính năng sao chép rẻ tiền nói chung sẽ làm cho các chuỗi bất biến có hiệu suất cao hơn các chuỗi có thể thay đổi được với khóa và sao chép phòng thủ.

+2

Điểm tốt. Nhưng, mỗi đồng xu có hai mặt. Bạn chỉ cần liệt kê các upsides. Điều gì về những nhược điểm? Vì nó là bất biến, chúng ta không thể nối hai chuỗi chỉ bằng cách chờ một chuỗi khác; Chúng ta cũng phải tạo một chuỗi mới ngay cả khi nó chỉ khác với một char từ một chuỗi hiện có, có nghĩa là phân bổ bộ nhớ tiêu thụ CPU. – snowfox

+4

Nếu Strings có thể thay đổi được, với mức sử dụng lớn, nó sẽ tạo ra rất nhiều lỗi khó phát hiện trong các dự án lớn. Chi phí của các lỗi này đơn giản làm cho việc ghép nối chuỗi liên quan đến việc tiêu thụ CPU không đáng kể. Hơn nữa, trong nhiều trường hợp, bạn vẫn cần phải tạo một chuỗi khác nhau khi thực hiện sửa đổi nhỏ ngay cả khi chuỗi có thể thay đổi được. –

+0

Tôi đã mở rộng câu trả lời của mình một chút. Nhưng lưu ý rằng đặc biệt với ví dụ phụ của bạn, chuỗi không thay đổi cũ thường sẽ nhận được GC khá nhanh chóng, do đó tác động đến việc sử dụng bộ nhớ chỉ là rất tạm thời. – mikera

16

Chủ yếu nhằm ngăn ngừa lỗi lập trình. Ví dụ, Strings thường được sử dụng như các khóa trong hashtables. Nếu họ có thể thay đổi, thì hashtable sẽ bị hỏng. Và đó chỉ là một ví dụ khi có một thay đổi dữ liệu trong khi bạn đang sử dụng nó gây ra vấn đề.Bảo mật là một cách khác: nếu bạn kiểm tra xem người dùng có được phép truy cập vào tệp tại một đường dẫn cụ thể hay không trước khi thực hiện thao tác mà họ yêu cầu, chuỗi chứa đường dẫn tốt hơn không thể thay đổi được ...

Nó trở nên quan trọng hơn khi bạn đang làm đa luồng. Dữ liệu không thay đổi có thể được truyền đi một cách an toàn giữa các luồng trong khi dữ liệu có thể thay đổi gây ra nhức đầu vô tận.

Về cơ bản, dữ liệu không thay đổi làm cho mã hoạt động trên lý do dễ dàng hơn. Đó là lý do tại sao các ngôn ngữ thuần túy chức năng cố gắng giữ mọi thứ không thay đổi.

3

Trong Java không chỉ chuỗi nhưng tất cả các lớp Wrapper nguyên thủy (số nguyên, đôi, ký tự, vv) là không thay đổi. Tôi không chắc chắn về lý do chính xác nhưng tôi nghĩ đây là những kiểu dữ liệu cơ bản mà tất cả các lược đồ lập trình đều hoạt động. Nếu họ thay đổi, mọi thứ có thể trở nên hoang dại. Để cụ thể hơn, tôi sẽ sử dụng một ví dụ: Giả sử bạn đã mở kết nối socket đến một máy chủ từ xa. Tên máy chủ sẽ là một Chuỗi và cổng sẽ là Số nguyên. Điều gì nếu các giá trị này được sửa đổi sau khi kết nối được thiết lập.

Theo như hiệu suất có liên quan, Java cấp phát bộ nhớ cho các lớp này từ một phần bộ nhớ riêng biệt được gọi là Literal Pool, chứ không phải từ stack hoặc Heap. The Literal Pool được lập chỉ mục và nếu bạn sử dụng một chuỗi "String" hai lần, chúng trỏ đến cùng một đối tượng từ Literal pool.

+0

Điểm tuyệt vời. Cảm ơn. – snowfox

+0

Hồ bơi "theo nghĩa đen" chỉ được sử dụng trong một số trường hợp nhất định. Đối với các lớp bao bọc số, nó chỉ được sử dụng cho các số trong một phạm vi nhất định (cho int tôi nghĩ -128 đến 127) và chỉ khi thực hiện thao tác boxing (tức là Integer mới (5) sẽ vẫn tạo đối tượng 'Integer' mới, nhưng' Integer n = 5' sẽ tham chiếu một đối tượng 'Integer' trong nhóm thực tập). Đối với 'Chuỗi', điều này chỉ xảy ra với các chữ cái thực tế; nếu bạn tạo chuỗi động (ví dụ: 'String a =" x "; String b = a +" abc ";' (ở đây b là trên heap)) thì nó sẽ là đối tượng mới trên heap bình thường trừ khi bạn cố ý thực tập nó bằng 'String.intern()'. –

+0

@ user1087373: Tôi không nghĩ bạn nên chấp nhận câu trả lời này. Điều này giải thích không có gì. Ngược lại, nó cung cấp lý do không chính xác. – Nawaz

1

Có chuỗi không thay đổi cũng cho phép tham chiếu chuỗi mới dễ dàng, vì các chuỗi tương tự/tương tự sẽ sẵn có từ nhóm các chuỗi đã tạo trước đây. Qua đó giảm chi phí tạo đối tượng mới.

Các vấn đề liên quan