4

This morning, I stumbled on this thread Why is it faster to process a sorted array than an unsorted array? and found it really interesting !

I wanted to give it a try in Objective-C and while implementing it, I faced the problem of sorting an array of integers. Hence the following question.

Let's consider an array of arraySize integers, initialized with random values between 0 and 256:

int data[arraySize];
for (int c = 0; c < arraySize; ++c)
{
    data[c] = arc4random() % 256;
}

I would like to sort this array and store the result in another array of integers. In C++ we could do something like :

std::sort(data, ...);

In Java, we would use :

Arrays.sort(data);

In Objective-C, I've done it like that :

int sortedData[arraySize];
NSArray* sortedArray = [NSArray array];
// Initialize the array to sort.
for ( int i = 0 ; i < arraySize ; ++i )
{
    sortedArray = [sortedArray arrayByAddingObject:[NSNumber numberWithInt:data[i]]];
}
// Sort the array.
sortedArray = [sortedArray sortedArrayUsingSelector:@selector(compare:)];
// Copy the array back into a int[] array.
for (int c = 0; c < arraySize; ++c)
{
    sortedData[c] = [sortedArray[c] intValue];
}

It works, but it seems to me it's a real pain and it's not optimized at all ! How could I improve this ?

Community
  • 1
  • 1
Andy M
  • 5,945
  • 7
  • 51
  • 96

3 Answers3

8

The "non-optimized" statement is true only for the code you have. Apple's frameworks are highly optimized and you're not supposed to second guess Apple, they've already second guessed you.

First, use methods for the purpose they have been created for. The way you're creating the unsorted array just wastes memory. In every single step of the loop, you're creating a new instance of the array, in the end, you'll end up with 256 (or whatever the count of the original integer array is) arrays, that's just superfluous.

So, if you really, badly want to use Objective-C for solving this problem, you can use a mutable array and you will only need one NSMutableArray:

int array[256];
// populate the C array here

NSMutableArray *objcArray = [NSMutableArray array];
for (int i = 0; i < sizeof(array) / sizeof(*array); i++) {
    [objcArray addObject:[NSNumber numberWithInt:array[i]];
}

[objcArray sortUsingSelector:@selector(compare:)];

By the way, even the two loops (one for populating the C array and one for transforming it into an NSMutableArray) are unnecessary here. You could just write

const size_t size = 256;
NSMutableArray *objcArray = [NSMutableArray array];
for (int i = 0; i < size; i++) {
    [objcArray addObject:[NSNumber numberWithInt:arc4random_uniform(256)];
}

[objcArray sortUsingSelector:@selector(compare:)];

However, you don't really need Objective-C for sorting an integer array. You can simply write a C function (or if you want, an Objective-C method) for sorting the integer array in-place, and that can be somewhat more efficient or faster:

#include <math.h>
#include <stdlib.h>
#include <unistd.h>

int compare(const void *first, const void *second)
{
    return *(const int *)first - *(const int *)second;
}

- (void)sortArray:(int *)array ofSize:(size_t)sz
{
    qsort(array, sz, sizeof(*array), compare);
}

Then use it like this:

int array[256];
for (int i = 0; i < sizeof(array) / sizeof(*array); i++) {
    array[i] = arc4random_uniform(256);
}

[self sortArray:array ofSize:sizeof(array) / sizeof(*array)];

Also, read this about arrays. Really good article.

  • No no, I succeeded in using Objective-C but your example of C code is perfect, I'll use this instead ! Thanks a lot ! – Andy M Nov 02 '12 at 11:03
  • @AndyM In case you find this answer useful, please upvote/accept when you'll be able to do so, thanks. –  Nov 02 '12 at 11:04
  • Why do you use "sizeof(array) / sizeof(*array)" all the time instead of a constant with the array size ? Is it more effecient in any way or is it just by habit ? In your MutableArray exemple, it would generate an infinite loop, doesn't it ? Increasing the size of the array each time it loops... – Andy M Nov 02 '12 at 11:18
  • Actually I receive the following message "Invalid application of 'sizeof' to interface 'NSMutableArray' in non-fragile ABI" when using sizeOf on your MutableArray exemple... – Andy M Nov 02 '12 at 11:22
  • 1
    @AndyM Neither one: it's not more efficient, since it's the same sort of constant expression that `256` is, neither "just a habit". It is used because it is less error-prone in case you change the array size from 256 to, say, 128 - in this case, the resulting program will nicely segfault, which is not what one wants. Also, not having to rewrite all the sizes when resizing an array is much more elegant. I also don't cast the return value of `malloc()`, write pointers' asterisk adherent to the variable name instead of the type name, etc. for similar conceptual and good practice-oriented reasons. –  Nov 02 '12 at 11:22
  • But if you have a constant for your array size (256), if you change this to 128, you got no seffault... I do prefer constants tho, it increases readability... Anyway, thanks a lot for your answers, perfect. – Andy M Nov 02 '12 at 11:29
  • @AndyM ***YOU DO.*** If you make `qsort()` believe that it has 256 elements to work with but there are actually only 128, it'll write out of bounds. –  Nov 02 '12 at 11:30
  • @H2C03 I'm sorry, I don't think we're talking about the same stuff. I just said that if you write "int array[MySize]; for (int i = 0; i < MySize; i++) { array[i] = arc4random() % 256; } [self sortArray:array ofSize:MySize];", then it should work... Doesn't it ? Except if someone jumps in and change the array size (I guess that's what you meant)... – Andy M Nov 02 '12 at 11:36
  • @AndyM Yes, that's exactly what I meant. If you hard-code all constants, it will work perfectly, up to the point when somebody messes up one of the constants :) –  Nov 02 '12 at 11:39
  • Okay, I was talking about the code snippets we wrote. Thank you for your answers. – Andy M Nov 02 '12 at 11:40
  • The compare () function will fail if the array contains both very large positive and negative values, where calculating the difference between two ints may produce an overflow. – gnasher729 Mar 15 '14 at 19:12
0

What about using NSSortDescriptor?

NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"propertieToSort" ascending:NO];
NSArray *descArray = [NSArray arrayWithObject:dateDescriptor];
representedItems = [unsortedItems sortedArrayUsingDescriptors:descArray];

I'm not 100% sure this will work with non NSObjects but I swear I used it sometime.

RubenVot
  • 188
  • 10
0

You can use c++ in your obj-c project, just rename your file into .mm and add #include < algorithm > for using std.sort. I think, it will be faster that using NSNumber for int data

aknew
  • 1,101
  • 7
  • 23