2014-12-19 32 views
38

Cố gắng hiểu cách so sánh các mảng nhanh chóng.So sánh các mảng trong swift

var myArray1 : [String] = ["1","2","3","4","5"] 
var myArray2 : [String] = ["1","2","3","4","5"] 

// 1) Comparing 2 simple arrays 

if(myArray1 == myArray2) { 
    println("Equality") 
} else { 
    println("Equality no") 
} 
// -> prints equality -> thanks god 

// 2) comparing to a "copy" of an array 

// swift copies arrays when passed as parameters (as per doc) 
func arrayTest(anArray: [String]) -> Bool { 
    return anArray == myArray1 
} 

println("Array test 1 is \(arrayTest(myArray1))") 
println("Array test 2 is \(arrayTest(myArray2))") 
// equality works for both 

myArray2.append("test") 
println("Array test 2 is \(arrayTest(myArray2))") 
// false (obviously) 

myArray2.removeAtIndex(5) 
println("Array test 2 is \(arrayTest(myArray2))") 
// true 

Apple cho biết có tối ưu hóa đằng sau hiện trường trên bản sao mảng. Hình như đôi khi - không phải luôn luôn - các cấu trúc thực sự được sao chép hay không.

Điều đó nói rằng,

1) là == lặp qua tất cả các mảng để thực hiện so sánh dựa trên yếu tố? (trông giống như nó) -> Làm thế nào về hiệu suất/bộ nhớ sử dụng trên mảng rất lớn sau đó?

2) Chúng tôi chắc chắn == sẽ bao giờ trả về true nếu tất cả các phần tử đều bằng nhau? Tôi có những ký ức xấu về == trên Java Strings

3) Có cách nào để kiểm tra xem myArray1 và myArray2 có sử dụng kỹ thuật "cùng một vị trí bộ nhớ"/con trỏ/v.v không? Tôi sau khi hiểu được cách tối ưu hóa hoạt động và tiềm năng cảnh báo.

Cảm ơn.

+0

So sánh con trỏ trực tiếp là '===' – Anorak

+0

Không hoạt động. === nói -> [Chuỗi] không phù hợp với AnyObject –

+0

@Anorak '===' chỉ dành cho các lớp, 'Mảng' là một cấu trúc. – Kirsteins

Trả lời

58

Bạn nói đúng là hơi lo lắng về ==:

struct NeverEqual: Equatable { } 
func ==(lhs: NeverEqual, rhs: NeverEqual)->Bool { return false } 
let x = [NeverEqual()] 
var y = x 
x == y // this returns true 

[NeverEqual()] == [NeverEqual()] // false 
x == [NeverEqual()] // false 

let z = [NeverEqual()] 
x == z // false 

x == y // true 

y[0] = NeverEqual() 
x == y // now false 

Tại sao? mảng Swift không phù hợp với Equatable, nhưng họ có một nhà điều hành ==, được định nghĩa trong thư viện chuẩn như:

func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool 

Toán tử này vòng qua các yếu tố trong lhsrhs, so sánh các giá trị tại mỗi vị trí. Nó không không so sánh bitwise - nó gọi toán tử == trên mỗi cặp phần tử. Điều đó có nghĩa là nếu bạn viết một tuỳ chỉnh == cho phần tử của bạn, nó sẽ được gọi.

Nhưng nó chứa tối ưu hóa - nếu bộ đệm cơ bản cho hai mảng giống nhau, nó không bận tâm, nó chỉ trả về đúng (chúng chứa các phần tử giống nhau, tất nhiên chúng đều bằng nhau!).

Vấn đề này hoàn toàn là lỗi của nhà điều hành bình đẳng NeverEqual. Sự bình đẳng phải là sự chuyển đổi, đối xứng và phản xạ, và cái này không phản xạ (x == x là sai). Nhưng nó vẫn có thể bắt bạn bất ngờ.

Mảng nhanh là sao chép khi viết - vì vậy khi bạn viết var x = y nó không thực sự tạo bản sao của mảng, nó chỉ trỏ con trỏ đệm lưu trữ của x tại số y. Chỉ khi x hoặc y bị đột biến thì sau đó nó sẽ tạo bản sao của bộ đệm, sao cho biến không thay đổi không bị ảnh hưởng. Điều này rất quan trọng đối với các mảng hoạt động như các kiểu giá trị nhưng vẫn hoạt động.

Trong các phiên bản đầu tiên của Swift, bạn thực sự có thể gọi === trên mảng (cũng trong các phiên bản đầu, hành vi đột biến là một chút khác nhau, nếu bạn đột biến x, y cũng sẽ thay đổi mặc dù nó đã được công bố với let - mà làm mọi người bối rối nên họ đã thay đổi nó).

Bạn kinda thể sao chép các hành vi cũ của === trên mảng với điều này (Rất thực hiện phụ thuộc không được dựa-trên trừ chọc và thúc giục điều tra) lừa:

