如何使用多个排序标准对 ArrayList 进行排序?

Posted

技术标签:

【中文标题】如何使用多个排序标准对 ArrayList 进行排序?【英文标题】:How to sort an ArrayList using multiple sorting criteria? 【发布时间】:2011-04-11 21:55:08 【问题描述】:

我有一个包含 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的错误,我倾向于认为这更多是程序员不愿意分支的错误。我以允许验证、类型安全和许多其他技巧的方式做到了这一点。没有样板代码,但工作量很大——尽管很值得——样板代码很烂。 不确定我是否完全同意,但有一件事让我印象深刻:有所有这些 setter 和 getter 方法,但它们获取/设置的字段都是公开的!这些应该是私人的。 【参考方案1】:

如果您(几乎)总是想使用该订单,您可以将 Comparable 接口添加到 Quote 并实现 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【参考方案2】:

你想使用比较器是对的。扩展这个想法,如果您希望能够根据多个标准进行排序,那么这样的类将适合您:

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[] (在构造函数中设置一次,从不改变,从不被客户阅读)——至少让它成为最终版本。 同意,或者该课程可能是最终课程。我几乎只是把它写下来来证明这个想法。 JDK 中确实应该有类似的东西,如果它不在任何数量的集合库中,我会感到惊讶...... 这种比较器链接的复杂性是多少?每次链接比较器时,我们本质上是在排序吗?那么我们对每个比较器进行 n*log(n) 操作? 这会在比较器确定差异后立即返回,因此它实际上取决于比较器是什么。主要的复杂性是由排序算法驱动的,而不是比较器。假设一个好的排序和一个“单个”比较器,复杂度应该在 n*log(n) 左右。如果第一个比较器可能区分两个对象(例如,person.birthdate(),那么整体复杂性应该保持在该水平附近。如果第一个比较器不太可能区分对象(例如person.sex()),那么您将趋近到 2n*log(n)。 另见:***.com/questions/8212339/…【参考方案3】:

查看 Apache Commons Collection 中的 ComparatorChain。这应该可以完成这项工作。如果已经可用并经过测试,则不要实现逻辑。 在以下站点我有一个教程:Sorting Objects By Multiple Attributes"

【讨论】:

+1 谢谢。恕我直言,不得不求助于第三方库是 Java 编程语言的一个巨大缺点。我同意奇怪的光学,当有一个库可以做到时,不要推出你自己的实现。直接链接到无依赖的 ComparatorChain 类:jarvana.com/jarvana/view/commons-collections/… 感谢您提供示例代码来演示该概念。这(结合你的文章)是对 OP 问题的一个很好的回答。【参考方案4】:

创建一个合适的Comparator,它将根据您所需的标准比较两个项目。然后在 ArrayList 上使用 Collections.sort()

如果以后您想按不同的标准进行排序,请使用不同的Comparator 再次调用Collections.sort()

【讨论】:

你能举例说明我的 compare() 方法可能是什么样子吗? @Sheehan 文档解释了合同。由您决定顺序。可以把它想象成在图书馆里查一本书,例如首先进入“小说”或“非小说”部分,然后查找整数,然后查找小数点后的部分……例如您首先比较“更重要”的事物并不断缩小范围。如果一个更重要的部分多于另一个,则终止排序(因为您已经找到了更好的顺序)。 这个答案对我不起作用,我有两列,首先我想根据第一列对数组列表进行排序,然后我想对第二列进行排序,但它不能正常工作并且我的数组列表已排序总共在第二列! 这里有一个很好的例子callicoder.com/java-comparable-comparator【参考方案5】:

Sun 将大部分教程用于 Java 集合中的排序:http://download.oracle.com/javase/tutorial/collections/interfaces/order.html

它通过示例讨论了 Comparable 和 Comparator 接口。

【讨论】:

【参考方案6】:

使用显式比较器查看Collections.sort(如果您愿意,也可以使用需要输入来实现Comparable 的Collections.sort 类型)。

【讨论】:

【参考方案7】:

有两件事:

    按对象的多个字段排序 多级排序(此处对第一个字段进行排序,然后将下一个排序应用于对先前排序中类似项目进行的分组)

对于#2:我发现下面的这篇文章非常接近我想要的 http://strangeoptics.blogspot.com/2011/09/sorting-objects-by-multiple-attributes.html

【讨论】:

以上是关于如何使用多个排序标准对 ArrayList 进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

Java中如何对集合排序

按对象属性对对象的 ArrayList 进行排序

Java:就地对 ArrayList 进行排序

如何使用Comparable Interface比较ArrayList中对象的多个属性? [重复]

如何利用php数组对百万数据进行排重

如何按字典顺序对 ArrayList 进行排序?