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 .
Related
I have a problem with Flux.mergeOrdered() method. I have a class Person:
static class Person {
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
//getters and setters
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
#Override
public int hashCode() {
return Objects.hash(age, name);
}
//toString()
}
Now I try to create some Persons, put them in different Fluxes, create comparator and with Flux.mergeOrdered() method get one ordered FLux:
Person alex = new Person(13,"Alex");
Person misha = new Person(12,"Misha");
Person oleg = new Person(14,"Oleg");
Person nikita = new Person(66,"Nikita");
Person dima = new Person(60,"Dima");
Person kolya = new Person(68,"Kolya");
Flux<Person> young = Flux.just(alex, misha, oleg);
Flux<Person> old = Flux.just(nikita, dima, kolya);
Comparator<Person> personComparator = Comparator.comparingInt(Person::getAge);
Flux.mergeOrdered(personComparator, young, old).subscribe(System.out::println);
My output:
Person{age=13, name='Alex'} Person{age=12, name='Misha'}
Person{age=14, name='Oleg'} Person{age=66, name='Nikita'}
Person{age=60, name='Dima'} Person{age=68, name='Kolya'}
Why the result Flux is not Ordered?
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.
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);
}
}
}
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
}
}
I have a map that looks like this: HashMap<Person, List<Items>>.
I want to remove a few of the items in the list, the specific items i want to remove are stored in another list. How could i remove the list-items in the map that match with the items in this other list in the most efficient way?
Here is a full example demonstrating your situation. Notice that hashcode and equals were implemented on the Item class. This is important because it equals will be used by the removeAll method to determine if the list of supplied items is equal to the list of possessed items.
Item.java
public class Item {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Item(String name) {
super();
this.name = name;
}
#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;
Item other = (Item) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Person.java
public class Person {
private String name;
public Person(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Application
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class StackTest {
public static void main(String[] args) {
Person person = new Person("Logan");
Person person2 = new Person("Jean");
Person person3 = new Person("Gambit");
Person person4 = new Person("Storm");
Item item1 = new Item("Claws");
Item item2 = new Item("Jacket");
Item item3 = new Item("Cards");
Item item4 = new Item("Cape");
List<Item> items = new ArrayList<Item>();
items.add(item1);
items.add(item2);
items.add(item3);
items.add(item4);
List<Item> loganItems = new ArrayList<Item>(items);
List<Item> jeanItems = new ArrayList<Item>(items);
List<Item> gambitItems = new ArrayList<Item>(items);
List<Item> stormItems = new ArrayList<Item>(items);
Map<Person, List<Item>> people = new HashMap<Person,List<Item>>();
people.put(person, loganItems);
people.put(person2, jeanItems);
people.put(person3, gambitItems);
people.put(person4, stormItems);
printMap(people);
List<Item> removeItems = new ArrayList<Item>();
Item rItem1 = new Item("Cards");
Item rItem2 = new Item("Jacket");
removeItems.add(rItem1);
removeItems.add(rItem2);
removeItem(people, person, removeItems);
printMap(people);
}
public static void removeItem(Map<Person,List<Item>> map, Person p, List<Item> items){
map.get(p).removeAll(items);
}
public static void printMap(Map<Person, List<Item>> map){
for(Entry<Person,List<Item>> entry:map.entrySet()){
System.out.println(entry.getKey().getName() + " items:");
for(Item item: entry.getValue()){
System.out.println(item.getName());
}
}
}
}
List already provides a method: List.removeAll(Collection<?> collection).
Of course, using a LinkedList would probably be preferable in this scenario as removing an element is a O(1) operation vs O(n) for an ArrayList.
HashMap<Person, List<Items>> map = // Your map
for(Person p:map.keySet()) {
map.get(p).removeAll(removeList);
}
This should work.