Which collection for sorted unique dataset? - java

I want to have a Collection with the following requirements:
1.) before inserting an element it checks if there is already a element with the same name, if there is, the element is not inserted
2.) after inserting, the collection is automatically sorted by the salary of the persons in the collection
I insert persons with name and salary as attributes.
Which collection does fit these requirements?

What about a TreeSet ?
You may need to use the constructor with a Comparator.

You can use TreeSet and implement Comparator to sort the needed proprty. See
this sample.
To discard the duplicate entry, you need to return 0 value;
public int compare(Empl e1, Empl e2) {
if(e1.getName().equals(e2.getName())) {
return 0;
}else if(e1.getSalary() > e2.getSalary()){
return 1;
}else if(e1.getSalary() == e2.getSalary() && (e1.getName().compareTo(e2.getName()) > 1)) {
return 1; // if the salary is equal, sort it based on name.
}
else {
return -1;
}
}

Louis is wrong!
A SortedSet would be the perfect.
You only have to implement the Comparable interface and compare the salary of the people.

it will be fine for the SortedSet, but don't forget to implements the interface COMPARABLE.

You can use a TreeSet over a HashSet, but Person has to implement Comparable interface (int compareTo(Person o) method) and override equals and hashCode methods to check the identity/unique based only on name field.
First create Person and implement/overrride methods :
public class Person implements Comparable<Person> {
public Person(String name, double salary) {
this.name = name;
this.salary = salary;
}
public Person() {
}
private String name;
private Double salary;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
#Override
public String toString() {
return "Person{" + "name=" + name + ", salary=" + salary + '}';
}
public int compareTo(Person o) {
return salary.compareTo(o.getSalary());
}
#Override
public int hashCode() {
int hash = 7;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
return true;
}
}
then create a HashSet and fill it with Person elements. The set will contain only elements with unique name attribute, that's done by using only name field in equals and hashCode methods.
To keep collection sorted - create a TreeSet using parameterized constructor - new TreeSet(Collection coll) , you have collection already populated with unique elements, so the sorting will take place during TreeSet initialization.
Here is the rest of snippet
public class TestPerson {
public static void main(String [] args){
Person p1 = new Person("first", 1000);
Person p2 = new Person("second", 2000);
Person p3 = new Person("third", 3000);
Person p4 = new Person("first", 4000);
Person p5 = new Person("second", 5000);
TreeSet<Person> personSet = new TreeSet<Person>();
personSet.add(p1);
personSet.add(p2);
personSet.add(p3);
personSet.add(p4);
personSet.add(p5);
for (Person person : personSet){
System.out.println(" === person element sorted : "+person);
}
HashSet<Person> personHashSet = new HashSet<Person>();
personHashSet.add(p1);
personHashSet.add(p2);
personHashSet.add(p3);
personHashSet.add(p4);
personHashSet.add(p5);
TreeSet<Person> treePersonSet = new TreeSet<Person>(personHashSet);
for (Person person : treePersonSet){
System.out.println(" === person element: "+person);
}
}
}

Related

Eliminate duplicate from List containing objects using collection in Java

