Collections.sort isn't sorting - java

I am building a web application using Java EE (although my problem is more Java based)
In a Servlet, I am getting a list of orders from the EJB. In this list of orders, there is a list of states for this order (sent, on dock, non received ...)
I want to sort this list of states by the date of the state. So I use Collections.sort like this:
for (Command c : commands) {
c.getStateList().sort(new Comparator<State>() {
#Override
public int compare(State o1, State o2) {
return o1.getStateDate().compareTo(o2.getStateDate());
}
});
c.getStateList().sort(Collections.reverseOrder());
}
request.setAttribute("commands", commands);
But when I display the results, the states are not sorted.
I tried to reverse the order as you can see, but it isn't working either.
As you can also see, I replaced the Collections.sort with the ListIWantToSort.sort. Still not working.
Any ideas on why it does not work or how I could repair it?
EDIT : Here is the getter for the list and its instanciation :
#OneToMany(cascade = CascadeType.ALL, mappedBy = "ciiCommande")
private List<Etat> etatList;
#XmlTransient
public List<Etat> getEtatList() {
return etatList;
}
List<Commande> commandes = new ArrayList<Commande>();
And I get my commands by a findAll Method.
To display them, I use that :
<c:forEach items="${commandes}" var="cmd">
<td>${cmd.etatList[0].codeStatut.libelleSituation}</td>
</c:forEach>

You are first sorting the list using the custom comparator. Then you are re-sorting it according to the reversed natural ordering of the elements - not the custom ordering you already applied. So the first sort is not taking effect as the list is re-ordered by the second sort. Note that Collections.reverseOrder() does not reverse the list - it is the reverse of the natural ordering (so the elements in getEtatList() must already be Comparable).
Try losing the second sort and doing:
c.getEtatList().sort(new Comparator<Etat>() {
#Override
public int compare(Etat o1, Etat o2) {
// Note o2/o1 reversed.
return o2.getDateEtat().compareTo(o1.getDateEtat());
}
});

Try:
for (Commande c : commandes) {
c.getEtatList().sort(Collections.reverseOrder(new Comparator<Etat>() {
#Override
public int compare(Etat o1, Etat o2) {
return o1.getDateEtat().compareTo(o2.getDateEtat());
}
}));
}
Since the sort method your using has been added to the List interface in Java SE 8, I guess you're using Java SE 8. Then you can rewrite it to the following:
commandes.foreach(c ->
c.getEtatList().sort(Comparator.comparing(Etat::getDateEtat).reversed());
);

This should be what you need:
Comparator<Etat> comparator = new Comparator<Etat>() {
#Override
public int compare(Etat o1, Etat o2) {
return o1.getDateEtat().compareTo(o2.getDateEtat());
}
};
for (Commande c : commandes) {
Collections.sort(c.getEtatList(), comparator);
// or this one: Collections.sort(c.getEtatList(), Collections.reverseOrder(comparator));
}

This works as expected, your problem is somewhere else:
public static void main(String[] args) {
List<State> states = Arrays.asList(new State(2015, 1, 1),
new State(2014, 1, 1),
new State(2016, 1, 1));
System.out.println(states); //not ordered
states.sort(new Comparator<State>() {
#Override public int compare(State o1, State o2) {
return o1.getStateDate().compareTo(o2.getStateDate());
}
});
System.out.println(states); //ordered
}
public static class State {
private final LocalDate stateDate;
public State(int year, int month, int day) {
this.stateDate = LocalDate.of(year, month, day);
}
public LocalDate getStateDate() { return stateDate; }
#Override public String toString() { return stateDate.toString(); }
}
Note that you seem to be using Java 8 and your comparator can be written:
states.sort(comparing(State::getStateDate));

After days of struggle, I managed to find a solution.
The list isn't sorted after every attempt I made. I still don't know why.
But I found an annotation, #OrderBy, that sorts the list the way I want.
Thank you all for your help, maybe one day this problem will be sorted out (see the pun ? I am so funny).
Cheers

