I have simple class
public class ActiveAlarm {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
}
and List<ActiveAlarm> con. How to sort in ascending order by timeStarted, then by timeEnded? Can anybody help? I know in C++ with generic algorithm and overload operator <, but I am new to Java.
Using Comparator
For Example:
class Score {
private String name;
private List<Integer> scores;
// +accessor methods
}
Collections.sort(scores, new Comparator<Score>() {
public int compare(Score o1, Score o2) {
// compare two instance of `Score` and return `int` as result.
return o2.getScores().get(0).compareTo(o1.getScores().get(0));
}
});
With Java 8 onwards, you can simply use lambda expression to represent Comparator instance.
Collections.sort(scores, (s1, s2) -> { /* compute and return int */ });
Either make ActiveAlarm implement Comparable<ActiveAlarm> or implement Comparator<ActiveAlarm> in a separate class. Then call:
Collections.sort(list);
or
Collections.sort(list, comparator);
In general, it's a good idea to implement Comparable<T> if there's a single "natural" sort order... otherwise (if you happen to want to sort in a particular order, but might equally easily want a different one) it's better to implement Comparator<T>. This particular situation could go either way, to be honest... but I'd probably stick with the more flexible Comparator<T> option.
EDIT: Sample implementation:
public class AlarmByTimesComparer implements Comparator<ActiveAlarm> {
#Override
public int compare(ActiveAlarm x, ActiveAlarm y) {
// TODO: Handle null x or y values
int startComparison = compare(x.timeStarted, y.timeStarted);
return startComparison != 0 ? startComparison
: compare(x.timeEnded, y.timeEnded);
}
// I don't know why this isn't in Long...
private static int compare(long a, long b) {
return a < b ? -1
: a > b ? 1
: 0;
}
}
JAVA 8 and Above Answer (Using Lambda Expressions)
In Java 8, Lambda expressions were introduced to make this even easier! Instead of creating a Comparator() object with all of it's scaffolding, you can simplify it as follows: (Using your object as an example)
Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted);
or even shorter:
Collections.sort(list, Comparator.comparingInt(ActiveAlarm ::getterMethod));
That one statement is equivalent to the following:
Collections.sort(list, new Comparator<ActiveAlarm>() {
#Override
public int compare(ActiveAlarm a1, ActiveAlarm a2) {
return a1.timeStarted - a2.timeStarted;
}
});
Think of Lambda expressions as only requiring you to put in the relevant parts of the code: the method signature and what gets returned.
Another part of your question was how to compare against multiple fields. To do that with Lambda expressions, you can use the .thenComparing() function to effectively combine two comparisons into one:
Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.timeStarted-a2.timeStarted
.thenComparing ((ActiveAlarm a1, ActiveAlarm a2) -> a1.timeEnded-a2.timeEnded)
);
The above code will sort the list first by timeStarted, and then by timeEnded (for those records that have the same timeStarted).
One last note: It is easy to compare 'long' or 'int' primitives, you can just subtract one from the other. If you are comparing objects ('Long' or 'String'), I suggest you use their built-in comparison. Example:
Collections.sort(list, (ActiveAlarm a1, ActiveAlarm a2) -> a1.name.compareTo(a2.name) );
EDIT: Thanks to Lukas Eder for pointing me to .thenComparing() function.
We can sort the list in one of two ways:
1. Using Comparator : When required to use the sort logic in multiple places
If you want to use the sorting logic in a single place, then you can write an anonymous inner class as follows, or else extract the comparator and use it in multiple places
Collections.sort(arrayList, new Comparator<ActiveAlarm>() {
public int compare(ActiveAlarm o1, ActiveAlarm o2) {
//Sorts by 'TimeStarted' property
return o1.getTimeStarted()<o2.getTimeStarted()?-1:o1.getTimeStarted()>o2.getTimeStarted()?1:doSecodaryOrderSort(o1,o2);
}
//If 'TimeStarted' property is equal sorts by 'TimeEnded' property
public int doSecodaryOrderSort(ActiveAlarm o1,ActiveAlarm o2) {
return o1.getTimeEnded()<o2.getTimeEnded()?-1:o1.getTimeEnded()>o2.getTimeEnded()?1:0;
}
});
We can have null check for the properties, if we could have used 'Long' instead of 'long'.
2. Using Comparable(natural ordering): If sort algorithm always stick to one property:
write a class that implements 'Comparable' and override 'compareTo' method as defined below
class ActiveAlarm implements Comparable<ActiveAlarm>{
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
public ActiveAlarm(long timeStarted,long timeEnded) {
this.timeStarted=timeStarted;
this.timeEnded=timeEnded;
}
public long getTimeStarted() {
return timeStarted;
}
public long getTimeEnded() {
return timeEnded;
}
public int compareTo(ActiveAlarm o) {
return timeStarted<o.getTimeStarted()?-1:timeStarted>o.getTimeStarted()?1:doSecodaryOrderSort(o);
}
public int doSecodaryOrderSort(ActiveAlarm o) {
return timeEnded<o.getTimeEnded()?-1:timeEnded>o.getTimeEnded()?1:0;
}
}
call sort method to sort based on natural ordering
Collections.sort(list);
In java8+ this can be written in single line as follows:
collectionObjec.sort(comparator_lamda) or comparator.comparing(CollectionType::getterOfProperty)
code:
ListOfActiveAlarmObj.sort((a,b->a.getTimeStarted().compareTo(b.getTimeStarted())))
or
ListOfActiveAlarmObj.sort(Comparator.comparing(ActiveAlarm::getTimeStarted))
public class ActiveAlarm implements Comparable<ActiveAlarm> {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
public int compareTo(ActiveAlarm a) {
if ( this.timeStarted > a.timeStarted )
return 1;
else if ( this.timeStarted < a.timeStarted )
return -1;
else {
if ( this.timeEnded > a.timeEnded )
return 1;
else
return -1;
}
}
That should give you a rough idea. Once that's done, you can call Collections.sort() on the list.
Since Java8 this can be done even cleaner using a combination of Comparator and Lambda expressions
For Example:
class Student{
private String name;
private List<Score> scores;
// +accessor methods
}
class Score {
private int grade;
// +accessor methods
}
Collections.sort(student.getScores(), Comparator.comparing(Score::getGrade);
Java-8 solution using Stream API:
A. When timeStarted and timeEnded are public (as mentioned in the requirement) and therefore do not (need to) have public getter methods:
List<ActiveAlarm> sorted =
list.stream()
.sorted(Comparator.comparingLong((ActiveAlarm alarm) -> alarm.timeStarted)
.thenComparingLong((ActiveAlarm alarm) -> alarm.timeEnded))
.collect(Collectors.toList());
B. When timeStarted and timeEnded have public getter methods:
List<ActiveAlarm> sorted =
list.stream()
.sorted(Comparator.comparingLong(ActiveAlarm::getTimeStarted)
.thenComparingLong(ActiveAlarm::getTimeEnded))
.collect(Collectors.toList());
If you want to sort the original list itself:
A. When timeStarted and timeEnded are public (as mentioned in the requirement) and therefore do not (need to) have public getter methods:
list.sort(Comparator.comparingLong((ActiveAlarm alarm) -> alarm.timeStarted)
.thenComparingLong((ActiveAlarm alarm) -> alarm.timeEnded));
B. When timeStarted and timeEnded have public getter methods:
list.sort(Comparator.comparingLong(ActiveAlarm::getTimeStarted)
.thenComparingLong(ActiveAlarm::getTimeEnded));
Guava's ComparisonChain:
Collections.sort(list, new Comparator<ActiveAlarm>(){
#Override
public int compare(ActiveAlarm a1, ActiveAlarm a2) {
return ComparisonChain.start()
.compare(a1.timestarted, a2.timestarted)
//...
.compare(a1.timeEnded, a1.timeEnded).result();
}});
We can use the Comparator.comparing() method to sort a list based on an object's property.
class SortTest{
public static void main(String[] args) {
ArrayList<ActiveAlarm> activeAlarms = new ArrayList<>(){{
add(new ActiveAlarm("Alarm 1", 5, 10));
add(new ActiveAlarm("Alarm 2", 2, 12));
add(new ActiveAlarm("Alarm 3", 0, 8));
}};
/* I sort the arraylist here using the getter methods */
activeAlarms.sort(Comparator.comparing(ActiveAlarm::getTimeStarted)
.thenComparing(ActiveAlarm::getTimeEnded));
System.out.println(activeAlarms);
}
}
Note that before doing it, you'll have to define at least the getter methods of the properties you want to base your sort on.
public class ActiveAlarm {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
public ActiveAlarm(String name, long timeStarted, long timeEnded) {
this.name = name;
this.timeStarted = timeStarted;
this.timeEnded = timeEnded;
}
public long getTimeStarted() {
return timeStarted;
}
public long getTimeEnded() {
return timeEnded;
}
#Override
public String toString() {
return name;
}
}
Output:
[Alarm 3, Alarm 2, Alarm 1]
Employee POJO Class
package in.ac.adit.oop.sort;
public class Employee {
private int id;
private String name;
private String department;
public int getId() {
return id;
}
public Employee() {
super();
}
public Employee(int id, String name, String department) {
super();
this.id = id;
this.name = name;
this.department = department;
}
#Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", department=" + department + "]";
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
Employee Class To Manage Employee
package in.ac.adit.oop.sort;
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String[] args) {
/*
* Create 10 Employee Object
*/
Employee emp1 = new Employee(1, "Nayan", "IT");
Employee emp2 = new Employee(2, "Siddarth", "CP");
Employee emp3 = new Employee(3, "Samarth", "AE");
Employee emp4 = new Employee(4, "Bhavesh", "CV");
Employee emp5 = new Employee(5, "Sam", "FT");
Employee emp6 = new Employee(6, "Keyur", "IT");
Employee emp7 = new Employee(7, "Bala", "ME");
Employee emp8 = new Employee(8, "Mitul", "ME");
Employee emp9 = new Employee(9, "Kamlesh", "EE");
Employee emp10 = new Employee(10, "Piyush", "EE");
/*
* List of Employee Object
*/
List<Employee> employeeList = new ArrayList<Employee>();
employeeList.add(emp1);
employeeList.add(emp2);
employeeList.add(emp3);
employeeList.add(emp4);
employeeList.add(emp5);
employeeList.add(emp6);
employeeList.add(emp7);
employeeList.add(emp8);
employeeList.add(emp9);
employeeList.add(emp10);
CustomObjectSort customObjectSort = new CustomObjectSort();
List<Employee> sortByDepartment = customObjectSort.sortByDepartment(employeeList);
/*
* Sorted By Department
*/
for (Employee employee : sortByDepartment) {
System.out.println(employee);
}
/*
* Sorted By Name
*/
List<Employee> sortByName = customObjectSort.sortByName(employeeList);
for (Employee employee : sortByName) {
System.out.println(employee);
}
/*
* Sorted By Id
*/
List<Employee> sortById = customObjectSort.sortById(employeeList);
for (Employee employee : sortById) {
System.out.println(employee);
}
}
}
Custom Sorting
package in.ac.adit.oop.sort;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CustomObjectSort {
public List<Employee> sortByName(List<Employee> employeeList) {
Collections.sort(employeeList, new Comparator<Employee>() {
#Override
public int compare(Employee employee1, Employee employee2) {
return employee1.getName().compareTo(employee2.getName());
}
});
return employeeList;
}
public List<Employee> sortByDepartment(List<Employee> employeeList) {
Collections.sort(employeeList, new Comparator<Employee>() {
#Override
public int compare(Employee employee1, Employee employee2) {
return employee1.getDepartment().compareTo(employee2.getDepartment());
}
});
return employeeList;
}
public List<Employee> sortById(List<Employee> employeeList) {
Collections.sort(employeeList, new Comparator<Employee>() {
#Override
public int compare(Employee employee1, Employee employee2) {
return employee1.getId() - employee2.getId();
}
});
return employeeList;
}
}
You can use Collections.sort and pass your own Comparator<ActiveAlarm>
In java you need to use the static Collections.sort method. Here is an example for a list of CompanyRole objects, sorted first by begin and then by end. You can easily adapt for your own object.
private static void order(List<TextComponent> roles) {
Collections.sort(roles, new Comparator() {
#Override
public int compare(Object o1, Object o2) {
int x1 = ((CompanyRole) o1).getBegin();
int x2 = ((CompanyRole) o2).getBegin();
if (x1 != x2) {
return x1 - x2;
} else {
int y1 = ((CompanyRole) o1).getEnd();
int y2 = ((CompanyRole) o2).getEnd();
return y2 - y1;
}
}
});
}
You can call Collections.sort() and pass in a Comparator which you need to write to compare different properties of the object.
As mentioned you can sort by:
Making your object implement Comparable
Or pass a Comparator to Collections.sort
If you do both, the Comparable will be ignored and Comparator will be used. This helps that the value objects has their own logical Comparable which is most reasonable sort for your value object, while each individual use case has its own implementation.
public class ActiveAlarm {
public long timeStarted;
public long timeEnded;
private String name = "";
private String description = "";
private String event;
private boolean live = false;
}
List<ActiveAlarm> con = new ArrayList<ActiveAlarm>();
Collections.sort(con , (a1, a2) -> a1.timeStarted.compareTo(a2.timeStarted));
Collections.sort(con , (a1, a2) -> a1.timeEnded.compareTo(a2.timeEnded));
The best and the easiest way to sort any list of objects in Java (Java 8 and above).
Lets sort a basket of fruits based on the property "fruitName"
Fruit POJO:
class Fruit
{
int price;
String fruitName;
public Fruit(int price, String fruitName) {
super();
this.price = price;
this.fruitName = fruitName;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
#Override
public String toString() {
return "Fruits [price=" + price + ", fruitName=" + fruitName + "]";
}
}
Now lets add fruits into a list and then sort it
List<Fruit> basketOfFruits = new ArrayList<>();
basketOfFruits.add(new Fruit(123, "oranges"));
basketOfFruits.add(new Fruit(45, "nectarine"));
basketOfFruits.add(new Fruit(369, "blueberries"));
basketOfFruits.add(new Fruit(248, "apple"));
basketOfFruits.add(new Fruit(968, "peaches"));
basketOfFruits.add(new Fruit(436, "grapes"));
basketOfFruits.add(new Fruit(596, "figs"));
//sorting by the property fruitName
Collections.sort(basketOfFruits, (f1, f2)->{return f1.getFruitName().compareTo(f2.getFruitName());});
You can now print the list (i.e basketOfFruits) and the fruits in the list would be sorted in ASCENDING order (lexicographically).
The output would look like this:
[Fruits [price=248, fruitName=apple], Fruits [price=369, fruitName=blueberries], Fruits [price=596, fruitName=figs], Fruits [price=436, fruitName=grapes], Fruits [price=45, fruitName=nectarine], Fruits [price=123, fruitName=oranges], Fruits [price=968, fruitName=peaches]]
Instead of Collections.sort(), Java streams can also be used (Java 8 and above). The following is the code using Java streams
List<Fruit> sortedFruits = basketOfFruits.stream().sorted( (f1, f2)->{return f1.getFruitName().compareTo(f2.getFruitName());}).collect(Collectors.toList());
here the list is sorted in the same manner as Collections.sort(), but the sorted items would be stored/collected in another list "sortedFruits". So, if we want to print the sorted items of the list, we need to print "sortedFruits" instead of "basketOfFruits" in this case
My case is:
class Person {
String id ;
String name;
String age;
}
List<Person> list1 = {p1,p2, p3};
List<Person> list2 = {p4,p5, p6};
I want to know if there is person in list1 that has the same name and age in list2 but don't mind about id.
What is best and fast way?
Define yourself a key object that holds and compares the desired properties. In this simple case, you may use a small list whereas each index corresponds to one property. For more complex cases, you may use a Map (using property names as keys) or a dedicated class:
Function<Person,List<Object>> toKey=p -> Arrays.asList(p.getName(), p.getAge());
Having such a mapping function. you may use the simple solution:
list1.stream().map(toKey)
.flatMap(key -> list2.stream().map(toKey).filter(key::equals))
.forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));
which may lead to poor performance when you have rather large lists. When you have large lists (or can’t predict their sizes), you should use an intermediate Set to accelerate the lookup (changing the task’s time complexity from O(n²) to O(n)):
list2.stream().map(toKey)
.filter(list1.stream().map(toKey).collect(Collectors.toSet())::contains)
.forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));
In the examples above, each match gets printed. If you are only interested in whether such a match exists, you may use either:
boolean exists=list1.stream().map(toKey)
.anyMatch(key -> list2.stream().map(toKey).anyMatch(key::equals));
or
boolean exists=list2.stream().map(toKey)
.anyMatch(list1.stream().map(toKey).collect(Collectors.toSet())::contains);
A simple way to do that is to override equals and hashCode. Since I assume the equality between Person must also consider the id field, you can wrap this instance into a PersonWrapper which will implement the correct equals and hashCode (i.e. only check the name and age fields):
class PersonWrapper {
private Person person;
private PersonWrapper(Person person) {
this.person = person;
}
public static PersonWrapper wrap(Person person) {
return new PersonWrapper(person);
}
public Person unwrap() {
return person;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
PersonWrapper other = (PersonWrapper) obj;
return person.name.equals(other.person.name) && person.age.equals(other.person.age);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + person.name.hashCode();
result = prime * result + person.age.hashCode();
return result;
}
}
With such a class, you can then have the following:
Set<PersonWrapper> set2 = list2.stream().map(PersonWrapper::wrap).collect(toSet());
boolean exists =
list1.stream()
.map(PersonWrapper::wrap)
.filter(set2::contains)
.findFirst()
.isPresent();
System.out.println(exists);
This code converts the list2 into a Set of wrapped persons. The goal of having a Set is to have a constant-time contains operation for better performance.
Then, the list1 is filtered. Every element found in set2 is kept and if there is an element left (that is to say, if findFirst() returns a non empty Optional), it means an element was found.
Brute force, but pure java 8 solution will be this:
boolean present = list1
.stream()
.flatMap(x -> list2
.stream()
.filter(y -> x.getName().equals(y.getName()))
.filter(y -> x.getAge().equals(y.getAge()))
.limit(1))
.findFirst()
.isPresent();
Here, flatmap is used to join 2 lists. limit is used as we are interested in first match only, in which case, we do not need to traverse further.
Well if you don't care about the id field, then you can use the equals method to solve this.
Here's the Person class code
public class Person {
private String id ;
private String name;
private String age;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person sample = (Person) o;
if (!name.equals(sample.name)) return false;
return age.equals(sample.age);
}
#Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + age.hashCode();
return result;
}
}
Now, you can use stream to get the intersection like so. common will contain all Person objects where name and age are the same.
List<Person> common = list1
.stream()
.filter(list2::contains)
.collect(Collectors.toList());
<h3>Find List of Object passing String of Array Using java 8?</h3>
[Faiz Akram][1]
<pre>
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
</pre>
// Main Class
<pre>
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class JavaLamda {
public static void main(String[] k)
{
List<Student> stud = new ArrayList<Student>();
stud.add(new Student("Faiz", 1));
stud.add(new Student("Dubai", 2));
stud.add(new Student("Akram", 5));
stud.add(new Student("Rahul", 3));
String[] name= {"Faiz", "Akram"};
List<Student> present = Arrays.asList(name)
.stream()
.flatMap(x -> stud
.stream()
.filter(y -> x.equalsIgnoreCase(y.getName())))
.collect(Collectors.toList());
System.out.println(present);
}
}
</pre>
OutPut //[Student#404b9385, Student#6d311334]
[1]: http://faizakram.com/blog/find-list-object-passing-string-array-using-java-8/
This would work:
class PresentOrNot { boolean isPresent = false; };
final PresentOrNot isPresent = new PresentOrNot ();
l1.stream().forEach(p -> {
isPresent.isPresent = isPresent.isPresent || l2.stream()
.filter(p1 -> p.name.equals(p1.name) && p.age.equals(p1.age))
.findFirst()
.isPresent();
});
System.err.println(isPresent.isPresent);
Since forEach() takes Consumer, we have no way of returning and PresentOrNot {} is a workaround.
Aside : Where did you get such a requirement ? :)
You need to iterate over the two lists and compare the atributtes.
for(Person person1 : list1) {
for(Person person2 : list2) {
if(person1.getName().equals(person2.getName()) &&
person1.getAge().equals(person2.getAge())) {
//your code
}
}
}
public static void main(String[] args) {
OTSQuestions ots = new OTSQuestions();
List<Attr> attrs = ots.getAttrs();
List<String> ids = new ArrayList<>();
ids.add("101");
ids.add("104");
ids.add("102");
List<Attr> finalList = attrs.stream().filter(
attr -> ids.contains(attr.getId()))
.collect(Collectors.toList());
}
public class Attr {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private List<Attr> getAttrs() {
List<Attr> attrs = new ArrayList<>();
Attr attr = new Attr();
attr.setId("100");
attr.setName("Yoga");
attrs.add(attr);
Attr attr1 = new Attr();
attr1.setId("101");
attr1.setName("Yoga1");
attrs.add(attr1);
Attr attr2 = new Attr();
attr2.setId("102");
attr2.setName("Yoga2");
attrs.add(attr2);
Attr attr3 = new Attr();
attr3.setId("103");
attr3.setName("Yoga3");
attrs.add(attr3);
Attr attr4 = new Attr();
attr4.setId("104");
attr4.setName("Yoga4");
attrs.add(attr4);
return attrs;
}
I have a list of bean coming from the db as a result of querying.
The bean is as follows:
public class Employee implements Comparator<Employee> {
protected String empId; //alphanumeric e.g.abc123
protected String empFullName;
protected String empAddress;
protected String dept;
protected String project;
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
public String getEmpFullName() {
return empFullName;
}
public void setEmpFullName(String empFullName) {
this.empFullName = empFullName;
}
public String getEmpAddress() {
return empAddress;
}
public void setEmpAddress(String empAddress) {
this.empAddress = empAddress;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String getProject() {
return project;
}
public void setProject(String project) {
this.project = project;
}
#Override
public int compare(Employee e1, Employee e2) {
if (e1 == null && e2 == null) return 0;
if (e1 != null && e2 == null) return -1;
if (e1 == null && e2 != null) return 1;
return e1.empId.compareTo (e2.empId);
}
}
I have comparator which sorts by empId which is alphanumeric.
I want to know what is the best way to sort it by empId,dept,project.
In the code if I do as follows, it sorts by empId.
List<Employee> empList = someDao.getEmpList();
Collections.sort(empList, new Employee());
Any suggestions.
This is an odd declaration in itself:
public class Employee implements Comparator<Employee>
It would be much more common to have:
public class Employee implements Comparable<Employee>
and then maybe:
public class EmployeeByIdComparator implements Comparator<Employee>
and
public class EmployeeByNameComparator implements Comparator<Employee>
etc.
An instance of a class implementing Comparable knows how to compare itself with another instance; typically this is reserved for a "natural" ordering. Compare that with an instance of Comparator, which knows how to compare two instances usually of a different type (the one specified as the type argument for Comparator).
So if you want multiple types of comparison, create multiple comparators. You may then want to chain them together - Guava provides an easy way of doing this. (See ComparisonChain.) For convenience, you may want to implement the comparators as private static classes within Employee, then expose single instances of them via public static final fields:
public class Employee
{
public static final Comparator<Employee> BY_ID_COMPARATOR
= new ByIdComparator();
private static final class ByIdComparator : Comparator<Employee>
{
...
}
}
public class Employee implements Comparator<Employee>
This is a wrong (probably not intended) use of the Comparator interface. I think you want Comparable here.
For sorting by empId, dept and project create a custom comparator that does just that and pass it to the sort() method. This comparator would then just check the properties you want to compare by in the order you need and return the result when it is not 0 for the first time - or at the end if all properties are equal.
Example:
new Comparator<Employee>() {
public int compare(Employee e1, Employee e2) {
//check if both are not null
int result = e1.empId.compareTo( e2.empId );
if( result == 0) {
result = e1.dept .compareTo( e2.dept );
}
...
return result;
}
}
I'm looking for a Collection type data structure to implement the following. Say I have a class like this:
class Person() {
String homeTown; // key
String sex; // key
String eyeColour; // key
String name;
long height;
// other stuff....
}
I am processing multiple Person objects. I want to organise them into sets whereby each set contains Person objects with the same homeTown, sex and eyeColour. At the moment I am implementing something like this:
Map<String, HashSet<Person>> = new HashMap<String, HashSet<Person>>;
where the key is a concatanation of the homeTown, sex and eyeColour. This works but seems a bit untidy - can anyone suggest a more elegant solution or a better data structure to use, thanks?
You could restructure your class to make the key explicit. This is more robust than simply concatenating the key values and avoids any additional object creation overhead at the point when you wish to store your Person instance in a Map, because you've eagerly created the key in advance.
public class Person {
public class Key {
private final String homeTown;
private final String sex;
private final String eyeColour;
public Key(String homeTown, String sex, String eyeColour) { ... }
public boolean equals(Object o) { /* Override to perform deep equals. */ }
public int hashCode() { /* Could pre-compute in advance if the key elements never change. */ }
}
private final Key key;
private final String name;
private final long height;
public Person(String homeTown, String sex, String eyeColour, String name, long height) {
this.key = new Key(homeTown, sex, eyeColour);
this.name = name;
this.height = height;
}
public Key getKey() {
return key;
}
public String getName() { return name; }
public long getHeight() { return height; }
}
Create an object to model your key. For example class PersonKey { String homeTown, sex, eyeColour } (getters and setters omitted for brevity)
Implement the equals and hashCode method for this object.
Use this object as the key in your Map.
Either remove the attributes from your Person object or replace them with a reference to your PersonKey object.
In addition, consider making the type of your map the following i.e. you don't need to specify what type of Set you are using as the key to your map.
Map<String, Set<Person>> = new HashMap<String, Set<Person>>();
And, if you are using a Set<Person> then you'll need to override equals and hashCode for the Person as well, otherwise the Set cannot correctly determine if two Person objects represent the same person or not, which is needed to make sure the collection contains only unique elements.
org.apache.commons.collections.map.MultiValueMap
You can use guava's Sets.filter method to filter the person objects.
Example:
Person class:
public class Person {
String name;
String hometown;
int age;
public Person(String name, String hometown, int age) {
this.name = name;
this.age = age;
this.hometown = hometown;
}
#Override
public int hashCode() {
int hash = 17;
hash = 37 * hash + name.hashCode();
hash = 37 * hash + hometown.hashCode();
hash = 37 * hash + age;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
Person p;
if (obj instanceof Person)
p = (Person) obj;
else
return false;
if (this.name.equals(p.name) && this.hometown.equals(p.hometown)
&& this.age == p.age)
return true;
return false;
}
#Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append("[name = ").append(name).append("\n");
b.append("home town = ").append(hometown).append("\n");
b.append("age = ").append(age).append("]");
return b.toString();
}
}
TestGuavaFilter class:
public class TestGuavaFilter {
public static void main(String[] args) {
Set<Person> set = new HashSet<Person>();
set.add(new Person("emil", "NY", 24));
set.add(new Person("Sam", "NY", 50));
set.add(new Person("george", "LA", 90));
System.out.println(Sets.filter(set, new FilterHomeTown("NY")));
}
}
class FilterHomeTown implements Predicate<Person> {
String home;
public FilterHomeTown(String home) {
this.home = home;
}
#Override
public boolean apply(Person arg0) {
if (arg0.hometown.equals(this.home))
return true;
return false;
}
}
The advantage of using a filter is that you can filter Person object in any way ,suppose you want to filter only using home-town and not the other 2 attributes this will helpful.More over since guava's filter only produces a view of the the real Set,you can save memory.