I have created a student class with details studentId, StudentName and studentSemester. Then added a constructor to create new students. in the main method i have given the below.
Student s1 = new Student(101, "Ron", 1);
Student s2 = new Student(102, "Angela", 9);
Student s3 = new Student(103, "Hazel", 5);
Student s4 = new Student(104, "David", 3);
Student s5 = new Student(104, "Alan", 4);
Student s6 = new Student(103, "Hazel", 5);
Student s7 = new Student(101, "Ron", 1);
List<Student> reEvaluationDetails = new ArrayList<Student>();
reEvaluationDetails.add(s1);
reEvaluationDetails.add(s2);
reEvaluationDetails.add(s3);
reEvaluationDetails.add(s4);
reEvaluationDetails.add(s5);
reEvaluationDetails.add(s6);
reEvaluationDetails.add(s7);
from the list, i have to eliminate duplicates by using another collection. I have tried adding the elements to a LinkedHashset but it doesnt remove the duplicates. Could you please suggest any solutions.
Implement equals and hashCode methods in Strudent and the use Set,
class Student {
public String name;
public int id;
public int semester;
public Student(int id, String name, int semester) {
this.id = id;
this.name = name;
this.semester = semester;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id == student.id &&
semester == student.semester &&
Objects.equals(name, student.name);
}
#Override
public int hashCode() {
return Objects.hash(name, id, semester);
}
}
Then use Set,
LinkedHashSet<Student> students = new LinkedHashSet<>(reEvaluationDetails);
You should consider using distinct doc
reEvaluationDetails .stream().distinct().collect(Collectors.toList()).
The equals() method of you student class should be overriden to detect equality between Student object
converting the ArrayList to a HashSet effectively removes duplicates, if you need to preserve insertion order you can also use this
Set<String> reEvaluationDetails = new LinkedHashSet<>(list);
reEvaluationDetails.add(s1);
reEvaluationDetails.add(s2);
reEvaluationDetails.add(s3);
reEvaluationDetails.add(s4);
reEvaluationDetails.add(s5);
reEvaluationDetails.add(s6);
reEvaluationDetails.add(s7);
Use a Set.
A Set is a Collection that, unlike List, does not allow duplicates.
Set<Student> reEvaluationDetails = new HashSet<Student>();
reEvaluationDetails.add(s1);
reEvaluationDetails.add(s2);
reEvaluationDetails.add(s3);
reEvaluationDetails.add(s4);
reEvaluationDetails.add(s5);
reEvaluationDetails.add(s6);
reEvaluationDetails.add(s7);
And override the equals and hashcode:
class Student {
int id;
String name;
int whatever;
Student(int id, String name, int whatever) {
this.id = id;
this.name = name;
this.whatever = whatever;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id == student.id &&
whatever == student.whatever &&
Objects.equals(name, student.name);
}
#Override
public int hashCode() {
return Objects.hash(id, name, whatever);
}
}
When you check what's in the Set you'll see that there are no duplicates.
It's the most efficient way of doing it.
You can check it running here.

How to sort first name also in this same compare method [duplicate]