I appreciate your question, as I have just experienced this. I implemented 'Comparable' (as I have done many other times) on my JPA Entity class. When doing a Collections.sort on myMainJPA_Object.getMyList(), the overriden comparable method does not get invoked.
My work-around has been to create a new List as an ArrayList (for example), do a .addAll(myObject.getMyList()), then do Collections.sort on that new list, and then the sort works (my comparable method is invoked on the sort). For example:
List<ObjectsToSort> tempList = new ArrayList<>();
tempList.addAll(jpaEntity.getListOfStuff());
Collections.sort(tempList);
//Then you could set the list again
jpaEntity.setListOfStuff(tempList);
I really don't like this solution, but I don't know any other way around it, and haven't found anything about this problem (until your post). I liked your #OrderBy annotation suggestion, in my case though I need to re-sort again on a different method call, so this solution works for me.

Related

Bring Class Leader Id on top of the list using Java8 Streams

I have a list of students and I want to bring only the leaderId on top of the same list following remains the remaining list items using Java Streams. I tried the below logic but it is not working as expected
List<Student> students = ....
Long leaderId = 123;
students.stream().sort((s1,s2) -> leaderId.equals(s1.getId()) || leaderId.equals(s2.getId()) ? 1: 0).collect(Collectors.toList());
Can anyone please give me some suggestion on this
Example
List of students:
[{id:121, name:"John"}, {id:131, name:"Susan"}, {id:123, name:"Jacob"}, {id:155, name:"Sunny"}]
Expected Output
[{id:123, name:"Jacob"}, {id:121, name:"John"}, {id:131, name:"Susan"}, {id:155, name:"Sunny"}]
You have to write your own comparator first which places the leader at the front of the list when sorted using it. Then you have to use it for sorting the list. I don't see any point of using the streams here, since the iterative code looks more simple and readable. Here's how it looks in practice.
static class LeaderFirstComparator implements Comparator<Student> {
final long leaderId;
LeaderFirstComparator(long leaderId) {
this.leaderId = leaderId;
}
#Override
public int compare(Student o1, Student o2) {
if (o1.id == leaderId && o2.id != leaderId)
return -1;
else if (o1.id != leaderId && o2.id == leaderId)
return 1;
else
return 0;
}
}
And the client code:
students.sort(new LeaderFirstComparator(leaderId));
Update
If the first object is the leader, then it should come before the second, hence returning -1 according to the contract. Otherwise, if the second object is the leader, then the first one should come after it, hence 1. If nether of the objects are the leader or both are leaders, then the original ordering is preserved. Thus returning 0.
As per the below comment, you can further simplify it to this one liner:
students.sort(Comparator.comparing(s -> s.getId() != leaderId));
If the leader has a concrete id equals to 123, yo can get it using only a filter
List leaderStudents = students.stream().filter(s1 -> leaderId.equals(s1.getId()).collect(Collectors.toList());

Java 8 sort float values

I have a list of employees which had different experiences like
5.0,3.3,5.5,5.6,4.5 etc..
When I am trying to sort max to min experience by using Math.round it's giving the wrong result like:
5.6,5.0,5.5,5.3,4.5 etc..
I want the result like:
5.6,5.5,5.3,5.0,4.5 etc..
Here I used Collections.sort like:
Collections.sort(employeeList, new Comparator<Emp>() {
#Override
public int compare(Emp t, Emp t1) {
return Math.round(t.getExperience() - t1.getExperience()); // which giving wrong results
// return Float.compare(t.getExperience() - t1.getExperience()); // which is not working
}
});
Here t1.getExperience() will give you float result.
Math.round(t.getExperience() - t1.getExperience()) doesn't compare the two numbers, so I don't know what you were expecting to achieve.
You are supposed to use:
Collections.sort(employeeList, new Comparator<Emp>() {
#Override
public int compare(Emp t, Emp t1) {
return Float.compare(t1.getExperience(), t.getExperience());
}
});
Note that the parameters passed to Float.compare are in the opposite order compared to the parameters of the wrapping compare method, which will produce sorting by descending order.
You can use Comparator.comparing
employeeList.sort(Comparator.comparing(Employee::getExperience).reversed());
It will produce:
5.6
5.5
5.0
4.5
3.3

Object sorting using java comparator but with fixed value should be in last value in a sorted list

I have a MasterPayee object sorting based on Payee category code with alphabetical order now i need to get Other Services category code to be last in the sorted list
List after sorting applied
Financial and Insurance services
Government Sectors
Other Services
Telecommunications and Utilities
Transportation Services
Required list as follows
Financial and Insurance services
Government Sectors
Telecommunications and Utilities
Transportation Services
Other Services
Need to acheive Other Services as last in the list Following Comparator is using to sort the list
Collections.sort(masterPayees, getCategoryNameComparatorByMasterPayee());
private Comparator<MasterPayee> getCategoryNameComparatorByMasterPayee() {
Comparator<MasterPayee> categoryNameComparatorByMasterPayee = new Comparator<MasterPayee>() {
public int compare(MasterPayee o1, MasterPayee o2) {
return (((MasterPayee) o1).getPayee_category().toString()
.compareToIgnoreCase(((MasterPayee) o2).getPayee_category().toString()));
}
};
return categoryNameComparatorByMasterPayee;
}
Other Services should be always last in the sorted list
Try this:
Comparator<MasterPayee> categoryNameComparatorByMasterPayee = new Comparator<MasterPayee>(){
public int compare(MasterPayee o1, MasterPayee o2) {
if (((MasterPayee) o1).getPayee_category().toString().equalsIgnoreCase("Other Services") && ((MasterPayee) o1).getPayee_category().toString().equalsIgnoreCase(((MasterPayee) o2).getPayee_category().toString())) {
return 0;
}
else if (((MasterPayee) o1).getPayee_category().toString().equalsIgnoreCase("Other Services")) {
return 1;
}
else if (((MasterPayee) o2).getPayee_category().toString().equalsIgnoreCase("Other Services")) {
return -1;
}
else return (((MasterPayee) o1).getPayee_category().toString().compareToIgnoreCase(((MasterPayee) o2).getPayee_category().toString()));
}
};
It treats an object with "Other Services" always as "larger", thus making it appear at the end.
Create a constant map <Payee, Integer> and in the comparator use the value.
You can use guava'a Ordering if you know all values that may be sorted.
To create comparator you can speccify your values like this:
Ordering<String> ordering1 = Ordering.explicit("Financial and Insurance services","Government Sectors","Telecommunications and Utilities","Transportation Services","Other Services");
You may also provide List with your values as argument to Ordering.explicit().
If there is only a limited set of those elements I would write them as enum.
A name for the output text and an ordinal for the sorting. It's cleaner.
Another suggestion, if "Other Services" is always present, remove it from the list, do the sorting, and then add "Other Services" last. That way you can keep the sorting logic simple and add the exception separately.
If not always present, then you can look for it first, and then only add if it was present.
I think we can handle the logic gracefully by using a ternary expression.
private Comparator<MasterPayee> getCategoryNameComparatorByMasterPayee() {
Comparator<MasterPayee> categoryNameComparatorByMasterPayee = new Comparator<MasterPayee>() {
public int compare(MasterPayee o1, MasterPayee o2) {
String s1 = ((MasterPayee) o1).getPayee_category().toString();
String s2 = ((MasterPayee) o1).getPayee_category().toString();
boolean b1 = s1.equalsIgnoreCase("Other Services");
boolean b2 = s2.equalsIgnoreCase("Other Services");
return b1 ? (b2 ? 0 : 1) : (b2 ? -1 : s1.compareToIgnoreCase(s2));
}
};
return categoryNameComparatorByMasterPayee;
}
This avoids having code which is difficult to read, and therefore difficult to maintain. And if we need to change the logic here, we might only have to make minimal changes.
If the list of strings is fixed ordering is based on business logic instead of string value, then i recommend using EnumMap collections.
enum Industry{
FINANCE, GOVERNMENT, UTILITIES, TRANSPORT, OTHER
}
public class StreamEnumMap {
public static void main(String... strings){
Map<Industry, String> industryMap = new EnumMap<>(Industry.class);
industryMap.put(Industry.FINANCE, "Financial and Insurance services");
industryMap.put(Industry.GOVERNMENT,"Government Sectors");
industryMap.put(Industry.UTILITIES,"Telecommunications and Utilities");
industryMap.put(Industry.OTHER,"Other Services");
industryMap.put(Industry.TRANSPORT, "Transportation Services");
industryMap.values().stream().forEach(System.out::println);
}
}
This produces the results in the below order,
Financial and Insurance services
Government Sectors
Telecommunications and Utilities
Transportation Services
Other Services

Difference between Iterables.tryFind and FluentIterable.firstMatch in Guava

Is there any difference between?
MyObject myWantedObj = Iterables.tryFind(myListOfObjects, new Predicate<MyObject>() {
public boolean apply(MyObject myObj) {
return myObj.getSomeAttribute().equals(someFinalVariable);
}
}).orNull();
and
MyObject myWantedObj = FluentIterable.from(myListOfObjects).firstMatch(new Predicate<MyObject>() {
public boolean apply(MyObject myObj) {
return myObj.getSomeAttribute().equals(someFinalVariable);
}
}).orNull();
Iterables.tryFind and FluentIterable.firstMatch Javadoc are equals to:
Returns an Optional containing the first element in iterable that satisfies the given predicate, if such an element exists.
I missing something?
Iterables.tryFind() pre-dates FluentIterable.firstMatch() by quite a bit. If you're just doing a single operation (as in your example), it doesn't really matter which you use. We probably never would have created the Iterables class if we had created FluentIterable first (hindsight is 20/20).
The power of FluentIterable comes when you're chaining several functional-type steps together. For example:
FluentIterable
.from(database.getClientList())
.filter(activeInLastMonth())
.transform(Functions.toStringFunction())
.limit(10)
.toList();

TreeSet Comparator

I have a TreeSet and a custom comparator.
I get the values from server according to the changes in the stock
ex: if time=0 then server will send all the entries on the stock (unsorted)
if time=200 then server will send entries added or deleted after the time 200(unsorted)
In client side i am sorting the entries. My question is which is more efficient
1> fetch all entries first and then call addAll method
or
2> add one by one
there can be millions of entries.
/////////updated///////////////////////////////////
private static Map<Integer, KeywordInfo> hashMap = new HashMap<Integer, KeywordInfo>();
private static Set<Integer> sortedSet = new TreeSet<Integer>(comparator);
private static final Comparator<Integer> comparator = new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
int integerCompareValue = o1.compareTo(o2);
if (integerCompareValue == 0) return integerCompareValue;
KeywordInfo k1 = hashMap.get(o1);
KeywordInfo k2 = hashMap.get(o2);
if (null == k1.getKeyword()) {
if (null == k2.getKeyword())
return integerCompareValue;
else
return -1;
} else {
if (null == k2.getKeyword())
return 1;
else {
int compareString = AlphaNumericCmp.COMPARATOR.compare(k1.getKeyword().toLowerCase(), k2.getKeyword().toLowerCase());
//int compareString = k1.getKeyword().compareTo(k2.getKeyword());
if (compareString == 0)
return integerCompareValue;
return compareString;
}
}
}
};
now there is an event handler which gives me an ArrayList of updated entries,
after adding them to my hashMap i am calling
final Map<Integer, KeywordInfo> mapToReturn = new SubMap<Integer, KeywordInfo>(sortedSet, hashMap);
I think your bottleneck can be probably more network-related than CPU related. A bulk operation fetching all the new entries at once would be more network efficient.
With regards to your CPU, the time required to populate a TreeSet does not change consistently between multiple add()s and addAll(). The reason behind is that TreeSet relies on AbstractCollection's addAll() (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b27/java/util/AbstractCollection.java#AbstractCollection.addAll%28java.util.Collection%29) which in turn creates an iterator and calls multiple times add().
So, my advice on the CPU side is: choose the way that keeps your code cleaner and more readable. This is probably obtained through addAll().
In general it is less memory overhead when on being loaded alread data is stored. This should be time efficient too, maybe using small buffers. Memory allocation costs time too.
However time both solutions, in a separate prototype. You really have to test with huge numbers, as network traffic costs much too. That is a bit Test Driven Development, and adds to QA both quantitative statistics, as correctness of implementation.
The actual implementation is a linked list, so add one by one will be faster if you do it right. And i think in the near future this behaviour wont be change.
For your problem a Statefull comparator may help.
// snipplet, must not work fine
public class NaturalComparator implements Comparator{
private boolean anarchy = false;
private Comparator parentComparator;
NaturalComparator(Comparator parent){
this.parentComparator = parent;
}
public void setAnarchy(){...}
public int compare(A a, A b){
if(anarchy) return 1
else return parentCoparator.compare(a,b);
}
}
...
Set<Integer> sortedSet = new TreeSet<Integer>(new NaturalComparator(comparator));
comparator.setAnarchy(true);
sortedSet.addAll(sorted);
comparator.setAnarchy(false);

Categories

Resources