如何使用多个排序条件对ArrayList进行排序?

8

我有一个包含Quote对象的数组列表。我想能够按名称、变化和百分比变化字母顺序排序。如何对我的数组列表进行排序?

package org.stocktwits.model;

import java.io.Serializable;
import java.text.DecimalFormat;

    public class Quote implements Serializable {

        private static final long serialVersionUID = 1L;

        public String symbol;
        public String name;
        public String change;
        public String percentChange;
        public String open;
        public String daysHigh;
        public String daysLow;
        public String dividendYield;
        public String volume;
        public String averageDailyVolume;
        public String peRatio;
        public String marketCapitalization;
        public String yearHigh;
        public String yearLow;
        public String lastTradePriceOnly;
        public DecimalFormat df = new DecimalFormat("#,###,###,###,###,##0.00");
        public DecimalFormat vf = new DecimalFormat("#,###,###,###,###,##0");

        public String getSymbol() {
            return symbol;
        }
        public void setSymbol(String symbol) {
            this.symbol = symbol;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getChange() {
            return change;
        }
        public void setChange(String change) {
            if(change.equals("null")){
                this.change = "N/A";
            }
            else{   
                float floatedChange = Float.valueOf(change);
                this.change = (df.format(floatedChange));
            }
        }
        public String getPercentChange() {
            return percentChange;
        }
        public void setPercentChange(String percentChange) {
            if(percentChange.equals("null"))
                percentChange = "N/A";
            else
                this.percentChange = percentChange;
        }
        public String getOpen() {
            return open;
        }
        public void setOpen(String open) {
            if(open.equals("null"))
                this.open = "N/A";
            else
                this.open = open;
        }
        public String getDaysHigh() {
            return daysHigh;
        }
        public void setDaysHigh(String daysHigh) {
            if(daysHigh.equals("null"))
                this.daysHigh = "N/A";
            else{
                float floatedDaysHigh = Float.valueOf(daysHigh);
                this.daysHigh = (df.format(floatedDaysHigh));
            }
        }
        public String getDaysLow() {
            return daysLow;
        }
        public void setDaysLow(String daysLow) {
            if(daysLow.equals("null"))
                this.daysLow = "N/A";
            else{
                float floatedDaysLow = Float.valueOf(daysLow);
                this.daysLow = (df.format(floatedDaysLow));
            }
        }
        public String getVolume() {
            return volume;
        }
        public void setVolume(String volume) {
            if(volume.equals("null")){
                this.volume = "N/A";
            }
            else{
                float floatedVolume = Float.valueOf(volume);
                this.volume = (vf.format(floatedVolume));
            }
        }
        public String getDividendYield() {
            return dividendYield;
        }
        public void setDividendYield(String dividendYield) {
            if(dividendYield.equals("null"))
                this.dividendYield = "N/A";
            else
                this.dividendYield = dividendYield;
        }
        public String getAverageDailyVolume() {
            return averageDailyVolume;
        }
        public void setAverageDailyVolume(String averageDailyVolume) {
            if(averageDailyVolume.equals("null")){
                this.averageDailyVolume = "N/A";
            }
            else{
                float floatedAverageDailyVolume = Float.valueOf(averageDailyVolume);
                this.averageDailyVolume = (vf.format(floatedAverageDailyVolume));
            }
        }
        public String getPeRatio() {
            return peRatio;
        }
        public void setPeRatio(String peRatio) {
            if(peRatio.equals("null"))
                this.peRatio = "N/A";
                else
            this.peRatio = peRatio;
        }
        public String getMarketCapitalization() {
            return marketCapitalization;
        }
        public void setMarketCapitalization(String marketCapitalization) {
            if(marketCapitalization.equals("null"))
                this.marketCapitalization = "N/A";
            else
                this.marketCapitalization = marketCapitalization;
        }
        public String getYearHigh() {
            return yearHigh;
        }
        public void setYearHigh(String yearHigh) {
            if(yearHigh.equals("null"))
                this.yearHigh = "N/A";
            else
                this.yearHigh = yearHigh;
        }
        public String getYearLow() {
            return yearLow;
        }
        public void setYearLow(String yearLow) {
            if(yearLow.equals("null"))
                this.yearLow = "N/A";
            else
                this.yearLow = yearLow;
        }

        public String getLastTradePriceOnly() {
            return lastTradePriceOnly;
        }

        public void setLastTradePriceOnly(String lastTradePriceOnly) {
            if(lastTradePriceOnly.equals("null")){
                this.lastTradePriceOnly = "N/A";
            }
            else{
                float floatedLastTradePriceOnly = Float.valueOf(lastTradePriceOnly);
                this.lastTradePriceOnly = (df.format(floatedLastTradePriceOnly));
            }
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((change == null) ? 0 : change.hashCode());
            result = prime * result
                    + ((daysHigh == null) ? 0 : daysHigh.hashCode());
            result = prime * result + ((daysLow == null) ? 0 : daysLow.hashCode());
            result = prime
                    * result
                    + ((lastTradePriceOnly == null) ? 0 : lastTradePriceOnly
                            .hashCode());
            result = prime
                    * result
                    + ((marketCapitalization == null) ? 0 : marketCapitalization
                            .hashCode());
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            result = prime * result + ((open == null) ? 0 : open.hashCode());
            result = prime * result + ((peRatio == null) ? 0 : peRatio.hashCode());
            result = prime * result
                    + ((percentChange == null) ? 0 : percentChange.hashCode());
            result = prime * result + ((symbol == null) ? 0 : symbol.hashCode());
            result = prime * result + ((volume == null) ? 0 : volume.hashCode());
            result = prime * result
                    + ((yearHigh == null) ? 0 : yearHigh.hashCode());
            result = prime * result + ((yearLow == null) ? 0 : yearLow.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Quote other = (Quote) obj;
            if (change == null) {
                if (other.change != null)
                    return false;
            } else if (!change.equals(other.change))
                return false;
            if (daysHigh == null) {
                if (other.daysHigh != null)
                    return false;
            } else if (!daysHigh.equals(other.daysHigh))
                return false;
            if (daysLow == null) {
                if (other.daysLow != null)
                    return false;
            } else if (!daysLow.equals(other.daysLow))
                return false;
            if (lastTradePriceOnly == null) {
                if (other.lastTradePriceOnly != null)
                    return false;
            } else if (!lastTradePriceOnly.equals(other.lastTradePriceOnly))
                return false;
            if (marketCapitalization == null) {
                if (other.marketCapitalization != null)
                    return false;
            } else if (!marketCapitalization.equals(other.marketCapitalization))
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            if (open == null) {
                if (other.open != null)
                    return false;
            } else if (!open.equals(other.open))
                return false;
            if (peRatio == null) {
                if (other.peRatio != null)
                    return false;
            } else if (!peRatio.equals(other.peRatio))
                return false;
            if (percentChange == null) {
                if (other.percentChange != null)
                    return false;
            } else if (!percentChange.equals(other.percentChange))
                return false;
            if (symbol == null) {
                if (other.symbol != null)
                    return false;
            } else if (!symbol.equals(other.symbol))
                return false;
            if (volume == null) {
                if (other.volume != null)
                    return false;
            } else if (!volume.equals(other.volume))
                return false;
            if (yearHigh == null) {
                if (other.yearHigh != null)
                    return false;
            } else if (!yearHigh.equals(other.yearHigh))
                return false;
            if (yearLow == null) {
                if (other.yearLow != null)
                    return false;
            } else if (!yearLow.equals(other.yearLow))
                return false;
            return true;
        }
    }

我真的很讨厌这样的类。类需要有真正的业务逻辑才能值得一试——我更喜欢在这种情况下使用哈希等东西。长远来看,把一个类当作属性的袋子只会变得烦人。重复性应该表明它显然是错误的。有些人认为这是 Java 的过错,但我倾向于认为这更多是程序员不愿意尝试新事物的过错。我已经以允许验证、类型安全和许多其他技巧的方式完成了这项工作。没有样板代码,但这需要大量的工作——虽然值得,但样板代码确实很糟糕。 - Bill K
6
我不确定我完全同意,但有一件事引起了我的注意:这里有很多设置器和获取器方法,然而它们所设置/获取的字段都是公共的!这些字段应该是私有的。 - Jason S
7个回答

32

如果你(几乎)总是想要使用该顺序,你可以向Quote类添加Comparable接口并实现compareTo方法。

 public int compareTo(Quote quote) {
     int result = this.getName().compareTo(quote.getName());
     if (result == 0) {
        result = this.getChange().compareTo(quote.getChange());
     }
     if (result == 0) {
        result = this.getPercentChange().compareTo(quote.getPercentChange());
     }
     return result;
 }

使用排序集合或对列表进行排序,引号将会被排序。

对于临时排序,最好使用单独的、可能是匿名的Comparator。


1
对于临时排序,最好使用单独的、可能是匿名的比较器。 - user3437460

14

大家都正确地认为您想使用比较器。扩展这个想法,如果您想要能够按多个标准进行排序,那么像这样的类就适合您:

public class MultiComparator<T> implements Comparator<T> {
    private List<Comparator<T>> comparators;

    public MultiComparator(List<Comparator<T>> comparators) {
        this.comparators = comparators;
    }

    public int compare(T o1, T o2) {
        for (Comparator<T> comparator : comparators) {
            int comparison = comparator.compare(o1, o2);
            if (comparison != 0) return comparison;
        }
        return 0;
    }
}

接下来,您只需要为您希望比较的任何字段编写非常简单的比较器,您可以更轻松地将它们组合成更复杂的比较器,并实现更多的重用。


如果需要进行大量比较且性能是一个问题,那么私有的 List<Comparator<T>> 可以作为一个数组 Comparator[] 进行存储(它在构造函数中被设置一次,从未更改,并且从未被客户端读取)——至少将其设置为 final。 - Jason S
同意,或者这个类可以是final的。我只是为了演示这个想法而写的。JDK中应该有类似的东西,如果在任何一个集合库中没有它,我会感到惊讶的... - romacafe
这种比较器的链接会导致什么样的复杂性呢?我们在每次链接比较器时实际上是在进行排序吗?因此,每个比较器都需要进行一次n*log(n)的操作吗? - John Baum
只要比较器确定了差异,就会立即返回结果,因此这取决于比较器的内容。主要的复杂性是由排序算法驱动的,而不是比较器。假设排序良好且只有一个比较器,则复杂度应该在nlog(n)左右。如果第一个比较器可能区分两个对象(例如person.birthdate()),则总体复杂度应保持在该水平附近。如果第一个比较器不太可能区分对象(例如person.sex()),则趋势将更接近于2nlog(n)。 - romacafe
1
另请参见:https://dev59.com/GV3Va4cB1Zd3GeqPAnfj#37586965 - Christophe Roussy

13

建议查看Apache Commons Collection的ComparatorChain。这个库已经被测试过且可用,不要自己重复实现。
我在下面的网址提供了一个教程:按多个属性排序对象


+1 谢谢您提供这个信息。在我看来,不得不使用第三方库来实现这一点是 Java 编程语言的一个巨大缺陷。我同意 strangeoptics 的观点,在有库可以完成任务时,不要自己重新实现。这是一个无需依赖的 ComparatorChain 类的直接链接:http://www.jarvana.com/jarvana/view/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1-sources.jar!/org/apache/commons/collections/comparators/ComparatorChain.java - Moritz
感谢您提供示例代码来演示概念。这与您的文章结合起来,是对OP问题的一个很好的回答。 - John Ward

9
创建一个合适的比较器(Comparator),根据您想要的标准比较两个项目。然后在您的ArrayList上使用Collections.sort()
如果以后想按不同的标准排序,请再次使用不同的Comparator调用Collections.sort()

2
你能提供一个compare()方法的例子吗? - Sheehan Alam
@Sheehan,文档解释了合同。由您确定顺序。就像在图书馆查找书籍一样,首先进入“小说”或“非小说”部分,然后查找整个编号,再查找小数点后面的部分……例如,您首先比较“更重要”的事情并不断缩小范围。如果一个更重要的部分大于另一个部分,则终止排序(因为您已经找到了更好的顺序)。 - user166390
这个答案对我没用,我有两列数据,首先我想按照第一列来排序arraylist,之后我想按照第二列来排序,但是它并没有正常工作,我的arraylist被完全按照第二列排序了! - Muhammad Ali
这里有一个很好的例子:https://www.callicoder.com/java-comparable-comparator/ - JGFMK

5

2
请参考Collections.sort中的显式比较器(或者如果您愿意的话,还可以使用需要实现Comparable的输入类型的Collections.sort)。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接