Sorting using comparator - java

I need to sort below list of Details class objects with Java 8 Comparator
class Details{
String name;
String age;
}
in the order of Details class's names as below preference
1st--starting with Alphabets
2nd--starting with numeric
and 3rd-- starts with special character
Expected Result s/b:
sorted List of Details = sorted based on Details name irrespective of other parameters in the class
"Came"
"result"
"Result came"
"01 Result"
"02 Result"
"05 Result"
"# Came"
"# Result"
Collections.sort(List.o(details1,details2,details3),(d1,d2)->{
// got stuck here what to include to achieve the sort as per my requirement
return d1.getName().compareTo(d2.getName());
});

Here is Java 11(Just used var, that's it) code for you:
var a = List.of(details1, details2, details3)
.stream()
.sorted(Comparator.comparingInt((Details d) -> priority(d.getName())).thenComparing(Details::getName))
.collect(Collectors.toList());
If priority method is difficult to understand, you can refer #Eklavya's code
private static int priority(String str){
return Character.isAlphabetic(str.charAt(0))?1:Character.isDigit(str.charAt(0))?2:3;
}

You can define a custom priority like this
private static int priority(String str){
if (Character.isAlphabetic(string.charAt(0)))return 1;
if (Character.isDigit(string.charAt(0))) return 2;
return 3;
}
Then you can use in the compare function
Collections.sort(list, new Comparator<Project>() {
#Override
public int compare(Details o1, Details o2) {
int result = Integer.compare(priority(o1.name), priority(o2.name));
if (result != 0) return result;
return o1.name.compareTo(o2.name);
}
});
Using java 8 syntax
Collections.sort(list,Comparator.comparingInt(d -> priority(d.getName()))
.thenComparing(d-> d.getName()));

Related

How to sort and avoid duplicates in TreeSet with my comparator?

I wan to create TreeSet() that will sort my elements with my predefined comparator. But the problem is when I give the comparator as a parameter to the constructor of the TreeSet(MyComparator), the TreeSet is not avoiding duplicates. Can I achieve sorting of the elements and avoiding duplicates?
The comparator looks like:
public static Comparator<Participant> byNameAndAge = (L, R) -> {
//check if they have the same code
if (L.code.equalsIgnoreCase(R.code))
return 0;
int res = L.name.compareToIgnoreCase(R.name);
if (res == 0)
res = Integer.compare(L.age, R.age);
return res;
};
You've misunderstood a few things. TreeSet does eliminate duplicates, with 'a duplicate' defined as 'any two elements for which your compare method returns 0'. No 2 such elements can both exist in a treeset. I'm sure your code doesn't work if you say so, but the code you pasted isn't the problem, nor is TreeSet's code.
A trivial example:
Comparator<String> byLength = (a, b) -> a.length() - b.length();
Set<String> set = new TreeSet<String>(byLength);
set.add("Hello");
set.add("World");
set.add("X");
set.add("VeryLong");
System.out.println(set);
> [X, Hello, VeryLong]
Note how 'World' disappeared, because the comparator says it is equal to Hello (they are both 5 length, a.length() - b.length() is returning 0, and 0 is 'equal, thus, eliminate the duplicate' according to treeset). In other words, your code as pasted would eliminate duplicates, the problem lies elsewhere.
This code is almost the same as yours.
Comparators chaining
public static void main(String[] args) {
// custom comparator
Comparator<Participant> byNameAndAge = Comparator
// first sorting by name ignoring case
.comparing(Participant::getName, String::compareToIgnoreCase)
// second sorting by age
.thenComparingInt(Participant::getAge);
// constructor with a comparator as a parameter
TreeSet<Participant> treeSet = new TreeSet<>(byNameAndAge);
treeSet.addAll(Set.of( // test data
new Participant("John", 25),
new Participant("Junior", 2),
new Participant("james", 31),
new Participant("john", 22)));
// output
treeSet.forEach(System.out::println);
//name=james, age=31
//name=john, age=22
//name=John, age=25
//name=Junior, age=2
}
static class Participant {
String name;
int age;
public Participant(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
#Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}

arraylist sort and convert to String[]

I have a simple class which is auto-generated and use to store responses from Retrofit. Anyway in the final step I would like to use row content, sort every element by position from highest to lowest and after sorting convert it to String[] with name only. How can I do that in the most efficient way?
public class RowModel implements Comparable<RowModel>{
private String name;
private double position;
public double getPosition() {
return position;
}
public void setPosition(float position) {
this.position = position;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public RowModel(String name, double position) {
this.name = name;
this.position = position;
}
}
After my search online I found this method to execute sorting but I'm not sure that the results are correct. And of course I still don't know how to convert sorted names to String[] at the final step:
#Override
public int compareTo(RowModel rowModel) {
double comparePosition = ((RowModel) rowModel).getPosition();
return (int) (this.position- comparePosition);
}
Your compareTo method won't work as you are comparing double values and it will fail when integral parts of two values are same (i.e. when comparing 1.5 & 1.2 your comparator will return 0 but it should return 1).
for that you can use Double.compare(d1, d2). you don't need to cast rowModel, too. Change your method as follow:
#Override
public int compareTo(RowModel rowModel) {
double comparePosition = rowModel.getPosition();
return Double.compare(this.position, comparePosition);
}
For Sorting you can use Collections.sort(rowModels).
To convert this sorted list to list of names, you can use Stream API.
Assuming you have Sorted List of RowModel with name rowModels.
List<String> names = rowModels.stream().map(RowModel::getName).collect(Collectors.toList());
If you want to convert this list to array,
String[] array = names.toArray(new String[names.size()]);
or directly,
String[] array = rowModels.stream().map(RowModel::getName).toArray(String[]::new);
Assuming a list of rows RowModel in a variable named rows, you can easily do that using the Stream API:
List<String> names = rows.stream().sorted()
.map(row -> row.getName())
.collect(Collectors.toList());
The intermediate step sorted() makes sure the stream is sorted, according to the Comparable implementation in your RowModel class, in this case.
Side note: Your compareTo implementation can be improved as it may be losing precision (casting double to int has the side effect of treating as equal two objects that aren't):
#Override
public int compareTo(RowModel rowModel) {
double comparePosition = ((RowModel) rowModel).getPosition();
double diff = this.position - comparePosition;
return diff == 0 ? 0 :
(diff > 0 ? 1 : -1);
}
List<RowModel> sortedModels = new ArrayList<>(models);
Collections.sort(sortedModels);
return sortedModels.toArray(new RowModel[0]);
Edit:
To actually return array of just names rather than whole RowModels, you could do something like this:
String[] result = new String[sortedModels.size()];
for (int i = 0; i < sortedModels.size(); i++) {
result.[i] = sortedModels.get(i).getName();
}
There is probably some shorter way to do this using Java 8 streams, but I'll leave that to someone who actually works with them.
Use util method of java.util.Collections class, i.e
Collections.sort(list)
If you want to sort custom object you can use
Collections.sort(List<T> list, Comparator<? super T> c)
see collections api

How to match the exact string value in the list of comma separated string [duplicate]

I have a String[] with values like so:
public static final String[] VALUES = new String[] {"AB","BC","CD","AE"};
Given String s, is there a good way of testing whether VALUES contains s?
Arrays.asList(yourArray).contains(yourValue)
Warning: this doesn't work for arrays of primitives (see the comments).
Since java-8 you can now use Streams.
String[] values = {"AB","BC","CD","AE"};
boolean contains = Arrays.stream(values).anyMatch("s"::equals);
To check whether an array of int, double or long contains a value use IntStream, DoubleStream or LongStream respectively.
Example
int[] a = {1,2,3,4};
boolean contains = IntStream.of(a).anyMatch(x -> x == 4);
Concise update for Java SE 9
Reference arrays are bad. For this case we are after a set. Since Java SE 9 we have Set.of.
private static final Set<String> VALUES = Set.of(
"AB","BC","CD","AE"
);
"Given String s, is there a good way of testing whether VALUES contains s?"
VALUES.contains(s)
O(1).
The right type, immutable, O(1) and concise. Beautiful.*
Original answer details
Just to clear the code up to start with. We have (corrected):
public static final String[] VALUES = new String[] {"AB","BC","CD","AE"};
This is a mutable static which FindBugs will tell you is very naughty. Do not modify statics and do not allow other code to do so also. At an absolute minimum, the field should be private:
private static final String[] VALUES = new String[] {"AB","BC","CD","AE"};
(Note, you can actually drop the new String[]; bit.)
Reference arrays are still bad and we want a set:
private static final Set<String> VALUES = new HashSet<String>(Arrays.asList(
new String[] {"AB","BC","CD","AE"}
));
(Paranoid people, such as myself, may feel more at ease if this was wrapped in Collections.unmodifiableSet - it could then even be made public.)
(*To be a little more on brand, the collections API is predictably still missing immutable collection types and the syntax is still far too verbose, for my tastes.)
You can use ArrayUtils.contains from Apache Commons Lang
public static boolean contains(Object[] array, Object objectToFind)
Note that this method returns false if the passed array is null.
There are also methods available for primitive arrays of all kinds.
Example:
String[] fieldsToInclude = { "id", "name", "location" };
if ( ArrayUtils.contains( fieldsToInclude, "id" ) ) {
// Do some stuff.
}
Just simply implement it by hand:
public static <T> boolean contains(final T[] array, final T v) {
for (final T e : array)
if (e == v || v != null && v.equals(e))
return true;
return false;
}
Improvement:
The v != null condition is constant inside the method. It always evaluates to the same Boolean value during the method call. So if the input array is big, it is more efficient to evaluate this condition only once, and we can use a simplified/faster condition inside the for loop based on the result. The improved contains() method:
public static <T> boolean contains2(final T[] array, final T v) {
if (v == null) {
for (final T e : array)
if (e == null)
return true;
}
else {
for (final T e : array)
if (e == v || v.equals(e))
return true;
}
return false;
}
Four Different Ways to Check If an Array Contains a Value
Using List:
public static boolean useList(String[] arr, String targetValue) {
return Arrays.asList(arr).contains(targetValue);
}
Using Set:
public static boolean useSet(String[] arr, String targetValue) {
Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);
}
Using a simple loop:
public static boolean useLoop(String[] arr, String targetValue) {
for (String s: arr) {
if (s.equals(targetValue))
return true;
}
return false;
}
Using Arrays.binarySearch():
The code below is wrong, it is listed here for completeness. binarySearch() can ONLY be used on sorted arrays. You will find the result is weird below. This is the best option when array is sorted.
public static boolean binarySearch(String[] arr, String targetValue) {
return Arrays.binarySearch(arr, targetValue) >= 0;
}
Quick Example:
String testValue="test";
String newValueNotInList="newValue";
String[] valueArray = { "this", "is", "java" , "test" };
Arrays.asList(valueArray).contains(testValue); // returns true
Arrays.asList(valueArray).contains(newValueNotInList); // returns false
If the array is not sorted, you will have to iterate over everything and make a call to equals on each.
If the array is sorted, you can do a binary search, there's one in the Arrays class.
Generally speaking, if you are going to do a lot of membership checks, you may want to store everything in a Set, not in an array.
For what it's worth I ran a test comparing the 3 suggestions for speed. I generated random integers, converted them to a String and added them to an array. I then searched for the highest possible number/string, which would be a worst case scenario for the asList().contains().
When using a 10K array size the results were:
Sort & Search : 15
Binary Search : 0
asList.contains : 0
When using a 100K array the results were:
Sort & Search : 156
Binary Search : 0
asList.contains : 32
So if the array is created in sorted order the binary search is the fastest, otherwise the asList().contains would be the way to go. If you have many searches, then it may be worthwhile to sort the array so you can use the binary search. It all depends on your application.
I would think those are the results most people would expect. Here is the test code:
import java.util.*;
public class Test {
public static void main(String args[]) {
long start = 0;
int size = 100000;
String[] strings = new String[size];
Random random = new Random();
for (int i = 0; i < size; i++)
strings[i] = "" + random.nextInt(size);
start = System.currentTimeMillis();
Arrays.sort(strings);
System.out.println(Arrays.binarySearch(strings, "" + (size - 1)));
System.out.println("Sort & Search : "
+ (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
System.out.println(Arrays.binarySearch(strings, "" + (size - 1)));
System.out.println("Search : "
+ (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
System.out.println(Arrays.asList(strings).contains("" + (size - 1)));
System.out.println("Contains : "
+ (System.currentTimeMillis() - start));
}
}
Instead of using the quick array initialisation syntax too, you could just initialise it as a List straight away in a similar manner using the Arrays.asList method, e.g.:
public static final List<String> STRINGS = Arrays.asList("firstString", "secondString" ...., "lastString");
Then you can do (like above):
STRINGS.contains("the string you want to find");
With Java 8 you can create a stream and check if any entries in the stream matches "s":
String[] values = {"AB","BC","CD","AE"};
boolean sInArray = Arrays.stream(values).anyMatch("s"::equals);
Or as a generic method:
public static <T> boolean arrayContains(T[] array, T value) {
return Arrays.stream(array).anyMatch(value::equals);
}
You can use the Arrays class to perform a binary search for the value. If your array is not sorted, you will have to use the sort functions in the same class to sort the array, then search through it.
ObStupidAnswer (but I think there's a lesson in here somewhere):
enum Values {
AB, BC, CD, AE
}
try {
Values.valueOf(s);
return true;
} catch (IllegalArgumentException exc) {
return false;
}
Actually, if you use HashSet<String> as Tom Hawtin proposed you don't need to worry about sorting, and your speed is the same as with binary search on a presorted array, probably even faster.
It all depends on how your code is set up, obviously, but from where I stand, the order would be:
On an unsorted array:
HashSet
asList
sort & binary
On a sorted array:
HashSet
Binary
asList
So either way, HashSet for the win.
Developers often do:
Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);
The above code works, but there is no need to convert a list to set first. Converting a list to a set requires extra time. It can as simple as:
Arrays.asList(arr).contains(targetValue);
or
for (String s : arr) {
if (s.equals(targetValue))
return true;
}
return false;
The first one is more readable than the second one.
If you have the google collections library, Tom's answer can be simplified a lot by using ImmutableSet (http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/ImmutableSet.html)
This really removes a lot of clutter from the initialization proposed
private static final Set<String> VALUES = ImmutableSet.of("AB","BC","CD","AE");
In Java 8 use Streams.
List<String> myList =
Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
One possible solution:
import java.util.Arrays;
import java.util.List;
public class ArrayContainsElement {
public static final List<String> VALUES = Arrays.asList("AB", "BC", "CD", "AE");
public static void main(String args[]) {
if (VALUES.contains("AB")) {
System.out.println("Contains");
} else {
System.out.println("Not contains");
}
}
}
Using a simple loop is the most efficient way of doing this.
boolean useLoop(String[] arr, String targetValue) {
for(String s: arr){
if(s.equals(targetValue))
return true;
}
return false;
}
Courtesy to Programcreek
the shortest solution
the array VALUES may contain duplicates
since Java 9
List.of(VALUES).contains(s);
Use the following (the contains() method is ArrayUtils.in() in this code):
ObjectUtils.java
public class ObjectUtils {
/**
* A null safe method to detect if two objects are equal.
* #param object1
* #param object2
* #return true if either both objects are null, or equal, else returns false.
*/
public static boolean equals(Object object1, Object object2) {
return object1 == null ? object2 == null : object1.equals(object2);
}
}
ArrayUtils.java
public class ArrayUtils {
/**
* Find the index of of an object is in given array,
* starting from given inclusive index.
* #param ts Array to be searched in.
* #param t Object to be searched.
* #param start The index from where the search must start.
* #return Index of the given object in the array if it is there, else -1.
*/
public static <T> int indexOf(final T[] ts, final T t, int start) {
for (int i = start; i < ts.length; ++i)
if (ObjectUtils.equals(ts[i], t))
return i;
return -1;
}
/**
* Find the index of of an object is in given array, starting from 0;
* #param ts Array to be searched in.
* #param t Object to be searched.
* #return indexOf(ts, t, 0)
*/
public static <T> int indexOf(final T[] ts, final T t) {
return indexOf(ts, t, 0);
}
/**
* Detect if the given object is in the given array.
* #param ts Array to be searched in.
* #param t Object to be searched.
* #return If indexOf(ts, t) is greater than -1.
*/
public static <T> boolean in(final T[] ts, final T t) {
return indexOf(ts, t) > -1;
}
}
As you can see in the code above, that there are other utility methods ObjectUtils.equals() and ArrayUtils.indexOf(), that were used at other places as well.
For arrays of limited length use the following (as given by camickr). This is slow for repeated checks, especially for longer arrays (linear search).
Arrays.asList(...).contains(...)
For fast performance if you repeatedly check against a larger set of elements
An array is the wrong structure. Use a TreeSet and add each element to it. It sorts elements and has a fast exist() method (binary search).
If the elements implement Comparable & you want the TreeSet sorted accordingly:
ElementClass.compareTo() method must be compatable with ElementClass.equals(): see Triads not showing up to fight? (Java Set missing an item)
TreeSet myElements = new TreeSet();
// Do this for each element (implementing *Comparable*)
myElements.add(nextElement);
// *Alternatively*, if an array is forceably provided from other code:
myElements.addAll(Arrays.asList(myArray));
Otherwise, use your own Comparator:
class MyComparator implements Comparator<ElementClass> {
int compareTo(ElementClass element1; ElementClass element2) {
// Your comparison of elements
// Should be consistent with object equality
}
boolean equals(Object otherComparator) {
// Your equality of comparators
}
}
// construct TreeSet with the comparator
TreeSet myElements = new TreeSet(new MyComparator());
// Do this for each element (implementing *Comparable*)
myElements.add(nextElement);
The payoff: check existence of some element:
// Fast binary search through sorted elements (performance ~ log(size)):
boolean containsElement = myElements.exists(someElement);
If you don't want it to be case sensitive
Arrays.stream(VALUES).anyMatch(s::equalsIgnoreCase);
Try this:
ArrayList<Integer> arrlist = new ArrayList<Integer>(8);
// use add() method to add elements in the list
arrlist.add(20);
arrlist.add(25);
arrlist.add(10);
arrlist.add(15);
boolean retval = arrlist.contains(10);
if (retval == true) {
System.out.println("10 is contained in the list");
}
else {
System.out.println("10 is not contained in the list");
}
Check this
String[] VALUES = new String[]{"AB", "BC", "CD", "AE"};
String s;
for (int i = 0; i < VALUES.length; i++) {
if (VALUES[i].equals(s)) {
// do your stuff
} else {
//do your stuff
}
}
Arrays.asList() -> then calling the contains() method will always work, but a search algorithm is much better since you don't need to create a lightweight list wrapper around the array, which is what Arrays.asList() does.
public boolean findString(String[] strings, String desired){
for (String str : strings){
if (desired.equals(str)) {
return true;
}
}
return false; //if we get here… there is no desired String, return false.
}
Use below -
String[] values = {"AB","BC","CD","AE"};
String s = "A";
boolean contains = Arrays.stream(values).anyMatch(v -> v.contains(s));
Use Array.BinarySearch(array,obj) for finding the given object in array or not.
Example:
if (Array.BinarySearch(str, i) > -1)` → true --exists
false --not exists
Try using Java 8 predicate test method
Here is a full example of it.
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Test {
public static final List<String> VALUES =
Arrays.asList("AA", "AB", "BC", "CD", "AE");
public static void main(String args[]) {
Predicate<String> containsLetterA = VALUES -> VALUES.contains("AB");
for (String i : VALUES) {
System.out.println(containsLetterA.test(i));
}
}
}
http://mytechnologythought.blogspot.com/2019/10/java-8-predicate-test-method-example.html
https://github.com/VipulGulhane1/java8/blob/master/Test.java
Create a boolean initially set to false. Run a loop to check every value in the array and compare to the value you are checking against. If you ever get a match, set boolean to true and stop the looping. Then assert that the boolean is true.
As I'm dealing with low level Java using primitive types byte and byte[], the best so far I got is from bytes-java https://github.com/patrickfav/bytes-java seems a fine piece of work
You can check it by two methods
A) By converting the array into string and then check the required string by .contains method
String a = Arrays.toString(VALUES);
System.out.println(a.contains("AB"));
System.out.println(a.contains("BC"));
System.out.println(a.contains("CD"));
System.out.println(a.contains("AE"));
B) This is a more efficent method
Scanner s = new Scanner(System.in);
String u = s.next();
boolean d = true;
for (int i = 0; i < VAL.length; i++) {
if (VAL[i].equals(u) == d)
System.out.println(VAL[i] + " " + u + VAL[i].equals(u));
}

How to use Collections.sort() in Java?

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

Java Sorting based on Enum constants

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.

Categories

Resources