4

I do not understand the underlying problem that tries to solve the stable sorting algorithm.

Arrays.sort(Object[]) Javadoc states:

This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort.

But if elements are equal, they are not distringuishable from each other! If you swap two equal elements, this should not affect anything. This is the definition of equality.

So, why do we need stability at all?

UPDATE: My question is about Collections.sort(List<T>) / Objects.sort(Object[]) methods, not Collections.sort(List<T>, Comparator<T>), Objects.sort(Comparator<T>). The latter ones are bit different. But there is still no need for stability for them: if you want predictable compound sorts, then you create appropriate compound comparators.

ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
  • From the various answers (and the wikipedia page) it seems to be that this really applies if you sort an array more than once; I believe as such that the "equal elements" in the Javadoc is misleading: it is not equal as in .equals(). The wiki page actually explains it quite well (the illustration in particular is revealing). – fge Sep 22 '15 at 07:54
  • 2
    The sorting key may be equal but the remaining data may not be equal at all. You should understand what [stability in sorting algorithms](http://stackoverflow.com/q/1517793/995714) is. [What does it mean for a sorting algorithm to be “stable”?](http://programmers.stackexchange.com/q/247440/98103). https://en.wikipedia.org/wiki/Category:Stable_sorts – phuclv Sep 22 '15 at 07:57
  • *If you swap two equal elements, this should not affect anything.* Of course it affects something. It affects the position of both elements. – Manu Sep 22 '15 at 07:57
  • 3
    You are assuming that `compareTo()` is consistent with `equals()`. [Javadoc says](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html): "It is strongly recommended (though not required) that natural orderings be consistent with equals". The stability aspect is relevant when `compareTo()` is **not** consistent with `equals()`. – Andreas Sep 22 '15 at 07:59

4 Answers4

15

Let's say you have two columns. Column name and column date. Then you start ordering your list by date first, afterwards you sort them by name. If your sort is stable what it will produce is that you get the name ordered correctly and if the names are equal you get them sorted by date since your order is stable. But if your order is not stable you won't have any relative ordering between the equal keys.

public static void main (String[] args) 
{
    // your code goes here
    List<FirstNameLastName> list = new ArrayList<FirstNameLastName> ();
    list.add(new("A","B"));
    list.add(new("D","B"));
    list.add(new("F","B"));
    list.add(new("C","C"));
    list.add(new("E","C"));
    list.add(new("B","C"));

    Arrays.sort(list,new Comparator(firstName)); //FIXME
    // A-B , B-C , C-C , D-B , E-C  , F-B
    Arrays.sort(list,new Comparator(lastName)); //FIXME
    // A-B , D-B F-B,B-C,C-C,E-C
    //So as you see here inside the last name B and C each first name 
    //is sorted also

    //However if you just sorted instead directly on last name only
    //A-B , D-B -F,B-C-C,E-C,B-C
}

private class FirstNameLastName {
      String firstName;
      Stirng lastName;
      public FirstNameLastName(String firstName,String lastName) {
          this.firstName = firstName;
          this.lastName = lastName;
       }
 }
Amr
  • 792
  • 3
  • 15
  • Sorry, could you provide some code demonstrating the problem? – ZhekaKozlov Sep 22 '15 at 07:43
  • 1
    Good Example on Wikipedia: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability – hinneLinks Sep 22 '15 at 07:45
  • @ZhekaKozlov Java code for what? There is no problem. – Andreas Sep 22 '15 at 07:53
  • @ZhekaKozlov have a look now hope this clarify it. – Amr Sep 22 '15 at 07:59
  • @Amr Can you provide an example where natural ordering is used instead of `Comparator`? – ZhekaKozlov Sep 22 '15 at 08:03
  • @ZhekaKozlov It is the same concept.You will need it if you will use Collections.sort(List, Comparator) and your class already implements the comparator interface. So the sort has to be stable in order to not mess up any previous ordering. – Amr Sep 22 '15 at 08:05
  • @Amr These are totally different concepts. You will have to implement `compareTo` method, which will be broken, because it will compare only the first field. – ZhekaKozlov Sep 22 '15 at 08:06
  • 4
    What do you mean by, *"which will be broken"*? What is broken? – Manu Sep 22 '15 at 08:08
  • @ZhekaKozlov This is not what I meant. What I meant is as follows. Let's take an example on the FirstNameLastName class. Let's it now implements the comparator interface and that it sorts on the last name only. In this case if we do Collections.sort(List), it will produce the second input only. On the other hand if you do Collections.sort(List, Comparator) and in this comparator you compare first name , then you do Collections.sort(List). It will give you the desired output. – Amr Sep 22 '15 at 08:09
  • @ZhekaKozlov you seem to be conflating two concepts. A `compareTo` implementation is not required to be "consistent with equals". And it often itsn't. – Boris the Spider Sep 22 '15 at 08:10
  • @Many Try to implement compareTo for `FirstNameLastName`. Good implementation would compare `firstName`s and then `lastName`s which will solve the ordering problem. @Amr wants to compare only `firstName`s. – ZhekaKozlov Sep 22 '15 at 08:11
  • @ZhekaKozlov Since you don't seem to have read it yet, I'll post here too: You are assuming that `compareTo()` is consistent with `equals()`. [Javadoc says](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html): *"It is strongly recommended (though not required) that natural orderings be consistent with equals".* The stability aspect is relevant when `compareTo()` is **not** consistent with `equals()`. Or heck, if equals is not comparing *all* fields, which is not absolutely required. – Andreas Sep 22 '15 at 08:12
  • @ZhekaKozlov Define *"good implementation"*. We don't need good implementations here, we're just creating examples for this specific concept. Comparing firstNames only would be sufficient, because the sort is stable. – Manu Sep 22 '15 at 08:14
  • @Andreas This is terrible way of programming. – ZhekaKozlov Sep 22 '15 at 08:20
  • 4
    @ZhekaKozlov Not at all, but you can have that opinion if you so choose. Doesn't change the fact that the `sort` methods are all defined to be stable, so that two objects with different values whose *natural order* is considered the same (`compareTo` returns 0), will sort consistently, i.e. in the order they were before the sort. If you *never* implement a class like that, then you don't care about "stability", and you can ignore that part. – Andreas Sep 22 '15 at 08:24
  • @Andreas Stable sorting is more expensive than instable. Why should I pay for it every time if I don't need it? – ZhekaKozlov Sep 22 '15 at 08:26
  • 2
    If you don't like the standard sort methods, go ahead and write one yourself. – Manu Sep 22 '15 at 08:29
  • 1
    @ZhekaKozlov Why? Have you noticed a performance issue with the builtin sorting? I believe it's a Timsort, which is pretty darn fast. Don't tune code until a profiler tells you where the problem is, if you even have one. --- But, as an exercise, go for it. Just out of curiosity, which algorithm are you implementing? – Andreas Sep 22 '15 at 08:33
  • @Andreas Dual pivot quick sort, which is in-place sort and does not require additional memory (TimSort requires). – ZhekaKozlov Sep 22 '15 at 08:36
  • 1
    The Java authors did ridiculous amounts of research to determine that the cost of stable sorting outweighed the risk of making it easier to shoot yourself in the foot. Years of work have been put into Java's sorting algorithm. It's deeply unlikely that you can do better. – Louis Wasserman Sep 22 '15 at 17:12
  • @LouisWasserman Which algorithm is used for primitives sorting in Java? – ZhekaKozlov Nov 17 '15 at 08:23
  • @ZhekaKozlov did you read [the documentation](https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#sort-int:A-)? – Louis Wasserman Nov 17 '15 at 18:10
  • @LouisWasserman Of course I did. Do you know the difference between `Arrays.sort(int[])` and `Arrays.sort(Object[])`? – ZhekaKozlov Nov 18 '15 at 04:08
  • Yes. It's not clear why you're bringing that up. Object sorting often needs to be stable; primitive sorting doesn't care. – Louis Wasserman Nov 18 '15 at 04:16
6

Unstable sort can suck for UIs. Imagine you are using file explorer in Windows, with a details view of songs. Can you imagine if you kept clicking filetype column to sort by filetype, and it randomly reordered everything within each type-group every time you clicked it?

Stable sort in UI's allows me (the user) to create my own compound sorts. I can chain multiple sorts together, like "sort by name", then "sort by artist", then "sort by type". The final resulting sort prioritizes type, then artist, and then finally, by name. This is because a stable sort actually preserves the previous sort, allowing me to "build" my own sorting from a series of elementary sorts! Whereas unstable sorts nuke the previous sort type's results.

Of course, in code, you'd just make one big, fully defined ordering, and then sort and sort once, rather than a chained "compound" sort like I described above. This is why you tend not to need stable sorts for most application-internal sorting. But when the user drives the click-by-click, stable sorting is the best/simplest.

EDIT: "My question is about Collections.sort(List<T>) / Objects.sort(Object[]) methods"

These are generic sorts that work on anything that defines Comparable. Your class's Comparable implementation might return 0 even when the objects are not technically "equal", because you might be trying for a particular ordering. In other words, these methods are every bit as open to custom orderings as Collections.sort(List<T>, Comparator<T>). And you might want stable sort, or you might not.

VoidStar
  • 5,241
  • 1
  • 31
  • 45
  • @VoidStar For compound sorts you don't need stability. Just craft a compound comparator. – ZhekaKozlov Sep 22 '15 at 08:28
  • 1
    @ZhekaKozlov: My answer explained that in the 3rd paragraph. The compounding of multiple sorts can be useful to an end-user pushing UI buttons, but it isn't a technique that a developer would use for application-internal sorts. – VoidStar Sep 22 '15 at 08:33
  • 1
    @ZhekaKozlov: Also note that crafting a compound comparator "on the fly" , reacting to user's inputs has more complexity associated with it than simply using stable sorts. Not only do you need to implement a dynamic list-based clause-builder (a good bit of inconvenience), but what about cleaning up old clauses? What if they click dozens of sorts? You'd need to clean it up eventually. But stable sorts do this beautifully... they are equivalent to an "infinite clause" compound comparator. – VoidStar Sep 22 '15 at 08:42
4

Consider the example

[{1, 'c'}, {2, 'a'}, {3, 'a'}] 

It is sorted by number field, but not by character. After a stable sort by character:

[{2, 'a'}, {3, 'a'}, {1, 'c'}] 

After an unstable sort the following order is possible:

[{3, 'a'}, {2, 'a'}, {1, 'c'}] 

You can notice that {3, 'a'} and {2, 'a'} were reordered.

Java 8 example (Java API has only stable sort for objects):

List<Point> list = Arrays.asList(new Point(1,1), new Point(1,0), new Point(2,1));
list.sort((a,b) -> Integer.compare(a.x,b.x));
System.out.println(list);
list.sort((a,b) -> Integer.compare(a.y,b.y));
System.out.println(list);
Community
  • 1
  • 1
sibnick
  • 3,995
  • 20
  • 20
  • @ZhekaKozlov this is an illustration of the problem! Use your imagination, assume a custom comparator is used - `Comparator.comparing(t -> t.get(0))` for example. – Boris the Spider Sep 22 '15 at 07:51
  • @ZhekaKozlov He didn't show any code. You sort using a `Comparator`, or by sorting a custom class that implements `Comparable`. – Andreas Sep 22 '15 at 07:52
  • @BoristheSpider Sorting list of Comparables and sorting by Comparator are totally different problems. The latter one does not need stability. If two objects are equal, they can be freely reordered. – ZhekaKozlov Sep 22 '15 at 07:54
  • 1
    @ZhekaKozlov not if the sort is **stable** they can't. What gave you the impression that there way any difference? Nothing _needs_ stability, it's a desirable property of a sorting algorithm. And you'll find that a `Comparator` is **always** used, just it's `Comparator.naturalOrder()` in the case where the elements are `Comparable`. – Boris the Spider Sep 22 '15 at 07:55
  • @ZhekaKozlov You are assuming that `compareTo()` is consistent with `equals()`. Javadoc says: "It is strongly recommended (though not required) that natural orderings be consistent with equals". – Andreas Sep 22 '15 at 07:57
  • @BoristheSpider Wrong. Comparator is `null` if natural ordering is used. – ZhekaKozlov Sep 22 '15 at 08:01
1

You may (and almost always do) have elements for which it is true that:

a.compareTo(b) == 0 and
a.equals(b) == false

Take, for example, a List<Product>, where product has a number of properties: - id - price

You could see several use cases where you would want to sort Product by id or price but not by other values.

The big benefit that stable sorting brings to the table is that if you sort by price then by id you will have a List that is correctly sorted by first price then by id.

If your sorting algorithm is unstable, then the second sort by id might be break the order of the initial sort by price.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • But in that case, all you have to do is to provide an appropriate `Comparator`, right? Why would you do a two-pass sorting? – fge Sep 22 '15 at 07:44
  • @fge you might want to find the greatest `price` then the greatest price for each `id`. The point is that a stable sort guarantees that two elements considered equal under one sort will not be swapped places. The is very important in algorithms such a Radix sort. – Boris the Spider Sep 22 '15 at 07:46