I have array of objects person (int age; String name;).
How can I sort this array alphabetically by name and then by age?
Which algorithm would you use for this ?
You can use Collections.sort as follows:
private static void order(List<Person> persons) {
Collections.sort(persons, new Comparator() {
public int compare(Object o1, Object o2) {
String x1 = ((Person) o1).getName();
String x2 = ((Person) o2).getName();
int sComp = x1.compareTo(x2);
if (sComp != 0) {
return sComp;
}
Integer x1 = ((Person) o1).getAge();
Integer x2 = ((Person) o2).getAge();
return x1.compareTo(x2);
}});
}
List<Persons> is now sorted by name, then by age.
String.compareTo "Compares two strings lexicographically" - from the docs.
Collections.sort is a static method in the native Collections library. It does the actual sorting, you just need to provide a Comparator which defines how two elements in your list should be compared: this is achieved by providing your own implementation of the compare method.
For those able to use the Java 8 streaming API, there is a neater approach that is well documented here:
Lambdas and sorting
I was looking for the equivalent of the C# LINQ:
.ThenBy(...)
I found the mechanism in Java 8 on the Comparator:
.thenComparing(...)
So here is the snippet that demonstrates the algorithm.
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
Check out the link above for a neater way and an explanation about how Java's type inference makes it a bit more clunky to define compared to LINQ.
Here is the full unit test for reference:
#Test
public void testChainedSorting()
{
// Create the collection of people:
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Dan", 4));
people.add(new Person("Andi", 2));
people.add(new Person("Bob", 42));
people.add(new Person("Debby", 3));
people.add(new Person("Bob", 72));
people.add(new Person("Barry", 20));
people.add(new Person("Cathy", 40));
people.add(new Person("Bob", 40));
people.add(new Person("Barry", 50));
// Define chained comparators:
// Great article explaining this and how to make it even neater:
// http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
// Sort the stream:
Stream<Person> personStream = people.stream().sorted(comparator);
// Make sure that the output is as expected:
List<Person> sortedPeople = personStream.collect(Collectors.toList());
Assert.assertEquals("Andi", sortedPeople.get(0).name); Assert.assertEquals(2, sortedPeople.get(0).age);
Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
Assert.assertEquals("Bob", sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
Assert.assertEquals("Bob", sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
Assert.assertEquals("Bob", sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
Assert.assertEquals("Dan", sortedPeople.get(7).name); Assert.assertEquals(4, sortedPeople.get(7).age);
Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3, sortedPeople.get(8).age);
// Andi : 2
// Barry : 20
// Barry : 50
// Bob : 40
// Bob : 42
// Bob : 72
// Cathy : 40
// Dan : 4
// Debby : 3
}
/**
* A person in our system.
*/
public static class Person
{
/**
* Creates a new person.
* #param name The name of the person.
* #param age The age of the person.
*/
public Person(String name, int age)
{
this.age = age;
this.name = name;
}
/**
* The name of the person.
*/
public String name;
/**
* The age of the person.
*/
public int age;
#Override
public String toString()
{
if (name == null) return super.toString();
else return String.format("%s : %d", this.name, this.age);
}
}
Using the Java 8 Streams approach, with method references on the getters...
// Create a stream...
var sortedList = persons.stream()
// sort it (does not sort the original list)...
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
// and collect to a new list
.collect(Collectors.toList());
Collection to an array ist also possible:
persons.stream()
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
.toArray(String[]::new);
And the Java 8 Lambda approach...
//Sorts the original list Lambda style
persons.sort((p1, p2) -> {
if (p1.getName().compareTo(p2.getName()) == 0) {
return p1.getAge().compareTo(p2.getAge());
} else {
return p1.getName().compareTo(p2.getName());
}
});
Lastly...
// This syntax is similar to the Streams example above, but sorts the original list!!!
persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
You need to implement your own Comparator, and then use it: for example
Arrays.sort(persons, new PersonComparator());
Your Comparator could look a bit like this:
public class PersonComparator implements Comparator<? extends Person> {
public int compare(Person p1, Person p2) {
int nameCompare = p1.name.compareToIgnoreCase(p2.name);
if (nameCompare != 0) {
return nameCompare;
} else {
return Integer.valueOf(p1.age).compareTo(Integer.valueOf(p2.age));
}
}
}
The comparator first compares the names, if they are not equals it returns the result from comparing them, else it returns the compare result when comparing the ages of both persons.
This code is only a draft: because the class is immutable you could think of building an singleton of it, instead creating a new instance for each sorting.
Have your person class implement Comparable<Person> and then implement the compareTo method, for instance:
public int compareTo(Person o) {
int result = name.compareToIgnoreCase(o.name);
if(result==0) {
return Integer.valueOf(age).compareTo(o.age);
}
else {
return result;
}
}
That will sort first by name (case insensitively) and then by age. You can then run Arrays.sort() or Collections.sort() on the collection or array of Person objects.
Guava's ComparisonChain provides a clean way of doing it. Refer to this link.
A utility for performing a chained comparison statement. For example:
public int compareTo(Foo that) {
return ComparisonChain.start()
.compare(this.aString, that.aString)
.compare(this.anInt, that.anInt)
.compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
.result();
}
You can do like this:
List<User> users = Lists.newArrayList(
new User("Pedro", 12),
new User("Maria", 10),
new User("Rafael",12)
);
users.sort(
Comparator.comparing(User::getName).thenComparing(User::getAge)
);
I would be careful when using Guava's ComparisonChain because it creates an instance of it per element been compared so you would be looking at a creation of N x Log N comparison chains just to compare if you are sorting, or N instances if you are iterating and checking for equality.
I would instead create a static Comparator using the newest Java 8 API if possible or Guava's Ordering API which allows you to do that, here is an example with Java 8:
import java.util.Comparator;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsLast;
private static final Comparator<Person> COMPARATOR = Comparator
.comparing(Person::getName, nullsLast(naturalOrder()))
.thenComparingInt(Person::getAge);
#Override
public int compareTo(#NotNull Person other) {
return COMPARATOR.compare(this, other);
}
Here is how to use the Guava's Ordering API: https://github.com/google/guava/wiki/OrderingExplained
Create as many comparators as necessary. After, call the method "thenComparing" for each order category. It's a way of doing by Streams. See:
//Sort by first and last name
System.out.println("\n2.Sort list of person objects by firstName then "
+ "by lastName then by age");
Comparator<Person> sortByFirstName
= (p, o) -> p.firstName.compareToIgnoreCase(o.firstName);
Comparator<Person> sortByLastName
= (p, o) -> p.lastName.compareToIgnoreCase(o.lastName);
Comparator<Person> sortByAge
= (p, o) -> Integer.compare(p.age,o.age);
//Sort by first Name then Sort by last name then sort by age
personList.stream().sorted(
sortByFirstName
.thenComparing(sortByLastName)
.thenComparing(sortByAge)
).forEach(person->
System.out.println(person));
Look: Sort user defined object on multiple fields – Comparator (lambda stream)
Use Comparator and then put objects into Collection, then Collections.sort();
class Person {
String fname;
String lname;
int age;
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public Person(String fname, String lname, int age) {
this.fname = fname;
this.lname = lname;
this.age = age;
}
#Override
public String toString() {
return fname + "," + lname + "," + age;
}
}
public class Main{
public static void main(String[] args) {
List<Person> persons = new java.util.ArrayList<Person>();
persons.add(new Person("abc3", "def3", 10));
persons.add(new Person("abc2", "def2", 32));
persons.add(new Person("abc1", "def1", 65));
persons.add(new Person("abc4", "def4", 10));
System.out.println(persons);
Collections.sort(persons, new Comparator<Person>() {
#Override
public int compare(Person t, Person t1) {
return t.getAge() - t1.getAge();
}
});
System.out.println(persons);
}
}
Or you can exploit the fact that Collections.sort() (or Arrays.sort()) is stable (it doesn't reorder elements that are equal) and use a Comparator to sort by age first and then another one to sort by name.
In this specific case this isn't a very good idea but if you have to be able to change the sort order in runtime, it might be useful.
You can use generic serial Comparator to sort collections by multiple fields.
import org.apache.commons.lang3.reflect.FieldUtils;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* #author MaheshRPM
*/
public class SerialComparator<T> implements Comparator<T> {
List<String> sortingFields;
public SerialComparator(List<String> sortingFields) {
this.sortingFields = sortingFields;
}
public SerialComparator(String... sortingFields) {
this.sortingFields = Arrays.asList(sortingFields);
}
#Override
public int compare(T o1, T o2) {
int result = 0;
try {
for (String sortingField : sortingFields) {
if (result == 0) {
Object value1 = FieldUtils.readField(o1, sortingField, true);
Object value2 = FieldUtils.readField(o2, sortingField, true);
if (value1 instanceof Comparable && value2 instanceof Comparable) {
Comparable comparable1 = (Comparable) value1;
Comparable comparable2 = (Comparable) value2;
result = comparable1.compareTo(comparable2);
} else {
throw new RuntimeException("Cannot compare non Comparable fields. " + value1.getClass()
.getName() + " must implement Comparable<" + value1.getClass().getName() + ">");
}
} else {
break;
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return result;
}
}
Arrays.sort(persons, new PersonComparator());
import java.util.Comparator;
public class PersonComparator implements Comparator<? extends Person> {
#Override
public int compare(Person o1, Person o2) {
if(null == o1 || null == o2 || null == o1.getName() || null== o2.getName() ){
throw new NullPointerException();
}else{
int nameComparisonResult = o1.getName().compareTo(o2.getName());
if(0 == nameComparisonResult){
return o1.getAge()-o2.getAge();
}else{
return nameComparisonResult;
}
}
}
}
class Person{
int age; String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Updated version:
public class PersonComparator implements Comparator<? extends Person> {
#Override
public int compare(Person o1, Person o2) {
int nameComparisonResult = o1.getName().compareToIgnoreCase(o2.getName());
return 0 == nameComparisonResult?o1.getAge()-o2.getAge():nameComparisonResult;
}
}
For a class Book like this:
package books;
public class Book {
private Integer id;
private Integer number;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "book{" +
"id=" + id +
", number=" + number +
", name='" + name + '\'' + '\n' +
'}';
}
}
sorting main class with mock objects
package books;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
Book b = new Book();
Book c = new Book();
Book d = new Book();
Book e = new Book();
Book f = new Book();
Book g = new Book();
Book g1 = new Book();
Book g2 = new Book();
Book g3 = new Book();
Book g4 = new Book();
b.setId(1);
b.setNumber(12);
b.setName("gk");
c.setId(2);
c.setNumber(12);
c.setName("gk");
d.setId(2);
d.setNumber(13);
d.setName("maths");
e.setId(3);
e.setNumber(3);
e.setName("geometry");
f.setId(3);
f.setNumber(34);
b.setName("gk");
g.setId(3);
g.setNumber(11);
g.setName("gk");
g1.setId(3);
g1.setNumber(88);
g1.setName("gk");
g2.setId(3);
g2.setNumber(91);
g2.setName("gk");
g3.setId(3);
g3.setNumber(101);
g3.setName("gk");
g4.setId(3);
g4.setNumber(4);
g4.setName("gk");
List<Book> allBooks = new ArrayList<Book>();
allBooks.add(b);
allBooks.add(c);
allBooks.add(d);
allBooks.add(e);
allBooks.add(f);
allBooks.add(g);
allBooks.add(g1);
allBooks.add(g2);
allBooks.add(g3);
allBooks.add(g4);
System.out.println(allBooks.size());
Collections.sort(allBooks, new Comparator<Book>() {
#Override
public int compare(Book t, Book t1) {
int a = t.getId()- t1.getId();
if(a == 0){
int a1 = t.getNumber() - t1.getNumber();
return a1;
}
else
return a;
}
});
System.out.println(allBooks);
}
}
I'm not sure if it's ugly to write the compartor inside the Person class in this case. Did it like this:
public class Person implements Comparable <Person> {
private String lastName;
private String firstName;
private int age;
public Person(String firstName, String lastName, int BirthDay) {
this.firstName = firstName;
this.lastName = lastName;
this.age = BirthDay;
}
public int getAge() {
return age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
#Override
public int compareTo(Person o) {
// default compareTo
}
#Override
public String toString() {
return firstName + " " + lastName + " " + age + "";
}
public static class firstNameComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.firstName.compareTo(o2.firstName);
}
}
public static class lastNameComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.lastName.compareTo(o2.lastName);
}
}
public static class ageComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.age - o2.age;
}
}
}
public class Test {
private static void print() {
ArrayList<Person> list = new ArrayList();
list.add(new Person("Diana", "Agron", 31));
list.add(new Person("Kay", "Panabaker", 27));
list.add(new Person("Lucy", "Hale", 28));
list.add(new Person("Ashley", "Benson", 28));
list.add(new Person("Megan", "Park", 31));
list.add(new Person("Lucas", "Till", 27));
list.add(new Person("Nicholas", "Hoult", 28));
list.add(new Person("Aly", "Michalka", 28));
list.add(new Person("Adam", "Brody", 38));
list.add(new Person("Chris", "Pine", 37));
Collections.sort(list, new Person.lastNameComperator());
Iterator<Person> it = list.iterator();
while(it.hasNext())
System.out.println(it.next().toString());
}
}

