I have a type of object which contains id and points information.
I have a list of that object. I need to add each new id, if any old id enters i need to add 1 to the points to the old entry .then add to the list.
Here is my pojo
public class Salary {
int id;
int points;
public Salary(int id, int points) {
this.id = id;
this.points = points;
}
// getters and setters
}
Here is the situation
class info{
public static void main(String[] args) {
List<Salary> salaries = new ArrayList<>();
salaries.add(new Salary(1,100));
salaries.add(new Salary(2,200));
Salary newSalary = new Salary(1,200);
}
}
Since the id is same i need to make 100 to 101 and update the list , how can i do that with java 8 ?
In your Salary class you should implement equals and hashcode as follows:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Salary other = (Salary) obj;
if (id != other.id)
return false;
return true;
}
and in your main method add this:
Salary newSalary = new Salary(1,200);
for(Salary s: salaries){
if(s.equals(newSalary)){
s.points++;
}
}
Related
I have 3 entities, student, grade and class. Code shows below. It is just a sample.
Student class
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private String fullName;
private long studentId;
//omit getter/setter column mapped to db
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getFullName() == null) ? 0 : getFullName().hashCode());
result = prime * result + (int) (getStudentId() ^ (getStudentId() >>> 32));
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (obj instanceof Student)
return false;
test other = (test) obj;
if (getFullName() == null) {
if (other.getFullName() != null)
return false;
} else if (!getFullName().equals(other.getFullName()))
return false;
if (getStudentId() != other.getStudentId())
return false;
return true;
}
}
SchoolClass class:
public class SchoolClass implements Serializable{
private static final long serialVersionUID = 1L;
private String className;
private long classId;
//omit getter/setter column mapped to db
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (getClassId() ^ (getClassId() >>> 32));
result = prime * result + ((getClassName() == null) ? 0 : getClassName().hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (obj instanceof SchoolClass)
return false;
SchoolClass other = (SchoolClass) obj;
if (getClassId() != other.getClassId())
return false;
if (getClassName() == null) {
if (other.getClassName() != null)
return false;
} else if (!getClassName().equals(other.getClassName()))
return false;
return true;
}
}
Grade Class:
public class Grade implements Serializable{
private static final long serialVersionUID = 1L;
private SchoolClass schoolClass;
private Student student;
//omit getter/setter column mapped to db
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getSchoolClass() == null) ? 0 : getSchoolClass().hashCode());
result = prime * result + ((getStudent() == null) ? 0 : getStudent().hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (obj instanceof Grade)
return false;
Grade other = (Grade) obj;
if (getSchoolClass() == null) {
if (other.getSchoolClass() != null)
return false;
} else if (!getSchoolClass().equals(other.getSchoolClass()))
return false;
if (getStudent() == null) {
if (other.getStudent() != null)
return false;
} else if (!getStudent().equals(other.getStudent()))
return false;
return true;
}
}
So I checked hibernate doc for hashcode and equals, it works perfectly fine for entity that exists in DB. The problem I have is for new transient entity objects before save to db. I did separate tests specifically on Student and SchoolClass using HashSet, the size of set won't increase if it tries to add same object.
Student s1 = studentRepo.findById(studentId).get();
SchoolClass sc = scRepo.findById(classId).get();
Grade grade = new Grade();
grade.setStudent(s1);
grade.setSchoolClass(sc);
grades.add(grade);
logger.info(grades.size());
Here I have a new Set of grades and preparing this set and save to db. Here comes the problem, this set will contains duplicate grade object. Meaning there will be 2 entries with same student and same class. In grade class, I override its hashcode and equals to Student and SchoolClass, it should NOT have duplicate entries. I figure it probably because the new Grade object is in transient state? Not really sure what is the cause.
Of course I can do unique check for grade in an manual way, but hashcode and equals should be the right way to go, isn't it?
So how to solve this? Need some help.
Thanks to #samabcde. He is right, I missed ! on the condition check.
There are four records in the above table; how can i get result which will have unique id and get the latest record sorted by creation time using Java 8 streams.
In this example; I want to see only two records like this:
6838322 45210 2018-03-08 06:07
and
6838320 45209 2018-03-08 05:50
yourObjects.stream()
.collect(Collectors.toMap(
YourObject::getId,
YourObject::getCreationTime,
BinaryOperator.maxBy(Comparator.comparing(Function.identity())
))
where YourObject is actually the object in java that you have and getCreationTime returns a Date that is Comparable.
You can sort the stream by creationtime, and apply equality of records on id (so that you can use distinct). Following is the code:
public class StreamStuff {
public static void main(String[] args) {
List<Data> datas = Arrays.asList(new Data(6838322, new Date()), new Data(6838320, new Date(1520574131111L)),
new Data(6838320, new Date(1520574136940L)), new Data(6838320, new Date(324324353999L)));
datas.stream()
.sorted((d1, d2) -> d2.creationTime.compareTo(d1.creationTime))
.distinct()
.forEach(System.out::println);
}
}
class Data {
int id;
Date creationTime;
public Data(int id, Date creationTime) {
super();
this.id = id;
this.creationTime = creationTime;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Data other = (Data) obj;
if (id != other.id)
return false;
return true;
}
#Override
public String toString() {
return "Data [id=" + id + ", creationTime=" + creationTime + "]";
}
}
I have a HashMap where the key is a class and value is an integer. I need to check if an object of the class already exists in the map. I use containsKey(), but for some reason it does not work when I include attribute sideDish in the equals() and hashCode(). Here is my code for the classes:
OrderItem class:
#ToString
#Entity
#Table(name="OrderItem")
public class OrderItem implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Getter #Setter
private Long id;
#ManyToOne
#Getter #Setter
private Food food;
#ManyToMany
#Getter #Setter
private List<SideDish> sideDishes;
public OrderItem() {}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((food == null) ? 0 : food.hashCode());
result = prime * result + ((sideDishes == null) ? 0 : sideDishes.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;
OrderItem other = (OrderItem) obj;
if (food == null) {
if (other.food != null)
return false;
} else if (!food.equals(other.food))
return false;
if (sideDishes == null) {
if (other.sideDishes != null)
return false;
} else if (!sideDishes.equals(other.sideDishes))
return false;
return true;
}
}
Food class:
#ToString
#Entity
#Table(name="Food")
public class Food implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Getter #Setter
private Long id;
#Column(nullable = false, unique = true)
#NotNull(message = "Name cannot be null.")
#Getter #Setter
private String name;
#ManyToMany
#Getter #Setter
private List<SideDish> sidedishes;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((foodtype == null) ? 0 : foodtype.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
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;
Food other = (Food) obj;
if (foodtype == null) {
if (other.foodtype != null)
return false;
} else if (!foodtype.equals(other.foodtype))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
SideDish class:
#Entity
#ToString(exclude= {"id","dishtype"})
#Table(name="SideDish")
public class SideDish implements Serializable, Comparable<SideDish>{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Getter #Setter
private Long id;
#Getter #Setter
#Column(nullable = false, unique = true)
private String name;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
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;
SideDish other = (SideDish) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
For some reason, if I removethe sideDish attribute from equals() and hashCode() in the OrderItem class, it works perfectly.
But I also need sideDish to be checked as part of the object identity.
Here is how I use it:
HashMap<OrderItem, Integer> orderItemsToSend = new HashMap<OrderItem, Integer>();
for (Order order : orders) {
for (OrderItem orderItem : order.getOrderItems()) {
int numSimilarOrders = getNumOfSimilarOrders(orderItem, orders);
if(!orderItemsToSend.containsKey(orderItem)) {
orderItemsToSend.put(orderItem, numSimilarOrders);
}else {
System.out.println("Vec je dodat item koji isti kao: " + orderItem.getFood().getName());
}
}
}
In your OrderItem class, both your hashCode() and equals() depend on the property List<SideDish> sideDishes.
Thus, if sideDishes changes, so does the hashCode() (and so does equality).
A HashMap uses both hashCode() and equals() to store and find the object which is the key. It uses a concept called "hash buckets". If you put a key into a HashMap, and then the hashCode() changes, that object will be in the wrong hash bucket, and you won't be able to find it again.
A key is something which is used for lookup purposes - that's what the word "key" means. An important quality of a key, whether in a database, or a hashmap, is immutability. So in Java, that means an object which changes its hashCode() makes for a bad key.
It's a bit like if a file system did lookups by the hash of the filename, but then you changed the filename, but it didn't update the hash. You'd only find the file by doing a lookup with the old name.
This simple test program will illustrate the point.
We store 2 objects in a HashMap, and then change the hashCode(). The map still contains both objects, but now one of them cannot be found or used for lookup.
The solution is use some simple immutable object as the key, such as a Long of its database ID.
Sample output is below the code.
public class HashTest {
static class Hashable {
String name;
#Override
public int hashCode() {
return ((name == null) ? 0 : name.hashCode());
}
#Override
public boolean equals(Object object) {
return (object instanceof Hashable) && equals((Hashable) object);
}
private boolean equals(Hashable that) {
return Objects.equals(this.name, that.name);
}
#Override
public String toString() {
// Use identityHashCode() so we can really see which object is which
return "[" + name + ":" + System.identityHashCode(this) + "]";
}
}
public static void main(String[] args) {
Hashable one = new Hashable();
one.name = "one";
Hashable two = new Hashable();
two.name = "one";
print(one, two);
two.name = "two";
print(one, two);
HashMap<Hashable, Integer> map = new HashMap<>();
map.put(one, 1);
map.put(two, 2);
find(map, one, two);
one.name = "two"; // Let's confuse things
print(one, two);
find(map, one, two);
}
private static void print(Hashable one, Hashable two) {
System.out.print("Names:" + one.name + ":" + two.name);
System.out.print("\tHashcodes:" + one.hashCode() + ":" + two.hashCode());
System.out.println("\tEquals:" + one.equals(two));
}
private static void find(HashMap<Hashable, Integer> map, Hashable one, Hashable two) {
System.out.print(map);
System.out.print("\tFound: " + map.get(one));
System.out.println("\tFound: " + map.get(two));
}
}
Sample output:
Names:one:one Hashcodes:110182:110182 Equals:true
Names:one:two Hashcodes:110182:115276 Equals:false
{[one:366712642]=1, [two:1829164700]=2} Found: 1 Found: 2
Names:two:two Hashcodes:115276:115276 Equals:true
{[two:366712642]=1, [two:1829164700]=2} Found: 2 Found: 2
I am trying to create a HashMap, that adds objects to a line, if they are not already present in this line. This is how I check it:
if (!waiting.containsKey(p)) {
waiting.put(current, p);
current++;
}
Where p is our object, which is stored with an Integer. However, when I run this code. It will store the same object several times under different integers, how can this be prevented?
thats because you call containsKey with the object and not the key:
parameter must be an Integer key
Integer lKey = 0;
if(!waiting.containsKey(lKey)){
waiting.put(current, p);
current++;
}
if your object has an identifier use this identifier for the map.
if(!waiting.containsKey(p.getId())){
waiting.put(p.getId(), p);
current++;
}
otherwise use containsValue():
if(!waiting.containsValue(p)){
waiting.put(current, p);
current++;
}
but then you have to overwrite the equals method.
If you want to use an object as a key, you can override the equals() and hashCode() methods to return and compare the id of the object.
Driver.java
import java.util.HashMap;
import java.util.Map;
public class Driver {
public static void main(String[] args) {
Map<MyObject, Integer> map = new HashMap<MyObject, Integer>();
map.put(new MyObject(1000L, "One"), 1);
map.put(new MyObject(1001L, "Two"), 2);
map.put(new MyObject(1002L, "Three"), 3);
Long id = 1001L;
System.out.println(contains(map, id)); // true
System.out.println(get(map, id)); // 2
}
public static <T, U> boolean contains(Map<T, U> map, T obj) {
return map.containsKey(obj);
}
public static boolean contains(Map<MyObject, Integer> map, Long id) {
return contains(map, new MyObject(id, ""));
}
public static <T, U> U get(Map<T, U> map, T obj) {
return map.get(obj);
}
public static Integer get(Map<MyObject, Integer> map, Long id) {
return get(map, new MyObject(id, ""));
}
}
MyObject.java
public class MyObject {
private Long id;
private String name;
protected Long getId() {
return id;
}
protected void setId(Long id) {
this.id = id;
}
protected String getName() {
return name;
}
protected void setName(String name) {
this.name = name;
}
public MyObject(Long id, String name) {
this.id = id;
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.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;
MyObject other = (MyObject) obj;
if (id == null) {
if (other.id != null) return false;
} else if (!id.equals(other.id)) return false;
return true;
}
#Override
public String toString() {
return "MyObject { id : " + id + ", name : " + name + "}";
}
}
I want to create a 2 TreeSets to hold sorted Employee Objects,one based on Employee id(int) and other based on Employee name(String).
I have overridden equals() and hashcode() and toString() in the Employee class shown below.
public class Employee {
private int id;
private String name;
#Override
public String toString() {
return "\nEmployee [id=" + id + ", name=" + name + "]";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
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;
Employee other = (Employee) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public Employee(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
I made 2 Comparators for the 2 TreeSets,
public class EmployeeNameComparator implements Comparator<Employee> {
#Override
public int compare(Employee o1, Employee o2) {
return o1.getName().compareTo(o2.getName());
}
}
public class EmployeeIdComparator implements Comparator<Employee>{
#Override
public int compare(Employee o1, Employee o2) {
if(o1.getId()<o2.getId())return -1;
else if(o1.getId()>o2.getId())return 1;
else return 0;
}
}
The Problem is the TreeSet which stores elements based on Id prints the output correctly ,but the one which stores the elements based on name does not print it as expected,
public class TreeSetTest {
public static void main(String[] args) {
//Employee Objects
Employee e1=new Employee(24, " bJohn");
Employee e2=new Employee(14, "aJonathan");
Employee e3=new Employee(4, "cJobs");
//put into a List and Print as it is
ArrayList<Employee> employees=new ArrayList<Employee>(3);
employees.add(e1);employees.add(e2);employees.add(e3);
System.out.println(employees);
//now create 2 Treesets with sorted id and Sorted Name
//the 2 comparators for name and Id
EmployeeNameComparator employeeNameComparator=new EmployeeNameComparator();
EmployeeIdComparator employeeIdComparator=new EmployeeIdComparator();
//creating the 2 tree Sets
TreeSet<Employee> employeeNameTree=new TreeSet<Employee>(employeeNameComparator);
employeeNameTree.addAll(employees);
TreeSet<Employee> employeesIdTree=new TreeSet<Employee>(employeeIdComparator);
employeesIdTree.addAll(employees);
//Printing both
System.out.println(employeeNameTree);
System.out.println(employeesIdTree);
}
}
Output:
[Employee [id=24, name= bJohn], Employee [id=14, name=aJonathan], Employee [id=4, name=cJobs]]
[Employee [id=24, name= bJohn], Employee [id=14, name=aJonathan], Employee [id=4, name=cJobs]]
[Employee [id=4, name=cJobs], Employee [id=14, name=aJonathan], Employee [id=24, name= bJohn]]
As you can see ,row 2 isn't sorted based on the name values.
My assumption all along is that the a.compareTo(b) in String class checks if String 'a' comes before or after String 'b' in the dictionary ie checks the alphabetical order , am i right ?, then why isn't the output as expected?