2013-07-23 22 views
9

Trong ứng dụng của tôi, tôi cần khả năng trao đổi các phần tử trong bộ sưu tập. Vì vậy, tôi có một lựa chọn:Biến có thể thay đổi và Bộ sưu tập có thể thay đổi

  1. sử dụng bộ sưu tập có thể thay đổi khai báo là val
  2. hoặc sử dụng bộ sưu tập bất biến khai báo là var (và luôn luôn phân công lại bộ sưu tập mới để var)

Nhưng trong Scala (trong lập trình chức năng) tính đột biến luôn tránh được. Vì vậy, điều gì ít tồi tệ hơn: bộ sưu tập có thể thay đổi được khai báo sử dụng val hoặc bộ sưu tập không thay đổi được khai báo là var?

+1

Nếu không có nhiều ngữ cảnh hơn, nó không thực sự tạo sự khác biệt. Không phải là cách tiếp cận chức năng. Bộ sưu tập có thể thay đổi sẽ có thể hoạt động tốt hơn so với var không thay đổi. – dbyrne

+0

@dbyrne * Bộ sưu tập có thể thay đổi sẽ có thể hoạt động tốt hơn so với biến thể không thay đổi * whoa? không phải hai người chơi trong cùng một đội? ví dụ. 'val misterMutableCollection = collection.mutable ....' –

+1

@ om-nom-nom Với một var không thay đổi, bạn sẽ phải xây dựng một bộ sưu tập hoàn toàn mới và trao đổi nó. Với một val có thể thay đổi, bạn có thể sử dụng bộ sưu tập gốc và các phương thức gọi để thay đổi nó. – dbyrne

Trả lời

1

Tính linh hoạt là con thú mà bạn nên giữ trong lồng. Lý tưởng nhất, trong loại được kiểm nghiệm và sử dụng rất cao của lồng như bộ sưu tập có thể thay đổi scala là.

0

Chuyển nhượng lại cho var có thể khó đọc/duy trì, đặc biệt nếu điều này xảy ra ở nhiều vị trí trong một phương pháp, ví dụ.

Vì vậy, với số val, bạn nhận được ít nhất một tham chiếu bất biến.

Nói chung phạm vi của một bộ sưu tập có thể thay đổi nên càng nhỏ càng tốt. Vì vậy, sau khi bạn đã điền đầy đủ bộ sưu tập có thể thay đổi được (trong trường hợp có bộ đệm ngắn), bạn có thể biến nó thành một bộ lọc không thể thay đổi, ví dụ để trả về kết quả của một phương thức.

Vì dbyrne đã chỉ ra chính xác: Nếu không thể chuyển đổi bộ sưu tập có thể thay đổi thành bộ sưu tập bất biến (ví dụ: nếu bạn đang triển khai bộ nhớ cache) và bộ sưu tập có thể thay đổi là một phần của API công khai, thì var có thể tốt hơn. Một tùy chọn khác trong trường hợp này là tạo một bản sao bất biến trong phương thức triển khai API công khai.

+1

Đây là câu trả lời tốt nhất cho đến nay theo ý kiến ​​của tôi, nhưng có những trường hợp quan trọng hơn đối với bộ sưu tập là không thay đổi, và tham chiếu không quan trọng lắm. Nếu bạn là tác giả thư viện và bạn muốn đảm bảo rằng bất kỳ ai sử dụng API của bạn không làm thay đổi bộ sưu tập sau khi bạn trả lại cho họ, một biến thể không thay đổi sẽ phù hợp. – dbyrne

+0

@dbyrne Cảm ơn bạn đã bình luận; Tôi đã cập nhật câu trả lời. – Beryllium

4

Nếu bạn sử dụng var giữ bộ sưu tập không thay đổi, bạn có thể xuất bản nó một cách tự do (mặc dù bạn có thể muốn đánh dấu var@volatile). Tại bất kỳ thời điểm nào, mã khác chỉ có thể nhận được một ảnh chụp nhanh cụ thể của trạng thái đó mà không bao giờ có thể thay đổi.

Nếu bạn sử dụng val giữ một phiên bản thu thập có thể thay đổi, thì bạn phải bảo vệ cẩn thận vì nó có thể được chứng kiến ​​ở trạng thái không nhất quán trong khi được cập nhật.

+0

Bạn có thể chia sẻ thêm một chút thông tin về cách hoạt động của @volatile trong Scala không? Từ những gì tôi hiểu nó cờ rằng nhiều chủ đề có thể được truy cập nó cùng một lúc, nhưng tôi không chắc chắn những gì chú thích này thực sự thay đổi hoặc không để biến? nhờ – LaloInDublin