let a = [1,2,3] 
var b = a 

a.withUnsafeBufferPointer { outer in 
    b.withUnsafeBufferPointer { inner in 
     println(inner.baseAddress == outer.baseAddress) 
    } 
} 
+2

Có nhiều điều này hơn là đáp ứng mắt. '[1] == [1]' trả về 'true', nhưng' [[1]] == [[1]] 'trả về' sai'. Cẩn thận! – Pitarou

+4

À vâng, vui vẻ. Nó thực sự là do một lỗi trong trình biên dịch Swift (được xác nhận bởi nhóm Swift trước đó), và nó không nên biên dịch. Nó không sử dụng toán tử 'Array'' == '. Không thể, vì mảng không tương đương nên không khớp với định nghĩa mà tôi đã đưa ra trong bài đăng của mình. Thay vào đó những gì đang xảy ra là Swift đang thực hiện chuyển đổi ngầm định của các mảng literal thành một con trỏ, và sau đó '==' cho 'UnsafePointer' đang được sử dụng. Lỗi là, điều này không nên đá vào cho các nhà khai thác, chỉ cho các cuộc gọi chức năng (nó chỉ có để thực hiện kêu gọi các chức năng C mà cần phải mất một mảng const dễ dàng hơn). –

+3

Chỉ vì mục đích chính xác. Bây giờ [[1]] == [[1]] trả về true (Swift 2.3) – PabloR

13

== trong Swift giống với số equals() của Java, nó so sánh các giá trị.

=== trong Swift giống với số == của Java, nó so sánh tham chiếu.

Trong Swift bạn có thể so sánh giá trị nội dung mảng dễ dàng như này:

["1", "2"] == ["1", "2"] 

Nhưng điều này sẽ không hoạt động nếu bạn muốn so sánh tài liệu tham khảo:

var myArray1 = [NSString(string: "1")] 
var myArray2 = [NSString(string: "1")] 

myArray1[0] === myArray2[0] // false 
myArray1[0] == myArray2[0] // true 

Vì vậy, câu trả lời:

  1. Tôi nghĩ hiệu suất là tối ưu để thực hiện giá trị (không tham khảo) so sánh
  2. Có, nếu bạn muốn so sánh các giá trị
  3. Mảng nhanh là loại giá trị chứ không phải loại tham chiếu. Vì vậy, các vị trí bộ nhớ là như nhau chỉ khi bạn so sánh nó với bản thân (hoặc sử dụng không an toàn con trỏ)
+0

=== không hoạt động. Rõ ràng nó được sử dụng để. Bất kỳ cách nào khác để làm điều đó? Cộng với những gì nếu bạn gọi một func với một mảng rất lớn? Điều này sẽ sao chép toàn bộ mảng như đã nêu trong tài liệu? Trông giống như trên không với tôi. –

+1

'===' sẽ chỉ hoạt động đối với các loại tham chiếu. Các kiểu giá trị luôn luôn chỉ có 1 tham chiếu đến chúng vì vậy '===' sẽ không có ý nghĩa đối với chúng. Mảng được "xem xét" để được sao chép giống như tất cả các cấu trúc, nhưng vì lý do hiệu suất theo mảng mui xe không được sao chép trừ khi đang bị đột biến. – Kirsteins

+0

Lý do 'myArray1 == myArray2' là' NSObject' phù hợp với 'Equatable', gọi' - [equals:] 'để thực hiện phép thử. –

4

Nó phụ thuộc vào làm thế nào để bạn muốn so sánh. Ví dụ: ["1", "2"] == ["1", "2"] // true nhưng ["1", "2"] == ["2", "1"] // false

Nếu bạn cần mà trường hợp thứ hai đến cũng là sự thật và là ok với bỏ qua các giá trị lặp đi lặp lại, bạn có thể làm: Set(["1", "2"]) == Set(["2", "1"]) // true (sử dụng NSSet cho Swift 2)

1

Mảng sẽ tuân theo Equatable trong Swift 4.1, phủ nhận các lưu ý được đề cập trong các câu trả lời trước. Nó xuất hiện này sẽ có sẵn trong Xcode 9.3.

https://swift.org/blog/conditional-conformance/

Nhưng chỉ vì họ thực hiện == không có nghĩa là Array hoặc Optional phù hợp để Equatable. Vì các kiểu này có thể lưu trữ các kiểu không cân bằng, nên chúng ta cần có khả năng biểu thị rằng chúng chỉ tương đương khi lưu trữ một kiểu tương đương.

Điều này có nghĩa là các nhà khai thác này có một giới hạn lớn: họ không thể sử dụng hai cấp độ sâu.

Với sự phù hợp có điều kiện, chúng tôi hiện có thể khắc phục điều này. Nó cho phép chúng tôi viết rằng các loại này phù hợp với Equatable — sử dụng toán tử đã được xác định là == — nếu các loại chúng dựa trên đều tương đương nhau.