How to add object with unique field to Set

How can I fill a Set by objects with unique field ?
For example I have a class Person which has an unique field called name thus if I add to Set an object with duplicate name it should not be added.
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Set<Person> objList = new HashSet<Person>();
objList.add(new Person("John", "New York", "Accountant"));
objList.add(new Person("Bill", "London", "Manager"));
objList.add(new Person("John", "New York", "Driver"));// this object should not be added
for(Person o : objList){
System.out.println(o.name);//here should printed only John and Bill
}
}
}
class Person {
String name;//must be unique
String city;
String position;
public Person(String c_name, String c_city, String c_position){
this.name = c_name;
this.city = c_city;
this.position = c_position;
}
}
In other words, you mean the person's name defines it's identity. You can produce such a behavior by overriding the equals(Object) and hashCode methods to include only it:
public class Person {
// members, constructors, etc.
#Override
public boolean equals(Object other) {
if (!(other instanceof Person)) {
return false;
}
return name.equals(((Person)other).name);
}
#Override
public int hashCode() {
return name.hashCode();
}
}
You need to overrride the equals() and hashcode() methods of Object class in Person class to achive this.
Since you have not done that for HashSet all object of Person class is different and hashSet will allow you to add all Person.
Below is the code snippet for Person class for unique name only.
class Person{
String name;//must be unique
String city;
String position;
public Person(String c_name, String c_city, String c_position){
this.name = c_name;
this.city = c_city;
this.position = c_position;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
You can use UnifiedSetWithHashingStrategy from Eclipse Collections to define your own custom hashing strategy for your Set. The following code will work for your example.
HashingStrategy<Person> uniqueNameStrategy = new HashingStrategy<Person>() {
#Override
public int computeHashCode(Person person) {
return person.name.hashCode();
}
#Override
public boolean equals(Person person1, Person person2) {
return person1.name.equals(person2.name);
}
};
UnifiedSetWithHashingStrategy<Person> people =
UnifiedSetWithHashingStrategy.newSet(uniqueNameStrategy);
people.add(new Person("John", "New York", "Accountant"));
people.add(new Person("Bill", "London", "Manager"));
people.add(new Person("John", "New York", "Driver"));
people.each(person -> System.out.println(person.name + "/" + person.position));
// Bill/Manager
// John/Accountant
Note: I am a committer for Eclipse Collections .

Creating or Instiatiating an ArrayList of Objects for Each User

Alright so, I am building an online registration system for a university. It's a fairly basic system written in java so there's no database issue to worry about. My problem is this: I have a class of objects called Course. Each course has a list of attribute (id, time, instructor, etc.). Each user then, has an arraylist (or schedule if you will) of Course objects which they can add or remove. My question is how do I create an arraylist for each student/user? Would it be beneficial to have a separate arraylist of Courses like a catalog from which to choose from? Any advice on the subject would be of help. If you'd like to see an example of my code thus far let me know and I'll edit my post to include it.
public class Course {
private int courseId;
private String courseDes;
private String courseIns;
private int time;
public Course(int courseId, String courseDes, String courseIns, int time) {
courseId = this.courseId;
courseDes = this.courseDes;
courseIns = this.courseIns;
time = this.time;
}
No need to use maps; you've expressed the right relationship yourself: "Each user has an ArrayList". The way to express a has-a relationship is with instance fields:
public class Student {
private final List<Course> courses = new ArrayList<>();
//write methods that operate on courses, or make courses public
....
Representing courses as a Course object is simplest if you care about the properties of the courses in any way. If however you only need the know the course ID, or if you need to be storing a large amount of Students, you can save space by storing courses as integers or shorts and looking them up in a static table.
I would have three separate classes Courses, Student and Enrollment.
public class Course {
private int courseId;
private String courseDes;
private String courseIns;
private int time;
public Course(int courseId, String courseDes, String courseIns, int time) {
courseId = this.courseId;
courseDes = this.courseDes;
courseIns = this.courseIns;
time = this.time;
}
}
Student
public class Student {
private final int studentID;
private final String name;
private Set<Course> studentCourses;
public Student(int studentId, String name) {
this.name = name;
this.studentID = studentId;
}
public String getName(){
return this.name;
}
public int getStudentId(){
return this.studentID;
}
void addCourse(Course course) {
if(!studentCourses.contains(course)){
studentCourses.add(course);
}
else{
studentCourses.remove(course);
studentCourses.add(course);
}
}
#Override
public int hashCode() {
int hash = 7;
hash = 23 * hash + this.studentID;
hash = 23 * hash + (this.name != null ? this.name.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Student other = (Student) obj;
if (this.studentID != other.studentID) {
return false;
}
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
return true;
}
}
Enrollment
class Enrollment{
//This Map will group student with the same name
private Map<String, List<Student>> enrollment;
public Enrollment(Student student){
if(enrollment.containsKey(student.getName())){
enrollment.get(student.getName()).add(student);
}else
{
List<Student> newStudent = new ArrayList<Student>();
newStudent.add(student);
enrollment.put(student.getName(), newStudent);
}
}
public void addCourse(Student student, Course course){
try{
List<Student> studentSameName = enrollment.get(student.name);
for(Student studentEntry : studentSameName){
if(studentEntry.getStudentId() == student.getStudentId()){
studentEntry.addCourse(course);
}
}
}catch(NullPointerException e){
//student does not exist
//TODO Add Logic
}
}
public void removeStudent(Student student){
//TODO Add Logic
}
}

How to use Java comparator properly?

If I have the following class:
public class Employee {
private int empId;
private String name;
private int age;
public Employee(int empId, String name, int age) {
// set values on attributes
}
// getters & setters
}
How can I use comparator that compares by name, then age, then id?
You need to implement it so that it orders by preferred elements. That is, you need to compare by name, then if that comparison is equal, compare by age, etc. An example is listed below:
public class EmployeeComparator implements Comparator<Employee> {
#Override
public int compare(Employee e1, Employee e2) {
int nameDiff = e1.getName().compareTo(e2.getName());
if(nameDiff != 0) {
return nameDiff;
}
int ageDiff = e1.getAge() - e2.getAge();
if(ageDiff != 0) {
return ageDiff;
}
int idDiff = e1.getEmpId() - e2.getEmpId();
return idDiff;
}
}
Update
Came across this a moment ago: How to compare objects by multiple fields One of the answers linked to ComparatorChain which will invoke multiple comparators in sequence until a non-zero result is received from a comparator or all comparators are invoked. This should probably be your preferred solution.
Perhaps this (untested) implementation of Comparator#compare() will do the trick.
int compare(Employee e, Employee f)
{
int val = e.name.compareTo(f.name);
if(val == 0)
{
val = e.age - f.age;
if(val == 0)
{
val = e.empId - f.empId;
}
}
return val;
}
You can also implement the Comparable Interface in your class.
for example, something like this:
public class Employee implements Comparable<Employee>{
private int empId;
private String name;
private int age;
public Employee(int empId, String name, int age) {
// set values on attributes
}
// getters & setters
public int compareTo(Employee o) {
int ret = this.name.compareTo(o.name);
if(ret == 0)
ret = this.age - o.age;
if(ret == 0)
ret = this.empId - o.empId;
return ret;
}
}
so you don't have to implement a extra class to compare your Employees.
Implement it
public class Employee {
private int empId;
private String name;
private int age;
/**
* #param empId
* #param name
* #param age
*/
public Employee(int empId, String name, int age) {
super();
this.empId = empId;
this.name = name;
this.age = age;
}
/**
*
*/
public Employee() {
super();
// TODO Auto-generated constructor stub
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//Compare by name, age and then id
public static Comparator<Employee> COMPARE_EMPLOYEE = new Comparator<Employee>() {
public int compare(Employee one, Employee other) {
//Compare Name
if (one.getName().compareToIgnoreCase(other.getName()) == 0) {
//Compare age
if((one.getAge() - other.getAge()) == 0) {
// Now check with id is useless
// So directly return result of compare by id
return one.getEmpId() - other.getEmpId();
} else { //If age Not equal
return one.getAge() - other.getAge();
}
} else { //If name not equal
return one.getName().compareToIgnoreCase(other.getName());
}
}
};
}
Use :
List<Employee> contacts = new ArrayList<Employee>();
//Fill it.
//Sort by address.
Collections.sort(contacts, Employee.COMPARE_EMPLOYEE);
Read Sorting an ArrayList of Contacts , this must help you and you will get more ideas and different different types of use of Comparator.
guava ComparisonChain:
List<Employee> list = new ArrayList<Employee>();
//...
Collections.sort(list, new Comparator<Employee>(){
#Override
public int compare(Employee e1, Employee e2) {
return ComparisonChain.start()
.compare(e1.empId, e2.empId)
.compare(e1.name, e2.name)
.compare(e1.age, e2.age).result();
}});
Use this:
public class Test
{
public static void main(String[] args)
{
Employee emp1 = new Employee(2, "Tom", 20);
Employee emp2 = new Employee(1, "Tom", 20);
Employee emp3 = new Employee(3, "Hank", 21);
List<Employee> list = new ArrayList<>();
list.add(emp1);
list.add(emp2);
list.add(emp3);
Collections.sort(list, new Employee().new MyComparator());
System.out.println(list);
}
}
class Employee
{
private int empId;
private String name;
private int age;
public Employee()
{}
public Employee(int empId, String name, int age)
{
this.empId = empId;
this.name = name;
this.age = age;
}
class MyComparator implements Comparator<Employee>
{
#Override
public int compare(Employee e1, Employee e2)
{
if(e1.name.compareTo(e2.name) == 0)
{
if(((Integer)e1.age).compareTo(e2.age) == 0)
{
return ((Integer)e1.empId).compareTo(e2.empId);
}
else
{
return ((Integer)e1.age).compareTo(e2.age);
}
}
return e1.name.compareTo(e2.name);
}
}
#Override
public String toString()
{
return "Employee [empId=" + empId + ", name=" + name + ", age=" + age + "]";
}
}
The Comparator interface defines two methods: compare() and equals().
The compare() method, compares two elements for order:
int compare(Object obj1, Object obj2)
obj1 and obj2 are the objects to be compared. This method returns zero if the objects are equal. It returns a positive value if obj1 is greater than obj2. Otherwise, a negative value is returned.
By overriding compare(), you can alter the way that objects are ordered. For example, to sort in a reverse order, you can create a comparator that reverses the outcome of a comparison.
The equals() method, tests whether an object equals the invoking comparator: boolean equals(Object obj)
obj is the object to be tested for equality. The method returns true if obj and the invoking object are both Comparator objects and use the same ordering. Otherwise, it returns false.
Example:
import java.util.*;
class Dog implements Comparator<Dog>, Comparable<Dog> {
private String name;
private int age;
Dog() {
}
Dog(String n, int a) {
name = n;
age = a;
}
public String getDogName() {
return name;
}
public int getDogAge() {
return age;
}
// Overriding the compareTo method
public int compareTo(Dog d) {
return (this.name).compareTo(d.name);
}
// Overriding the compare method to sort the age
public int compare(Dog d, Dog d1) {
return d.age - d1.age;
}
}
public class Example {
public static void main(String args[]) {
// Takes a list o Dog objects
List<Dog> list = new ArrayList<Dog>();
list.add(new Dog("Shaggy", 3));
list.add(new Dog("Lacy", 2));
list.add(new Dog("Roger", 10));
list.add(new Dog("Tommy", 4));
list.add(new Dog("Tammy", 1));
Collections.sort(list); // Sorts the array list
for(Dog a: list) // printing the sorted list of names
System.out.print(a.getDogName() + ", ");
// Sorts the array list using comparator
Collections.sort(list, new Dog());
System.out.println(" ");
for(Dog a: list) // printing the sorted list of ages
System.out.print(a.getDogName() +" : "+ a.getDogAge() + ", ");
}
}
Check it out for more Java Comparator examples.

Categories

Resources