8

I have an array list that contains Quote objects. I want to be able to sort alphabetically by name, by change, and by percent change. How can I sort my arraylist?

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;
        }
    }
Sheehan Alam
  • 60,111
  • 124
  • 355
  • 556
  • I really hate classes like this. Classes need to have real business logic to be worth while--I prefer to use something along the lines of hashes for stuff like this. In the long run using a class as a bag of attributes just gets annoying. The repetitiveness should show that it's obviously wrong. Some people take this as a fault of Java, I tend to think it's more a fault of programmers not being willing to branch out. I've done this in ways that allowed validation, type safety and lots of other tricks. No boilerplate code but it was lots of work--well worth it though--boilerplate sucks. – Bill K Sep 13 '10 at 22:25
  • 6
    not sure I agree completely but one thing stands out to me: there's all these setter and getter methods, yet the fields they get/set are all public! Those should be private. – Jason S Sep 13 '10 at 22:44

7 Answers7

32

If you (almost) always want to use that order you can add the Comparable interface to Quote and implement a compareTo method.

 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;
 }

Then use a sorted collection, or sort a list, and the quotes will be sorted.

For ad hoc sorting, a separate, possibly anonymous, Comparator is better.

Peter Tillemans
  • 34,983
  • 11
  • 83
  • 114
14

Everybody is right that you want to use Comparators. Extending on that idea, if you want to be able to sort on multiple criteria, then a class like this will work for you:

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;
    }
}

Then you just write really simple comparators for whichever fields you desire and you can combine them into more complex comparators more easily and with more reuse.

romacafe
  • 3,098
  • 2
  • 23
  • 27
  • If there are a lot of comparisons to be made and performance is an issue, the private List> could be stored as an array Comparator[] (it is set once at the constructor, never altered, and never read by clients) -- at the very least make it final. – Jason S Sep 13 '10 at 22:46
  • Agreed, or maybe the class could be final. I pretty much just wrote that up cold to demonstrate the idea. There really should be something of the sort in the JDK, and I'd be surprised if it wasn't in any number of collections libraries... – romacafe Sep 13 '10 at 23:01
  • What would be the complexity of this sort of chaining of comparators? Are we essentially sorting each time we chain the comparators? So we do a n*log(n) operation for each comparator? – John Baum Nov 10 '15 at 16:53
  • This returns as soon as a comparator determines a difference, so it really depends on what the comparators are. The main complexity is driven by the sort algorithm, not the comparator. Assuming a good sort and a "single" comparator, complexity should be around n\*log(n). If the first comparator is likely to differentiate two objects (for example, `person.birthdate()`, then overall complexity should remain near that level. If the first comparator is unlikely to differentiate objects(for example `person.sex()`), then you'll trend closer to 2n\*log(n). – romacafe Nov 13 '15 at 22:53
  • 1
    Also see: http://stackoverflow.com/questions/8212339/how-to-perform-a-series-of-sort-operation-on-arraylist/37586965#37586965 – Christophe Roussy Jun 02 '16 at 09:18
13

Have a look at the ComparatorChain from the Apache Commons Collection. This should do the job. Don't implement logic if is already available and tested.
At the following site I have a tutorial: Sorting Objects By Multiple Attributes"

strangeoptics
  • 753
  • 8
  • 17
  • +1 Thank you for this. Having to resort to a third party library for this is IMHO a huge shortcoming of the Java programming language. I agree with strangeoptics, don't roll out your own implementation when there is a library that can do it. Direct link to the dependency free ComparatorChain class: 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 Jan 10 '13 at 13:21
  • Thank you for offering example code to demonstrate the concept. This (combined with your article) is an excellent answer to the OP question. – John Ward Jan 30 '17 at 17:05
9

Create an appropiate Comparator that will compare two items according to your desired criteria. Then use Collections.sort() on your ArrayList.

If at a later time you want to sort by different criteria, call Collections.sort() again with a different Comparator.

gpeche
  • 21,974
  • 5
  • 38
  • 51
  • 2
    Can you provide an example of what my compare() method may look like? – Sheehan Alam Sep 13 '10 at 22:37
  • @Sheehan The documentation explains the contract. It's up to you to determine the ordering. Think of it just like looking up a book in the library, e.g. first you go to the "Fiction" or "Non-Fiction" section, then you look up the whole number, then the part after the decimal... e.g. you compare the "more significant" things first and keep narrowing down. If one more-significant part is more than another, then that terminates the ordering (as you've found the better order already). –  Sep 13 '10 at 23:01
  • This answer not work for me, i have two columns, first i would like sort arraylist based on columns one after it i would like sort on columns two, but it is not work properly and my arraylist sorted on columns two totaly! – Muhammad Ali Dec 13 '16 at 11:57
  • There is a good example here https://www.callicoder.com/java-comparable-comparator/ – JGFMK Nov 10 '19 at 16:32
5

Sun has devoted big part of its tutorial to sorting in Java collections:
http://download.oracle.com/javase/tutorial/collections/interfaces/order.html

It discusses both Comparable and Comparator interfaces with examples.

Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
2

See Collections.sort with an explicit Comparator (or the Collections.sort kind that requires the input to implement Comparable, if you prefer).

0

There are two things:

  1. Sorting on multiple fields of an object
  2. Multilevel sorting (Here sorting done on first field and then next sorting applied on grouping done on similar items in previous sort)

For #2: I found this below article very much close to what i desire http://strangeoptics.blogspot.com/2011/09/sorting-objects-by-multiple-attributes.html

sagar borse
  • 155
  • 1
  • 2
  • 11