We have an enum
enum listE {
LE1,
LE4,
LE2,
LE3
}
Furthermore, we have a list that contains the strings ["LE1","LE2","LE3","LE4"]. Is there a way to sort the list based on the enum defined order (not the natural String order).
The sorted list should be ["LE1", "LE4", "LE2", "LE3"].
Enum<E> implements Comparable<E> via the natural order of the enum (the order in which the values are declared). If you just create a list of the enum values (instead of strings) via parsing, then sort that list using Collections.sort, it should sort the way you want. If you need a list of strings again, you can just convert back by calling name() on each element.
I used following to sort my List<theEnum> in an ascending order, and it worked fine for me.
Collections.sort(toSortEnumList, new Comparator<theEnum>() {
#Override
public int compare(theEnum o1, theEnum o2) {
return o1.toString().compareTo(o2.toString());
}
});
values() method returns in the order in which it is defined.
enum Test{
A,B,X,D
}
for(Test t: Test.values()){
System.out.println(t);
}
Output
A
B
X
D
Every enum constant has an ordinal value corresponding to its position in the enum declaration. You can write a comparator for your strings using the ordinal value of the corresponding enum constant.
Jon's answer is correct per the specification:
Enum implements Comparable via the natural order of the enum (the order in which the values are declared).
However, I wanted to leave a Java8 example in case somebody wants to map string values to an enum and sort by the enum order. With that you can map your strings to the enum, sort using the default comparable, and then map it back using a toString. This leaves you with something like this:
enum listE {
LE1,
LE4,
LE2,
LE3
}
public static void main(String[] args) {
List<String> originalList = Arrays.asList("LE1", "LE2", "LE3", "LE4");
System.out.println("Original List: " + originalList);
List<String> sortedList = originalList.stream()
.map(listE::valueOf)
.sorted(listE::compareTo)
.map(listE::toString)
.collect(Collectors.toList());
System.out.println("Sorted List: " + sortedList);
}
The result would be:
Original List: [LE1, LE2, LE3, LE4]
Sorted List: [LE1, LE4, LE2, LE3]
If you want different sort order then provided in Enum class and you cannot modify it, just assign int to your enum fields and compare it:
public class MyComparator implements Comparator<ListE> {
#Override
public int compare(ListE o1, ListE o2) {
return Integer.compare(getAssignedValue(o1), getAssignedValue(o2));
}
int getAssignedValue(ListE listE) {
switch (listE) {
case LE2:
return 0;
case LE1:
return 1;
case LE4:
return 2;
case LE3:
return 3;
default:
return Integer.MAX_VALUE;
}
}
}
and then use
Collections.sort(myList, new MyComparator());
public class Student implements Comparable<Student>{
public String studentName;
public Student(String name,DayInWeek weekDay){
this.studentName = name;
this.studentDays = weekDay;
}
public enum DayInWeek {
SATURDAY, SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
}
public DayInWeek studentDays;
#Override
public int compareTo(Student s1) {
if(s1.studentDays.ordinal() < this.studentDays.ordinal())
return 1;
else if(s1.studentDays.ordinal() > this.studentDays.ordinal())
return -1;
else
return 1;
}
}
you should probably look at the ordinal() method of the enum, it returns an Integer of the position the enum type appears in the enum class, so in your case LE1 = 0, LE4 = 1, etc...
Try to use :
add to enum field(sorted field)
like
enum MyEnum{
private String sorted;
MyEnum(String sorted){
this.sorted = sorted;
}
String getSorted(){
return this.sorted;
}
}
Use TreeSet
Implement Comparator using MyEnum.sorted filed
Here is a method you can add to your enumeration to get an array of sorted enumerated constants:
Where Element is the name of your Enumeration
Where the enumerations are sorted by their toString
public static Element[] getSortedValues(){
return Stream.of(values()).sorted((o1,o2)->
{
return o1.toString().compareTo(o2.toString());
}).
toArray(Element[]::new);
}
If you wan to sort by ordinal you can use valueOf to convert the string and add these to an EnumSet (which is sorted by ordinal)
Otherwise you can sort the values based on an attribute of the enum. (This can be more robust and not dependent of the order the enums are declared) Use valueOf and write a custom Comparator.
Related
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.
}
I have a object called project, I want to sort this project by 2 of its fields:
First: by Date(Gregorian Callander);
Second: by Name(String);
I want to sort the project by date form new to old. The only way I know to do this is to reverse the collection. However I want to sort the project with same date on name(alphabetically), where reverse also reverses this part of the sort.
Is there a way to reverse only part of the sort method, or any other way to get this sorted first by a date(reverse) and then a string(normal order a-z) ?
At the moment I am overriding the object compareTo method like so:
#Override
public int compareTo(Project project) {
int i = this.projectDate.compareTo(project.projectDate);
if(i != 0) return i;
return this.projectName.compareTo(project.projectName);
}
Date#compareTo returns a value < 0 if this Date is before the Date argument and a value > 0 otherwise.
If you want to reverse the sort from new to old, you can just return the negative compare result:
#Override
public int compareTo(Project project) {
int i = this.projectDate.compareTo(project.projectDate);
if(i != 0) return -i; // reverse sort
return this.projectName.compareTo(project.projectName);
}
In Java 8, the Comparator interface has a method thenComparing. You can use this method to create a comparator that compare by more than one field.
If you have a comparator to compare alphabetically and other to compare by dates, you can combine the comparator to sort by the field you want:
Comparator<Project> nameComparator = ...
Comparator<Project> dateComparator = ...
You can mix the comparator, using the reverse comparator if needed. These are some examples:
Comparator<Project> nameAndDateComparator = nameComparator.thenComparing(dateComparator);
Comparator<Project> nameAndReversedDateComparator = nameComparator.thenComparing(dateComparator.reversed());
Then, you can use the method sort as usual with the comparator that matches your needs.
If you are not using Java 8, you can create an utility class to combine your comparators:
public class CombinedComparator<T> implements Comparator<T> {
Comparator<T> firstComparator;
Comparator<T> secondComparator;
public CombinedComparator(Comparator<T> firstComparator, Comparator<T> secondComparator) {
this.firstComparator = firstComparator;
this.secondComparator = secondComparator;
}
#Override
public int compare(T o1, T o2) {
int result = firstComparator.compare(o1, o2);
return (result != 0) ? result : secondComparator.compare(o1, o2);
}
}
And you could create multiple fields comparators this way:
Comparator<Project> nameAndDateComparator = new CombinedComparator<Project>(nameComparator, dateComparator);
Comparator<Project> nameAndReversedDateComparator = new CombinedComparator<Project>(nameComparator, Collections.reverseOrder(dateComparator));
I have an arraylist defined whose elements are say, [man, animal, bird, reptile]. The elements in the arraylist are non-mandatory. The list can even be empty.
I always need to give the output as [animal,man,reptile,bird]. Means, the order of the elements are to be maintained. Is there any way of doing in arraylist?
I thought I can do like
for (String listElement: customList) { //custom list variable holds all elements
if (listElement.equalsIgnoreCase("animal"){
newList.add(0, listElement);
} else if("man") {
newlist.add(1, listElement);
}
But I would want to know the best practice of doing. Can someone please help me on this?
You can define a custom sorting and use it to order your array (save the comparator somewhere, so you don't have to instantiate it many times):
List<String> definedOrder = // define your custom order
Arrays.asList("animal", "man", "reptile", "bird");
Comparator<String> comparator = new Comparator<String>(){
#Override
public int compare(final String o1, final String o2){
// let your comparator look up your car's color in the custom order
return Integer.valueOf(definedOrder.indexOf(o1))
.compareTo(Integer.valueOf(definedOrder.indexOf(o2)));
}
};
Collections.sort(myList, comparator);
Use a custom comparator:
Collections.sort(customList, comparator);
int i = 0;
for (String temp : customList) {
System.out.println("customList " + ++i + " : " + temp);
}
Custom comparator below:
public static Comparator<String> comparator = new Comparator<String>() {
public int compare(String str1, String str2) {
return orderOf(str1) - orderOf(str2);
}
private int orderOf(String name) {
return ((List)Arrays.asList("animal", "man", "reptile", "bird")).indexOf(name);
}
};
You can use Collections.sort(yourArrayList, new CustomComparator());
You need to create your own comparator for this, though, but it is easy.
public class CustomComparator implements Comparator<YourType>{
#Override
public int compare(YoyrType o1, YoyrType o2) {
// logic for ordering the list
}
}
arraylist is ordered.
maybe you want to insert element into the list.
could you create a new list every time when you have to insert and copy each of them?
I got an object Recipe that implements Comparable<Recipe> :
public int compareTo(Recipe otherRecipe) {
return this.inputRecipeName.compareTo(otherRecipe.inputRecipeName);
}
I've done that so I'm able to sort the List alphabetically in the following method:
public static Collection<Recipe> getRecipes(){
List<Recipe> recipes = new ArrayList<Recipe>(RECIPE_MAP.values());
Collections.sort(recipes);
return recipes;
}
But now, in a different method, lets call it getRecipesSort(), I want to sort the same list but numerically, comparing a variable that contains their ID. To make things worse, the ID field is of the type String.
How do I use Collections.sort() to perform the sorts in Java?
Use this method Collections.sort(List,Comparator) . Implement a Comparator and pass it to Collections.sort().
class RecipeCompare implements Comparator<Recipe> {
#Override
public int compare(Recipe o1, Recipe o2) {
// write comparison logic here like below , it's just a sample
return o1.getID().compareTo(o2.getID());
}
}
Then use the Comparator as
Collections.sort(recipes,new RecipeCompare());
The answer given by NINCOMPOOP can be made simpler using Lambda Expressions:
Collections.sort(recipes, (Recipe r1, Recipe r2) ->
r1.getID().compareTo(r2.getID()));
Also introduced after Java 8 is the comparator construction methods in the Comparator interface. Using these, one can further reduce this to 1:
recipes.sort(comparingInt(Recipe::getId));
1 Bloch, J. Effective Java (3rd Edition). 2018. Item 42, p. 194.
Create a comparator which accepts the compare mode in its constructor and pass different modes for different scenarios based on your requirement
public class RecipeComparator implements Comparator<Recipe> {
public static final int COMPARE_BY_ID = 0;
public static final int COMPARE_BY_NAME = 1;
private int compare_mode = COMPARE_BY_NAME;
public RecipeComparator() {
}
public RecipeComparator(int compare_mode) {
this.compare_mode = compare_mode;
}
#Override
public int compare(Recipe o1, Recipe o2) {
switch (compare_mode) {
case COMPARE_BY_ID:
return o1.getId().compareTo(o2.getId());
default:
return o1.getInputRecipeName().compareTo(o2.getInputRecipeName());
}
}
}
Actually for numbers you need to handle them separately check below
public static void main(String[] args) {
String string1 = "1";
String string2 = "2";
String string11 = "11";
System.out.println(string1.compareTo(string2));
System.out.println(string2.compareTo(string11));// expected -1 returns 1
// to compare numbers you actually need to do something like this
int number2 = Integer.valueOf(string1);
int number11 = Integer.valueOf(string11);
int compareTo = number2 > number11 ? 1 : (number2 < number11 ? -1 : 0) ;
System.out.println(compareTo);// prints -1
}
Use the method that accepts a Comparator when you want to sort in something other than natural order.
Collections.sort(List, Comparator)
Sort the unsorted hashmap in ascending order.
// Sorting the list based on values
Collections.sort(list, new Comparator<Entry<String, Integer>>() {
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2)
{
return o2.getValue().compareTo(o1.getValue());
}
});
// Maintaining insertion order with the help of LinkedList
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
I have an ArrayList that consists of an ArrayList that constists of Strings: ArrayList<ArrayList<String>>. How can I sort on the first entry of he inner ArrayList? For example I would like this:
a = [['1','apple'],['3','pear'],['2','banana'],['1',orange']]
to become:
a_sorted = [['1','apple'],['1','orange'],['2','banana'],['3','pear']]
The order of duplicate first entries (like apple and orange) do not matter. I've tried using Collections.sort(a,new ColumnComparator()) but it will not accept ArrayLists. This is the class I used:
public class ColumnComparator implements Comparator<ArrayList<String>>{
public int compare(ArrayList<String> ar1, ArrayList<String> ar2){
return ar1.get(0).compareTo(ar2.get(0));
}
}
Instead of storing an Array of an Array, why don't you create a custom Class that implements Comparable. eg.
class Fruit implements Comparable<Fruit> {
protected int number;
protected String name;
public Fruits(int number, String name) {
this.number = number;
this.name = name;
}
#Override
public int compareTo(Fruit f) {
return number < f.number;
// or depending on if ascending or descending order wanted
// return number > f.number
}
}
Then to sort just run Collections.sort(a). This way is flexible and easily extended.
You can create a Map <String, ArrayList<String>> with first entry of the ArrayLists as key and the ArrayList itself as value. Then sort the Map (use Sorted Map or a Comparator to sort on the Map keys) on keys and you will get what you want.
Why cant you use a this ArrayList<Map<String,String>> instead of ArrayList<ArrayList<String>>. You can easily sort the Map on the key by using TreeMap.
Note: This will only work if you have only two entries in your inner arraylist.
If you really want to do it that way, you can try this:
import java.util.Comparator;
public class ColumnComparable implements Comparator<ArrayList<String>>{
#Override
public int compare(ArrayList<String> o1, ArrayList<String> o2) {
return (Integer.parseInt(o1.get(0)) > Integer.parseInt(o2.get(0)) ? -1 : (Integer.parseInt(o1.get(0)) == Integer.parseInt(o2.get(0)) ? 0 : 1));
}
}
The code was found here.