如何使用两种排序对ArrayList进行排序



我有一个有性别的名字列表,以及有多少人有这个名字。

例如:

John M 600

Mike M 200

Sarah F 700

Tom M 400

Emily F 600

Chris M 600

我试图按照计数按降序对这些名字进行排序,如果它们的计数相同,我希望它们按ABC顺序排序。我单独制作的函数按ABC顺序打印计数,然后按降序打印另一个计数。

示例:发生了什么

Chris M 600

Emily F 600

Mike M 200

John M 600

Sarah F 700

Tom M 400

Sarah F 700

John M 600

Emily F 600

Chris M 600

Tom M 400

Mike M 200

我想要得到什么

Sarah F 700

Chris M 600

Emily F 600

John M 600

Tom M 400

Mike M 200

ArrayList<OneName> oneName = new ArrayList<OneName>();
while(sc.hasNextLine())
    {
    // read a line from the input file via sc into line
        line = sc.nextLine();

        StringTokenizer stk = new StringTokenizer(line, ",");
        String name = stk.nextToken();
        char sex = stk.nextToken().charAt(0);
        int count = Integer.parseInt(stk.nextToken());

        OneName list = new OneName(name, sex, count);
        oneName.add(list);      

    }    Collections.sort(oneName, new OneNameCompare());
    for(OneName a: oneName)
    {
        System.out.println(a.toString());
    }
     Collections.sort(oneName, new OneNameCountCompare());
     for(OneName b: oneName)
    {
        System.out.println(b.toString());

     }

OneNameCompare.java

import java.util.Comparator;
import java.util.Collections;
public class OneNameCompare implements Comparator<OneName>
{
    public int compare(OneName a1, OneName a2)
    {
         return a1.getName().compareTo(a2.getName());
    }

 }

OneNameCountCompare.java

import java.util.Comparator;
import java.util.Collections;
public class OneNameCountCompare implements Comparator<OneName>
{
    {
     if(b1.getCount() < b2.getCount())
     {
        return 1;
     }
 else
 {
    return -1; 
 }
}
}

Java 8解决方案

您可以通过Java 8中Comparator中引入的thenComparing(Comparator<? super T> other)方法来组合两个比较器。

所以不是

Collections.sort(oneName, new OneNameCompare());
for (OneName a : oneName) {
    System.out.println(a.toString());
}
Collections.sort(oneName, new OneNameCountCompare());
for (OneName b : oneName) {
    System.out.println(b.toString());
}

使用

Collections.sort(oneName, new OneNameCountCompare()
                              .thenComparing(new OneNameCompare()));
for (OneName b : oneName) {
    System.out.println(b.toString());
}

顺便说一句,你似乎可以";"简化";您的代码使用称为方法引用的Java 8新特性来创建所需的比较器。

import static java.util.Comparator.comparing;
//...
Comparator<OneName> reversedCountComparator = comparing(OneName::getCount).reversed();
Comparator<OneName> nameComparator = comparing(OneName::getName);
oneName.sort(reversedCountComparator.thenComparing(nameComparator));
oneName.forEach(System.out::println);

Java 7解决方案

由于Collections.sort(collection, comparator)只接受一个比较器,所以您需要创建一个类来实现comparator,但将在内部使用其他比较器。此类代码可能看起来像

class ComparatorsMixer<T> implements Comparator<T> {
    List<Comparator<T>> comparatorsList;
    public ComparatorsMixer(List<Comparator<T>> list) {
        this.comparatorsList = list;
    }
    public ComparatorsMixer() {
        comparatorsList = new ArrayList<>();
    }
    public void addComparator(Comparator<T> comparator){
        comparatorsList.add(comparator);
    }
    
    public int compare(T o1, T o2) {
        for (Comparator<T> c : comparatorsList) {
            int result = c.compare(o1, o2);
            if (result != 0)
                return result;
            //if result == 0 then move on to next comparator
        }
        return 0;
    }
}

你可以像一样使用它

ComparatorsMixer<OneName> mix = new ComparatorsMixer<>();
mix.addComparator(new OneNameCountCompare());
mix.addComparator(new OneNameCompare());
Collections.sort(oneName, mix);

只需在第二次比较后打印结果。Collections.sort使用"稳定排序"算法,这意味着对于第二次排序中的相等项,顺序与第一次排序后的顺序保持不变,这正是您想要的,不是吗?

请参阅http://en.wikipedia.org/wiki/Sorting_algorithm#Stability

当然,更好的解决方案是组合比较器,因此您只需要对项目进行一次遍历。

最新更新