+1

@volatile hoạt động chính xác giống như từ khóa dễ bay hơi trong java. Nó hoạt động trên cấp VM. Xem http://stackoverflow.com/questions/2694439/how-does-volatile-actually-work chẳng hạn. –

10

Điều này thực sự phụ thuộc vào việc bạn có cần chia sẻ bộ sưu tập đó một cách rộng rãi hay không. Lợi thế của một bộ sưu tập có thể thay đổi là nó thường nhanh hơn bộ sưu tập bất biến, và dễ dàng hơn để có một đối tượng duy nhất để vượt qua thay vì phải đảm bảo bạn có thể đặt var từ các ngữ cảnh khác nhau. Nhưng kể từ khi họ có thể thay đổi ra từ dưới bạn, bạn phải cẩn thận ngay cả trong một bối cảnh đơn luồng:

import collection.mutable.ArrayBuffer 
val a = ArrayBuffer(1,2,3,4) 
val i = a.iterator 
i.hasNext    // True 
a.reduceToSize(0) 
i.next    // Boom! 

java.lang.IndexOutOfBoundsException: 0 
at scala.collection.mutable.ResizableArray$class.apply(ResizableArray.scala:43) 
    ... 

Vì vậy, nếu nó sẽ được sử dụng rộng rãi, bạn nên xem xét liệu bạn có thể một cách thích hợp cẩn thận để tránh vấn đề như thế này Nói chung là an toàn hơn khi sử dụng một số var cho một bộ sưu tập bất biến; sau đó bạn có thể nhận được lỗi thời, nhưng ít nhất bạn sẽ không rơi vào khuôn mặt của bạn với một segfault.

var v = Vector(1,2,3,4) 
val i = v.iterator 
i.hasNext   // True 
v = Vector[Int]() 
i.next    // 1 

Bây giờ, tuy nhiên, bạn phải vượt qua v như một giá trị trả về từ bất kỳ phương pháp nào có thể sửa đổi nó (bên ngoài của lớp chứa nó, ít nhất).Điều này cũng có thể gây ra vấn đề nếu bạn quên cập nhật các giá trị ban đầu:

var v = Vector(1,2,3,4) 
def timesTwo = v.map(_ * 2) 
timesTwo 
v  // Wait, it's still 1,2,3,4?! 

Nhưng sau đó điều này không cập nhật hoặc, nó ?:

a.map(_ * 2) // Map doesn't update, it produces a new copy! 

Vì vậy, như một quy luật chung,

  1. hiệu suất đòi hỏi bạn phải sử dụng một - sử dụng nó
  2. phạm vi địa phương trong phạm vi một phương pháp - sử dụng bộ sưu tập có thể thay đổi
  3. chung với chủ đề/lớp khác - sử dụng bộ sưu tập bất biến
  4. thực hiện trong một lớp học, mã đơn giản - sử dụng bộ sưu tập có thể thay đổi
  5. thực hiện trong một lớp học, mã phức tạp - sử dụng bất biến

nhưng bạn nên có thể vi phạm điều này thường xuyên khi bạn dính vào nó.

1

Một phương pháp an toàn Tôi đã sử dụng hoạt động như thế này .. đầu tiên ẩn var của bạn để làm cho nó chủ đề an toàn như thế này:

private[this] val myList: scala.collection.mutable.(whatever) 

tin [này] hạn chế một biến không chỉ để lớp này, nhưng chỉ với ví dụ chính xác này. Không có biến nào khác có thể truy cập nó.

Tiếp theo, hãy một hàm helper để trả lại một phiên bản bất biến của nó bất cứ khi nào nhu cầu một cái gì đó bên ngoài để làm việc với nó:

def myListSafe = myList.toList (or some other safe conversion function like toMap, etc) 

Bạn có thể nhận được một số chi phí thực hiện việc chuyển đổi này, nhưng nó mang lại cho bạn sự linh hoạt của việc sử dụng một bộ sưu tập có thể thay đổi một cách an toàn trong lớp - trong khi cung cấp cơ hội xuất chủ đề an toàn khi cần. Như bạn đã chỉ ra các tùy chọn khác là liên tục biến đổi một var trỏ đến một bộ sưu tập bất biến.

0

Đồng ý với Rex, mối quan tâm lớn nhất là liệu bạn có đang chia sẻ bộ sưu tập hay không. Trong trường hợp đó sử dụng bất biến. Một giải pháp thay thế khác:

@volatile 
private var myList = immutable.List.empty[Foo] 
def theList = myList