简体   繁体   中英

Java: Sorting Elements

I know we normally use java.util.Arrays.sort(Object[], Comparator) . The JavaDoc says it is a modified mergesort.

But I need a sorting algorithm that compares every object with every object. I will have a lot of elements of wich the order doesn't matter. But there are some elements that really need to come after a specific other element (not necessary consecutive). And I think (but don't know) that the mergesort is not enough...

Perhaps, what I want to achieve is not even called sorting ?

Do I need to implement such a system my own, or does there exist something for this?


Example:

Obj1, Obj2, Obj3, Obj4

The order of following couples doesn't matter (which mean my Comparator should return 0):

Obj1, Obj2 (*)
Obj1, Obj3
Obj2, Obj3
Obj2, Obj4 (*)
Obj3, Obj4

But! It is really necessary that Obj4 is followed by Obj1 .

Because of the two (*) couples, this Mathematically means that Obj1 == Obj4.

Will it work using mergesort?

Thanks

If you know your ideal ordering, one option is to add some sortable value like an integer that represents relationships between the data. For instance, if item A has to come before item B, then make sure its sorting value is less than item B's value. Then you can provide a custom comparator that only looks at the sort values and you can use a standard sorting algorithm.

It sounds like you have a set of DAGs (directed acyclic graphs). I think you'll need to model this and then do a topological sort on each one. One approach:

Wrap each element in a wrapper object that references the object and has a list for holding dependencies to other objects. Put all these wrappers in a hashMap keyed by object id.

For all elements with no direct dependencies, emit the element, and remove it from the hashMap. Repeat until hashmap is empty.

If dependency lists are often long, this will be inefficient. It's intended for an average number of direct dependencies under 5 or so. If performance is a problem because too many "Repeat until hashmap is empty" passes are being made, a bidirectional data structure for representing the dependency graphs should be used, or maybe, a list of the map entries that have only one dependency on this pass, and thus are strong candidates for the next pass.

Here's an untested sketch:

class Obj           { String id;    }

class ObjWrapper    {
    String id;
    Obj obj;
    String[] depends; // may be null, may have null entries

    public ObjWrapper(Obj obj, String[] depends) {
        this.id = obj.id;
        this.obj = obj;
        if ( depends != null )
            this.depends = Arrays.copyOf(depends, depends.length);
    }
}

// order the objects by dependency. 
ArrayList<Obj> orderObjs(Iterable<ObjWrapper> objs)
{
    ArrayList<Obj> output = new ArrayList();
    HashMap<String, ObjWrapper> objMap = new HashMap();
    for ( ObjWrapper obj : objs )  
        objMap.put(obj.id, obj);  
    while ( !objMap.isEmpty() ) {
        Iterator<ObjWrapper> iter = objMap.values().iterator();
        while ( iter.hasNext() ) {
            ObjWrapper objWrapper = iter.next();
            if ( !hasDependencies(objWrapper, objMap) ) {
                output.add(objWrapper.obj);
                // must use iter to remove from hash, or bad things happen.
                iter.remove();  
            }
        }
    }
    return output;
}

boolean hasDependencies(ObjWrapper objWrapper, 
                        HashMap<String, ObjWrapper> objMap)
{
    if ( objWrapper.depends == null ) return false;
    for ( String depend : objWrapper.depends )   {
        if ( depend != null ) 
            if ( objMap.containsKey(depend) )
                return true;
            else
                depend = null;  // to speed up future passes
    }
    return false;
}

I find your requirements a little unclear. However, from what I gather you should be able to achieve this by providing an appropriate comparator.

edit Now that you've added an example, I don't think you even need a sort. Let's say elements X, Y and Z have to come after some element A. All you have to do is shift X, Y and Z to the end of your list. This can be done in O(n) time as opposed to the sort's O(n logn) .

Alternatively, you can move A to the start of your list.

If you have an order between the elements (ie you can say this one is smaller than this one), then the merge sort (as well as any sorting algo) will actually sort the collection.

Try to express formally (mathematically) the final ordering you need.

The fact that the order of two elements does not matter does not mean that your comparator must return 0. It must return 0 iif they are equal.

You have implement comparator (and pass it to standard mergesort), something like this:

int compare(Object o1, Object o2) {
  if (o1 instanceOf NotMatter) {
     if (o2 instanceOf NotMatter) {
        return 0;
     }
     return -1;
  }
  if (o2 instanceOf NotMatter) {
     return 1;
  }

  // ok, now we have two important objects
  if (o1.better(o2) {
     return 1;
  }
  if (o2.better(o1) {
     return -1;
  }
  return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM