Sort ArrayList of ArrayList of objects - java

I'm having trouble using a Comparator to sort an ArrayList of ArrayList of Objects, i have this simple code:
ArrayList<ArrayList<Object>> office = new ArrayList<ArrayList<Object>>(15);
ArrayList<Object> objectArrayList1 = new ArrayList<Object>(15);
objectArrayList1.add("Denver");
objectArrayList1.add(108);
office.add(objectArrayList1);
ArrayList<Object> objectArrayList2 = new ArrayList<Object>(15);
objectArrayList2.add("NewYork");
objectArrayList2.add(109);
office.add(objectArrayList2);
[[Denver, 118], [NewYork, 109]]
How do i sort it by the numbers or the String?

You're going to have to override the compare() method using Collections.sort() and create a new Comparator. Because you are using object it's going to be difficult to write a generic sorter. Here is an example for a sort on an arrayList of a custom object
Collections.sort(finalList, new Comparator<FinalAccount>()
#Override
public int compare(FinalAccount o1, FinalAccount o2) {
Double o1Double = o1.reductionAmount;
Double o2Double = o2.reductionAmount;
return o1Double.compareTo(o2Double);
}
});
but again this is for only one type of object, generics confuse things because you may have any type of object.

Your data model is a little off.
Assuming you want to store data for cities and their associated "scores" to sort on, you need a list of objects that can contain both the city and its score. This entity has to be comparable.
For e.g
Team implements Comparable {
String city;
int score;
Team(String city, int score) {
this.city = city;
this.score = score;
}
#Override
public int compareTo(Team other) {
return this.score - other.score;
}
}
Now you do can sort an ArrayList of Teams
List<Team> teams = Arrays.asList(new Team("New York", 101), new Team("Denver", 108));
Collections.sort(teams); // sorts in-place
You can also use custom comparators like
Collections.sort(teams, new Comparator<Team>(){
#Override
public int compare(Team t1, Team t2) {
return t1.city.compareTo(t2.city);
}
})

Related

Can Collections.sort() take treeSet as first argument? If not why?

I have my 'Person' class as follows -
public class Person implements Comparable<Person> {
private int marks;
private String name;
Person(int marks, String name) {
this.marks = marks;
this.name = name;
}
public int getMarks() {
return marks;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "Person: Marks = " + marks + ", Name = " + name;
}
#Override
public int compareTo(Person person) {
return marks > person.marks ? 1 : -1;
}
}
Now in main method I have created another comparator with opposite sorting -
Comparator<Person> comparator = new Comparator<Person>() {
#Override
public int compare(Person p1, Person p2) {
int marks1 = p1.getMarks();
int marks2 = p2.getMarks();
return marks1 > marks2 ? -1 : 1;
}
};
Now, I create a TreeSet -
TreeSet<Person> treeSet1 = new TreeSet<>(List.of(
new Person(67, "Himani"),
new Person(73, "Hasani"),
new Person(21, "Rohini")
));
Now, I try passing treeSet as an argument to Collections.sort() -
Collections.sort(treeSet1, comparator);
Here, I get the following error -
Required type Provided
list:List<T> TreeSet<Person>
c:Comparator<? super T> Comparator<Person>
As far as I can deduce from the error - Collections.sort() with comparator can only be used on List and not Set.
Am I correct? If not where am I going wrong? If yes why am I correct?
The first argument to Collections.sort is of type List<T>. So you can only pass List objects to it, not Set objects, or anything else.
This is sensible. The reason is that Set objects are of two types.
There are those that are, by their nature, always unsorted, such as HashSet. It doesn't make sense to sort these.
There are those that are, by their nature, always sorted the same way, such as TreeSet. It doesn't make sense to sort these, because they're already sorted.
I think maybe what you want is to take your TreeSet and have it sorted using a different Comparator. To do that, you'll have to make a new TreeSet with your new Comparator, and copy the entries across from the old set using addAll.

java sort list of objects by specifiable attribute

I want to sort a List of objects by a specified attribute of those objects and I want to choose which attribute should be used for sorting. Example:
class Car{
private String name;
private String colour;
public enum sortBy {NAME, COLOUR};
public String name(){
return name;
}
public String colour(){
return colour;
}
public static Car[] getSortedArray(Car[] carArray, sortBy sortType){
HashMap<Object, Car> carMap = new HashMap<Object, Car>();
Object[] sortArray = new Object[carArray.length];
Object value = null;
for(int i = 0; i < carArray.length; i++){
if(sortType == sortBy.NAME){
value = carArray[i].name();
}else if(sortType == sortBy.COLOUR){
value = carArray[i].colour();
}
carMap.put(value, carArray[i]);
sortArray[i] = value;
}
Arrays.sort(sortArray);
Car[] sortedArray = new Car[sortArray.length];
for(int i = 0; i < sortArray.length; i++){
sortedArray[i] = carMap.get(sortArray[i]);
}
return sortedArray;
}
}
//external:
Car[] cars = getSomeCars();
Car[] nameSortedCars = Car.getSortedArray(cars, Car.sortBy.NAME);
Car[] colourSortedCars = Car.getSortedArray(cars, Car.sortBy.COLOUR);
The idea is simple:
I put all values that i want to sort by into an array, and i create a map that maps these values back to their objects. After I sorted this array I take the objects mapped to these values and put them in the same order into a new array which is then sorted by these values. The values are just created with type Object so I can sort by multiple types (not just Strings as in the example).
This works fine unless you have two objects with the same attribute value, then only one object will be in the returned array, but two times.
Is there a better way to achieve this sorting?
It would be much simpler to use custom comparators:
To sort by name:
Arrays.sort(carArray, Comparator.comparing(Car::name));
To sort by colour:
Arrays.sort(carArray, Comparator.comparing(Car::colour));
So you could modify getSortedArray():
public static Car[] getSortedArray(Car[] carArray, Comparator<Car> comparator) {
Car[] sorted = carArray.clone()
Arrays.sort(sorted, comparator);
return sorted;
}
And call it like this:
Car[] sorted = getSortedArray(carArray, Comparator.comparing(Car::name));
Edit:
If you use a language version that does not support these features, you can create the comparators by explicitly creating a nested class that implements the Comparator interface.
This, for example, is a singleton Comparator that compares Car instances by name:
static enum ByName implements Comparator<Car> {
INSTANCE;
#Override
public int compare(Car c1, Car c2) {
return c1.name().compareTo(c2.name());
}
}
Then call:
Car[] sorted = getSortedArray(carArray, ByName.INSTANCE);
TL;DR: There's already a wheel for that.
I would say the easiest way to do this is to create a comparator:
final Comparator<Car> byName = Comparator.comparing(Car::name);
final Comparator<Car> byColour = Comparator.comparing(Car::colour);
Then just use the appropriate method on Arrays to sort by a comparator:
Arrays.sort(carArray, byName);
Now you want to do it with an enum? Just have the enum implements Comparator<Car>:
enum SortBy implements Comparator<Car> {
NAME(Comparator.comparing(Car::name)),
COLOUR(Comparator.comparing(Car::colour));
private final Comparator<Car> delegate;
private SortBy(Comparator<Car> delegate) {
this.delegate = delegate;
}
#Override
public int compare(final Car o1, final Car o2) {
return delegate.compare(o1, o2);
}
}
Want to sort by name then by colour? Easy:
final Comparator<Car> byName = SortBy.NAME.thenComparing(SortBy.COLOUR);
Want to sort by name in reverse order? Easy:
final Comparator<Car> byName = SortBy.NAME.reversed();
You're reinventing the wheel! Life will be much easier for you if you use the templated Collections API. To do this, you would work with List instead of arrays, define a Comparator to do your sorting, and then let the API do the work for you.
Comparator<Car> carComparator = new Comparator<Car>(){
public int sort(Car car1, Car car2){
//Sorting logic goes here.
}
}
List<Car> cars = getCars();
cars = Collections.sort(cars, carComparator); //the cars collection is now sorted.
If you wanted to sometimes sort by one attribute or another, you could make my variable carComparator into its own class and define which attributes to sort by in the constructor.
Hope that helps :)
Edit: As others have pointed out, this approach also works with arrays. But unless you have a good reason to be working with Arrays, working with Collections will generally be easier.
I think the solution would be more efficient if you passed a Comparator implementation to the Arrays.sort. Right now, you are looping n*2 from the looks of it, the hash map (O(1)) plus the Arrays.sort (which is another 0(n log n) or such). If you do the below, you could skip the 2 loops, and the map, you are using currently.
You can simply create a Comparator like (rough code):
class CarComparator implements Comparator<Car> {
enum compareType; //plus setter
public int compareTo(Car a, Car b) {
if(compareType == COLOUR) return a.colour.compareTo(b.colour);
if(compareType == NAME.....
}
}
, and then simply send the array of Cars to
Arrays.sort(cars, new CarComparator(COLOUR))
, or use more specialised comparator classes, one for each attribute, and a factory to render them, and of course don't create a new Comparator() for each sort if this is happening often. :-)
Overall, this approach should make your code more efficient.
}

How to fill arrays with object parameter based of a value?

If I have a certain number of objects which each take multiple parameters, how can I fill an array with one particular parameter for all objects, but have the order of the elements in the array based off another parameter. For example, I have this code:
public CollegeList(double gpa, int act, int sat, String name, String location){
this.gpa = gpa;
this.act = act;
this.sat = sat;
this.name = name;
this.location = location;
if(act/36.0>sat/2400.0){
this.score = 0.6*gpa*25.0+0.4*(act/36.0)*100.0;
}else{
this.score = 0.6*gpa*25.0+0.4*(sat/2400.0)*100.0;
}
this.scoreDistance = Math.abs(this.score-MainActivity.scoreDouble)/MainActivity.scoreDouble;
}
public double getGpa(){
return this.gpa;
}
public int getAct(){
return this.act;
}
public int getSat(){
return this.sat;
}
public String getName(){
return this.name;
}
public String getLocation(){
return this.location;
}
public double getScore(){
return this.score;
}
public double getScoreDistance(){
return this.scoreDistance;
}
Here, I would like the name parameter for all objects that I may create to populate a String array, but have those names go in ascending order by the double scoreDistance in the array. I'm sorry if the wording of this question is bad, but I hope it makes sense.
1) Create a CollegeList[] or ArrayList<CollegeList> containing the objects you want to sort.
2) Create a Comparator<CollegeList> that compares two CollegeList objects by comparing the scoreDistance. In Java 8 (yes, I know this isn't available for Android, but other readers may find this useful):
Comparator<CollegeList> compareByScoreDistance = (CollegeList a, CollegeList b) -> Double.compare(a.getScoreDistance(), b.getScoreDistance());
In Java 7:
Comparator<CollegeList> compareByScoreDistance = new Comparator<CollegeList>() {
#Override
public int compare(CollegeList a, CollegeList b) {
return Double.compare(a.getScoreDistance(), b.getScoreDistance());
}
};
3) Sort the array or ArrayList using the comparator. If it's an array:
Arrays.sort(theArray, compareByScoreDistance);
If it's an ArrayList, use Collections.sort instead of Arrays.sort.
4) Now you can create the string array by going through the CollegeList[] or ArrayList<CollegeList> and creating an array or ArrayList using getName(). For example, if your list is an ArrayList, then you can use this from #user3717646's answer:
for (CollegeList collegeList : theList) {
nameList.add(collegeList.getName());
}
Or using Java 8:
String[] names = theList.stream().map(CollegeList::getName).toArray(String[]::new);
or
ArrayList<String> names = new ArrayList<>(theList.stream().map(CollegeList::getName).collect(Collectors.toList()));
EDIT: Code has now been tested, and several mistakes fixed.
Try Using ArrayLists. Following sample code is given for two CollegeList objects.
ArrayList<CollegeList> collegeLists=new ArrayList<>(); // To store all CollegeList Objects
ArrayList<String> nameList=new ArrayList<>(); // To store Names
CollegeList cl1=new CollegeList(12, 45, 5, "Name1", "Location1");
CollegeList cl2=new CollegeList(12, 45, 5, "Name2", "Location2");
collegeLists.add(cl1);
collegeLists.add(cl2);
for (CollegeList collegeList : collegeLists) {
nameList.add(collegeList.getName());
}
collegeLists stores all CollegeList objects.
then you can get each and every parameter using get methods and put the in to seperate aarraylists.
If you want to sort the arraylist, You can uose Collections.sort(nameList); to do it.

Sorting Lines Based on words in them as keys

Odrer ID Descrption Type
Envelope K205 Green Paper >>>>String Line with space in between
Box J556 Yellow Thermocol >>>>String Line with space in between
Envelope L142 White Plastic >>>>String Line with space in between
I have a table something like above and I have to provide options sort the
data based on the column name, like "sort by Order", "Sort By Type" something like that
plz suggest me the best ideas to achieve this in Java... The table rows are strings with
space separating each touple..
Represent each row in your table as an object with order, id, description and type attributes. Add each object to a collection such as a list. You can then create different comparators such as an OrderComparator and a TypeComparator to sort the collection based on order or type respectively.
For example, here is what your data class might look like:
public class Data{
private final String order;
private final String id;
private final String description;
private final String type;
public Data(String order, String id, String description, String type) {
this.order=order;
this.id=id;
this.description=description;
this.type=type;
}
/** getter methods here **/
}
Now, you would add them to a list and use a specific comparator to sort it:
//add data to a list
List<Data> list = new ArrayList<Data>();
list.add(new Data("Envelope","K205","Green","Paper"));
list.add(new Data("Box","J556","Yellow","Thermocol"));
list.add(new Data("Envelope","L142","White","Plastic"));
//create a comparator to sort by type
Comparator<Data> typeComparator = new Comparator<Data>() {
#Override
public int compare(Data o1, Data o2) {
return o1.getType().compareTo(o2.getType());
}
};
//sort the list
Collections.sort(list, typeComparator);
You have to develop your custom Comparator. For example something like the following.
class ColumnComparator implements Comparator<String> {
private int column;
ColumnComparator(int column) {
this.column = column;
}
public int compare(String s1, String s2) {
return getColumn(s1).compareTo(getColumn(s2));
}
private String getColumn(String line) {
return line.split("\\s+")[column];
}
Then you can use Arrays.sort() or Collections.sort() passing there array or collection of your lines and instance of this comparator. I leave it for you to implement logic that finds the column index by column name.
}
Outline of a possible approach (assuming the data does not come from a database):
Represent your data as a class with the appropriate fields:
class YourData {
private final String Order;
private final String Id;
private final String Description;
private final String Type;
// add getters, setters and appropriate constructors
}
Use the appropriate data-types. You can use a Scanner to parse the data.
Write a Comparator for each attribute you want to order by. If you want to reverse the ordering, you can use Collections.reverseOrder.
Put your data into an ArrayList, and use Collections.sort with the appropriate Comparator to achieve the desired ordering.

How do you sort numeric values stored in a hashmap then in an ArrayList in Java?

I have an ArrayList.
Each element is a HashMap lookup for my values.
1 of the values is numeric.
I'm using Collections.sort() for alpha sorting. But the numbers are getting alpha sorted, instead of numeric sorted.
protected ArrayList<HashMap<String, String>> myAccountList;
String Name = myAccountList.get(i).get("accountName");
Double balance = Double.valueOf(myAccountList.get("accountBalance")); //is a number stored as a string
So I have the sort working on accountName just fine.
Collections.sort(myAccountList, new Comparator(){
public int compare(Object o1, Object o2) {
HashMap<String,String> old1 = (HashMap<String, String>) o1;
HashMap<String,String> old2 = (HashMap<String, String>) o2;
return old1.get("accountName").compareToIgnoreCase(old2.get("accountName"));
}
});
How would I sort the field accountBalance as a number? I doubt it matters, but I'm programming an Android app.
It looks like you're using a HashMap where you should design your own class:
public class Account {
public final String name;
public final Double balance;
public Account(String name, Double balance) {
this.name = name;
this.balance = balance;
}
}
(Note: currently this class is immutable. To make it mutable, change the fields from public final to private and add getters/setters)
Then you can store them in a List<Account> and sort accordingly:
//sort by name
Collections.sort(myAccountList, new Comparator<Account>(){
public int compare(Account acc1, Account acc2) {
return acc1.name.compareTo(acc2.name);
}
});
//sort by balance
Collections.sort(myAccountList, new Comparator<Account>(){
public int compare(Account acc1, Account acc2) {
return acc1.balance.compareTo(acc2.balance);
}
});
Note the use of a Comparator with the generic type parameter <Account>, so you don't have to cast the compareTo() arguments from Object.
You will have to do something like this:
Double.valueOf(old1.get("accountBalance")).compareTo(Double.valueOf(old2.get("accountBalance")));
To sort by both the account name and balance, you would check if the first compare is 0 (they are equal) and if so, return the accountBalance compare, assuming you are sorting by account name first.

Categories