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
Related
As the title implies, I am trying to is groupingBy to group a list of objects by range of their "power levels". I am very new to Java Stream API, so I don't quite know my way around it. The code I have written won't compile and I'm just not certain why, I think I have the right idea, but it's not working. Any help would be greatly appreciated! You can find my failed attempt below.
This is the groupingBy attempt:
Map<BigDecimal, List<Employee>> ranges = ObjectList.stream()
.collect(Collectors.groupingBy(a -> {
if(a.powerLevel < 2500) { a.powerLevel = LOWER;}
else if(a.powerLevel >10000) {a.powerLevel = UPPER;}
else {a.PowerLevel = MIDDLE;}
}
));
This is my enum:
enum salaryRanges {
LOWER, MIDDLE, UPPER
}
PS:I tried to see if I could make an enum with ranges, but from what I've read that it not possible in Java.
I would recommended to move logic into different method that defines range
public static salaryRanges getRange(Employee employee) {
if(employee.powerLevel < 2500){
return salaryRanges.LOWER;
}
if(employee.powerLevel > 10000){
return salaryRanges.UPPER;
}
return salaryRanges.MIDDLE;
}
Then use Collectors.groupingBy
Map<salaryRanges,List<Employee>> res = list.stream()
.collect(Collectors.groupingBy(JavaMain::getRange));
You can also do it in another way as #Andreas suggested, by moving defining salary range logic into enum
enum salaryRanges {
LOWER, MIDDLE, UPPER
public static salaryRanges fromPowerLevel(int powerLevel){
if(powerLevel<2500){
return salaryRanges.LOWER;
}
if(powerLevel>10000){
return salaryRanges.UPPER;
}
return salaryRanges.MIDDLE;
}
}
And then calling that method using lambda expression
Map<salaryRanges,List<Employee>> res = list.stream()
.collect(Collectors.groupingBy(emp->salaryRanges.fromPowerLevel(emp.powerLevel)));
Below function creates a Map, gets the count of passengers where passengers are > minTrips. The code works completely fine. Please see below
fun List<Trip>.filter(minTrips : Int): Set<Passenger> {
var passengerMap: HashMap<Passenger, Int> = HashMap()
this.forEach { it: Trip ->
it.passengers.forEach { it: Passenger ->
var count: Int? = passengerMap.get(it)
if (count == null) {
count = 1
passengerMap.put(it, count)
} else {
count += 1
passengerMap.put(it, count)
}
}
}
val filteredMinTrips: Map<Passenger, Int> = passengerMap.filterValues { it >= minTrips }
println (" Filter Results = ${filteredMinTrips}")
return filteredMinTrips.keys
}
Even though this is written in Kotlin, it seems like the code was first written in Java and then converted over to Kotlin. If it was truly written in Kotlin I am sure this wouldnt have been so many lines of code. How can I reduce the lines of Code? What would be a more funtional approach to solve this? What function or functions can I use to extract the Passengers Set directly where Passengers are > minTrips? This is too much of a code and seems crazy. Any pointers would be helpful here.
One way you could do this is to take advantage of Kotlin's flatmap and grouping calls. By creating a list of all passengers on all trips, you can group them, count them, and return the ones that have over a certain number.
Assuming you have data classes like this (essential details only):
data class Passenger(val id: Int)
data class Trip(val passengers: List<Passenger>)
I was able to write this:
fun List<Trip>.frequentPassengers(minTrips: Int): Set<Passenger> =
this
.flatMap { it.passengers }
.groupingBy { it }
.eachCount()
.filterValues { it >= minTrips }
.keys
This is nice because it is a single expression. Going through it, we look at each Trip and extract all of its Passengers. If we had just done map here, we would have List<List<Passenger>>, but we want a List<Passenger> so we flatmap to achieve that. Next, we groupBy the Passenger objects themselves, and call eachCount() on the returned object, giving us a Map<Passenger, Int>. Finally we filter the map down the Passengers we find interesting, and return the set of keys.
Note that I renamed your function, List already has a filter on it, and even though the signatures are different I found it confusing.
You basically want to count the trips for each passenger, so you can put all passengers in a list and then group by them and afterwards count the occurences in each group:
fun List<Trip>.usualPassengers(minTrips : Int) = // 1
flatMap(Trip::passengers) // 2
.groupingBy { it } // 3
.eachCount() // 4
.filterValues { it >= minTrips } // 5
.keys // 6
Explanation:
return type Set<Passenger> can be inferred
this can be ommitted, a list of the form [p1, p2, p1, p5, ...] is returned
a Grouping is created, which looks like this [p1=[p1, p1], p2=[p2], ...]]
the number of occurences in each group will be counted: [p1=2, p2=1, ...]
all elementes with values which less than minTrips will be filtered out
all keys that are left will be returned [p1, p2, ...]
p1...pn are Passenger instances
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
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.
I'm trying to simulate a CPU scheduler. I have an ArrayList of a Process class I made. I'm trying to sort this array list by the arrival time of the process. I must be missing something, because when I print my ArrayList, nothing happens.
I've been looking through other users posts but I haven't found anything that made sense to me.
Here is my Comparator and call to sort:
class ArrivalTimeComparator implements Comparator<Process> {
#Override
public int compare(Process p1, Process p2) {
return (int) (p1.getArrivalTime()-p2.getArrivalTime());
}
}
Collections.sort(processArray, new ArrivalTimeComparator());
This code
(int)(p1.getArrivalTime()-p2.getArrivalTime())
may suffer from integer operation overflow, thus you can get odd results. If you're using Java 7, use
Integer.compare(p1.getArrivalTime(), p2.getArrivalTime()); //or Long.compare
If you're working with Java 6 or less:
return p1.getArrivalTime() > p2.getArrivalTime() ? 1 : p1.getArrivalTime() < p2.getArrivalTime() ? -1 : 0;