5

I'm getting data from my database in the reverse order of how I need it to be. In order to correctly order it I have a couple choices: I can insert each new piece of data gotten at index 0 of my array, or just append it then reverse the array at the end. Something like this:

let data = ["data1", "data2", "data3", "data4", "data5", "data6"]
var reversedArray = [String]()

for var item in data {
   reversedArray.insert(item, 0)
}

// OR

reversedArray = data.reverse()

Which one of these options would be faster? Would there be any significant difference between the 2 as the number of items increased?

MarksCode
  • 8,074
  • 15
  • 64
  • 133
  • 5
    Try it and measure! – Martin R Feb 22 '17 at 15:26
  • You mean `reversedArray.insert(item, at: 0)` and `reversedArray = data.reversed()`, right? Have you benchmarked it? If so, what did you find? – Hamish Feb 22 '17 at 15:26
  • No experience with swift so I'm just guessing that .reverse should be more efficient. IMO you should also consider ReverseRandomAccessCollection http://stackoverflow.com/questions/34558390/why-does-the-reverse-function-in-the-swift-standard-library-return-reverserand – Jake Feb 22 '17 at 15:27
  • 1
    Well I guess I could also just access my array elements in reverse order which would probably be the fastest: `array[array.count - 1 - i]` – MarksCode Feb 22 '17 at 15:30

3 Answers3

11

Appending new elements has an amortized complexity of roughly O(1). According to the documentation, reversing an array has also a constant complexity.

Insertion has a complexity O(n), where n is the length of the array and you're inserting all elements one by one.

So appending and then reversing should be faster. But you won't see a noticeable difference if you're only dealing with a few dozen elements.

DrummerB
  • 39,814
  • 12
  • 105
  • 142
  • 5
    Obtaining a *reverse view* has constant complexity. Reversing an array (with `array.reverse()` or `newArray = array.reversed()`) has O(n) complexity. – Martin R Feb 22 '17 at 16:08
  • 1
    @MartinR Isn't that what array.reversed() does though? From the documentation: "reversed(): Returns a view presenting the elements of the collection in reverse order. [...] Complexity: O(1)" – DrummerB Feb 22 '17 at 16:30
  • 1
    There are multiple "reversed" methods. In `array = array.reversed()` this method https://developer.apple.com/reference/swift/sequence/1641355-reversed of SequenceType is called. – Martin R Feb 22 '17 at 16:32
4

Creating the array by repeatedly inserting items at the beginning will be slowest because it will take time proportional to the square of the number of items involved.

(Clarification: I mean building the entire array reversed will take time proportional to n^2, because each insert will take time proportional to the number of items currently in the array, which will therefore be 1 + 2 + 3 + ... + n which is proportional to n squared)

Reversing the array after building it will be much faster because it will take time proportional to the number of items involved.

Just accessing the items in reverse order will be even faster because you avoid reversing the array.

Look up 'big O notation' for more information. Also note that an algorithm with O(n^2) runtime can outperform one with O(n) for small values of n.

simonWasHere
  • 1,307
  • 11
  • 13
  • 1
    Ok true, but all array implementations I have seen in common use are not set up to allow insertion at the start without moving the existing objects, so each insertion likely takes time proportional to the number of elements, and 1 + 2 + 3 + ... + n is proportional to n squared. – simonWasHere Feb 22 '17 at 15:42
  • 1
    Each insert yes, but he is building a list of n elements by inserting one at a time, building the entire list with ALL of the inserts is O(n^2) because the insert cost is 1 + 2 + 3 + ... + n which is O(n^2) – simonWasHere Feb 22 '17 at 15:47
  • 1
    Sorry for the confusion, I edited my answer to be more clear. However I think you are wrong about the log n. Consider taking two copies of the sequence 1 + 2 + 3 + ... + n and paring them in reverse order to get: (n + 1) + (n - 1 + 2) + ... + (1 + n). This equals 2x the original sum and clearly equals n*(n+1). So the original sum equals half of n^2 + n which is O(n^2). – simonWasHere Feb 22 '17 at 15:56
1

My test results…

    do {
        let start = Date()
        (1..<100).forEach { _ in
            for var item in data {
                reversedArray.insert(item, at: 0)
            }
        }
        print("First: \(Date().timeIntervalSince1970 - start.timeIntervalSince1970)")
    }



    do {
        let start = Date()
        (1..<100).forEach { _ in
            reversedArray = data.reversed()
        }
        print("Second: \(Date().timeIntervalSince1970 - start.timeIntervalSince1970)")
    }


First: 0.0124959945678711
Second: 0.00890707969665527

Interestingly, running them 10,000 times…

First: 7.67399883270264
Second: 0.0903480052947998
Ashley Mills
  • 50,474
  • 16
  • 129
  • 160
  • So reversing the array at the end takes around 70% of inserting at 0. Most people's hypothesis were true then. Interesting, thanks for the tests. – MarksCode Feb 22 '17 at 15:38
  • 2
    This wasn't something *you* could have done? Sorry for being critical - but why did you post this question if "most people's hypothesis were true"? You could have tested this! –  Feb 22 '17 at 15:42
  • 1
    I asked on here because I thought someone would have the answer already – MarksCode Feb 22 '17 at 16:03
  • 1
    While this this sort of test can profile a specific case it tells you relatively little about performance in general. By incrementally expanding an array with a small capacity you are triggering some of the wors cases for exponential capacity expansion (https://developer.apple.com/reference/swift/array/1538388-capacity). Preallocating capacity with [`reserveCapacity`](https://developer.apple.com/reference/swift/array/1538966-reservecapacity) could dramatically change the result. – Jonah Feb 22 '17 at 16:10