To compare the different objects of the same class with their contents like jobTitleId, classificationId, deptId & classificationId was to be done and do some manipulations later using Set and Map. I was able to do that by simply overriding the equals and hashCode methods of Object class and was able to fetch the information (like in the following Map).
Map<LocationData, List<LocationData>>
The following is the class I used (its been shown to you so that it can be referred for my problem statement):
LocationData class
package com.astreait.bulkloader;
public class LocationData {
String locId, deptId, jobTitleId, classificationId;
#Override
public boolean equals(Object obj) {
LocationData ld = (LocationData)obj;
return this.deptId.equals(ld.deptId) && this.jobTitleId.equals(ld.jobTitleId) && this.classificationId.equals(ld.classificationId) &&
this.locId.equals(ld.locId);
}
#Override
public int hashCode() {
return deptId.hashCode() + jobTitleId.hashCode() + classificationId.hashCode() +locId.hashCode();
}
}
Problem:
I'm already known to which all fields of this object I need to make the comparison.
i.e I'm bound to use the variables named classificationId, deptId, jobTitleId & locId etc.
Need:
I need to customize this logic such that the fields Names (classificationId, deptId, jobTitleId & locId etc) can be pulled dynamically along with their values. So, as far as my understanding I made use of 2 classes (TableClass and ColWithData) such that the List of ColWithData is there in TableClass object.
I'm thinking what if I override the same two methods equals() & hashCode();
such that the same can be achieved.
TableClass class #1
class TableClass{
List<ColWithData> cwdList;
#Override
public boolean equals(Object obj) {
boolean returnVal = false;
// I need to have the logic to be defined such that
// all of the dynamic fields can be compared
return returnVal;
}
#Override
public int hashCode() {
int returnVal = 0;
// I need to have the logic to be defined such that
// all of the dynamic fields can be found for their individual hashCodes
return returnVal;
}
}
ColWithData class #2
class ColWithData{
String col; // here the jobTitleId, classificationId, deptId, locId or any other more fields info can come.
String data; // The corresponding data or value for each jobTitleId, classificationId, deptId, locId or any other more fields.
}
Please let me know if I'm proceeding in the right direction or I should make some any other approach. If it is ok to use the current approach then what should be performed in the equals and hashCode methods?
Finally I need to make the map as: (Its not the concern how I will make, but can be considered as my desired result from this logic)
Map<TableClass, List<TableClass>> finalMap;
EDIT I have been down voted. So, I made some modifications for my requirements again. (Please help me out solving this)
Using this class ColWithData is kind of ugly. You should be using a Map<String,String> :
package mypack;
import java.util.*;
public class TableClass {
/* HashMap containing your values:
map.put("locId", [data]);
...
*/
public Map<String,String> cwdMap;
public Map<String,String> getCwdMap() {
return cwdMap;
}
public void setCwdMap(Map<String,String> cwdMap) {
this.cwdMap = cwdMap;
}
#Override
public boolean equals(Object obj) {
TableClass tClass = (TableClass) obj;
for(String col: this.cwdMap.keyset()){
if (! tClass.cwdMap.get(col).equals(this.cwdMap.get(col)){
return false;
}
}
return true;
}
#Override
public int hashCode() {
int hCode = 0;
for(String col: this.cwdMap.keyset()){
hCode = hCode+cwdMap.get(col).hashCode();
}
return hCode;
}
}
In this code I never check for null values but your probably should.
There is another thing that confuse me in your code:
why use getter/setter if your property (cwdList) is public?
I think I have found the solution and its working for me.
Please let me know if there could be the simple or any other way out finding the solution for this problem.
The code snippet is:
package mypack;
import java.util.*;
public class TableClass {
public List<ColWithData> cwdList;
public List<ColWithData> getCwdList() {
return cwdList;
}
public void setCwdList(List<ColWithData> cwdList) {
this.cwdList = cwdList;
}
#Override
public boolean equals(Object obj) {
TableClass tClass = (TableClass) obj;
boolean returnVal = true;
for(ColWithData cwd: this.getCwdList()){
for(ColWithData innerCwd: tClass.getCwdList()){
if(cwd.getCol().equalsIgnoreCase(innerCwd.getCol())){
if(!cwd.getData().equalsIgnoreCase(innerCwd.getData()))
returnVal = false;
}
}
}
return returnVal;
}
#Override
public int hashCode() {
int hCode = 0;
for(ColWithData cwd: this.getCwdList()){
hCode = hCode+cwd.getData().hashCode();
}
return hCode;
}
}
And finally made a map as said:
Map<TableClass, List<TableClass>> map = new LinkedHashMap<TableClass, List<TableClass>>();
displaying the things as desired.
Related
I have a HashMap<CustomClass1, ArrayList<CustomClass2>> called map.
Is it possible to get the ArrayList<CustomClass2> using a String contained within CustomClass1? e.g. I would like to say map.get("zone1") and get the matching ArrayList. This is for a school project and I have to use the HashMap described above. This resembles my code:
public class Main {
public static void main(String [] args) {
HashMap<CustomClass1, ArrayList<CustomClass2>> map = new HashMap<CustomClass1, ArrayList<CustomClass2>();
CustomClass1 example = new CustomClass1("zone1");
map.put(example, new ArrayList<CustomClass2>());
//Later in the code where I don't have the objects in scope (except for map)...
ArrayList<CustomClass2> value = map.get("zone1");
}
class CustomClass1 {
private final String name;
//Additional variables
public CustomClass1 (String name) {
this.name = name;
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public boolean equals(Object obj) {
...
String test = (String) obj;
if(test.equals(this.name))
return true;
...
}
}
}
So far I have tried overriding the hashCode() and equals() methods in my CustomClass1 so that it uses the String's hashCode. From my understanding, when I call the map.get(obj) method, it calls obj.hashCode() to find the correct bucket and then obj.equals() to see if it is the correct object. I think I am wrong in thinking this, since I keep getting null values as a result and the equals() method in CustomClass1 is never called.
See comments for workaround/solution.
Summary Question: Do different instances of a sub-class inherit the same parent class instance?
I would have thought that two instances of a sub-class also have different parent class instances, but perhaps I am not understanding something about inheritance. Hopefully someone can explain why I am seeing this behavior.
Here is the class where I see the "problem":
#Entity
#Table(name="inventory.parts_fstnr_capscrews")
public class FastenerCapScrew implements PartInterface {
...
private Dimension length;
private Dimension threadLength;
...
#ManyToOne
#JoinColumn(name="fk_lengthid")
#JsonView(View.CommodityPartPOView.class)
public Dimension getLength() {
return length;
}
public void setLength(Dimension length) {
this.length = length;
}
#ManyToOne
#JoinColumn(name="fk_threadlengthid")
#JsonView(View.CommodityPartPOView.class)
public Dimension getThreadLength() {
return threadLength;
}
public void setThreadLength(Dimension threadLength) {
this.threadLength = threadLength;
}
#Override
#Transient
public List<FiltersInterface> getFilters() {
List<FiltersInterface> filters = new ArrayList<>();
LOGGER.debug(filters.toString());
LOGGER.debug(length.toString());
LOGGER.debug(threadLength.toString());
if (length!=null) {
length.setDbColumnName("FK_LengthID");
filters.add(length);
}
LOGGER.debug(filters.toString());
LOGGER.debug(length.toString());
LOGGER.debug(threadLength.toString());
if (threadLength!=null) {
threadLength.setDbColumnName("FK_ThreadLengthID");
filters.add(threadLength);
}
LOGGER.debug(filters.toString());
LOGGER.debug(length.toString());
LOGGER.debug(threadLength.toString());
return filters;
}
}
And here is the Dimension class:
#Entity
#Table(name="utilities.dimensions")
public class Dimension extends FiltersExtension implements FiltersDimensionInterface {
...
}
And the extended class:
public class FiltersExtension {
protected String dbColumnName;
public String getDbColumnName() {
return dbColumnName;
}
public void setDbColumnName(String dbColumnName) {
this.dbColumnName = dbColumnName;
}
}
When I call the getFilters() method in FastenersCapScrew, the initial output for length and threadLength is as expected, and both have dbColumnName=null. Then it runs length.setDbColumnName("FK_LengthID");, but both length and threadLength are changed and both show dbColumnName=FK_LengthID. Then it runs threadLength.setDbColumnName("FK_ThreadLengthID");, and again both items are changed so that dbColumnName=FK_ThreadLengthID.
Initially, I thought it must have something to do with the hashCode and equals methods in Dimension, so I changed them to include dbColumnName as below:
#Override
public int hashCode() {
LOGGER.debug("First compare hashCode with dbColumnName="+this.dbColumnName);
int hash = 3;
hash = 37 * hash + this.dimID;
hash = 37 * hash + Objects.hashCode(this.dbColumnName);
return hash;
}
#Override
public boolean equals(Object obj) {
LOGGER.debug("Now compare equals with dbColumnName="+this.dbColumnName);
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Dimension other = (Dimension) obj;
if (this.dimID != other.dimID) {
return false;
}
LOGGER.debug("Now compare the column name: "+this.dbColumnName+" vs. "+other.dbColumnName);
if (!Objects.equals(this.dbColumnName,other.dbColumnName)) {
return false;
}
return true;
}
Can anyone explain to me why changing one Dimension instance changes the other one as well? And what would be the way to fix this so that I do have two totally separate instances? Thanks!
For what it is worth, I am using Java 8 and Spring Boot 2.0.3 with Hibernate, but I don't think that has any bearing on this problem.
Definitely two instances of a sub-class do not share memory for their parent fields. Maybe the cause of this behavior is just a Hibernate's cache. Hibernate does create a new instance of Dimension for one of the fields of FastenerCapScrew class loading it from cache instead. Try to enable logging of SQL-queries to investigate what happens when you call getFilters method.
EDIT
The simplest way to get different instances of essentially the same entity is to use defensive copying in setters. As long as you do not apply this technique to collections Hibernate should still be able to perform dirty checking since it compares objects by value. In contrast collections are compared by identity and dirty checking will not work for them.
Using the Check class, add the code to have the Checks sorted by checkNumber.
import java.util.Date;
public class Check implements Comparable {
private int checkNumber;
private String payTo;
private Date date;
private float amount;
public int compareTo(Object arg0) {
//Insert code here
}
public int getCheckNumber() {
return checkNumber;
}
public void setCheckNumber(int checkNumber) {
this.checkNumber = checkNumber;
}
public String getPayTo() {
return payTo;
}
public void setPayTo (String payTo) {
this.payTo = payTo;
}
public Date getDate() {
return date;
}
public void setDate (Date date) {
this.date = date;
}
public float getAmount() {
return amount;
}
public void setAmount (float amount) {
this.amount = amount;
}
}
My solution is below, but it does not seem to work.
Can anyone help me with the solution?
public int compareTo(Object arg0) {
if(this.checkNumber == arg0.checkNumber)
return 0;
else
return this.checkNumber > arg0.checkNumber ? 1 : -1;
}
You didn't specify what you mean by "doesn't work", but reading your code, I'm assuming you get a compilation error.
The reason for this is because the code you were given has not specified a type for the comparable, so it doesn't know what type you even want to compare it to. It will use Object by default, which does not (by design) know a thing about Check's fields and methods.
The following modification is the best solution:
public class Check implements Comparable<Check> {
public int compareTo(Check arg0) {/* ...*/ }
}
This will force you to compare this to other Check's only and make arg0 a Check object, rendering its fields and methods available to you.
Should the parameters of the problem you were given not allow you to modify the provided code, then the (very very distant) second best solution is:
public int compareTo(Object arg0) {
Check other = null;
if(arg0 instanceof Check)
other = (Check)arg0;
// Other checks.
}
This adaptation would technically work for your problem given the stipulation that you are not allowed to modify the provided code, but is otherwise not at all recommended, as the contract for Comparable wants the type of object you wish to compare against to be specified, and not specifying it can introduce problems.
In fact, I'd say you are fully allowed to tell the person who gave you this problem that they are a terrible person for giving you a problem with this mistake in it, because not specifying a Comparable type is a really bad practice. Especially if they're teaching you how to program.
I'm using hibernate 4.3.10.
When refer to composite primary key, my entity looks like next (added equals() and hashCode() after getting the answer from #Master Slave) :
#Entity
#Table(name="compositepk")
public class Car {
#EmbeddedId
private CarPK carPK;
private String name;
public CarPK getCarPK() {
return carPK;
}
public void setCarPK(CarPK carPK) {
this.carPK = carPK;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Embeddable
public static class CarPK implements Serializable{
private static final long serialVersionUID = -5202331188724915048L;
private int chassisNumber;
private int engineNumber;
public int getChassisNumber() {
return chassisNumber;
}
public void setChassisNumber(int chassisNumber) {
this.chassisNumber = chassisNumber;
}
public int getEngineNumber() {
return engineNumber;
}
public void setEngineNumber(int engineNumber) {
this.engineNumber = engineNumber;
}
#Override
public boolean equals(Object obj) {
if(obj instanceof CarPK) {
CarPK car = (CarPK)obj;
if(this.getChassisNumber().intValue() == car.getChassisNumber().intValue() &&
this.getEngineNumber().intValue() == car.getEngineNumber().intValue()) {
return true;
} else {
return false;
}
}
return false;
}
#Override
public int hashCode() {
return this.chassisNumber.hashCode()+this.engineNumber.hashCode();
}
}
}
hibernate doc says we have to implements equals() and hashCode() in composite primary key.
However, I found there is not any problems without overriding them in CarPK. I can compare cars and add them to Set with right result. For instance, the following is comparison code :
Car.CarPK pk = new Car.CarPK();
pk.setChassisNumber(3);
pk.setEngineNumber(2017);
Car c1 = (Car) session1.get(Car.class, pk);
Car c2 = (Car) session2.get(Car.class, pk);
if(c1.equals(c2)) {
System.out.println("==");
} else {
System.out.println("!=");
}
This code prints "!=" and it prints "==" if chassisNumber and engineNumber are the same.
(After modifying the above code, get the same result in different session, the above code prints "!=", why? doesn't it should print out "==" because I have implemented the equals() and hashCode()?)
So can anyone show me the problems when I don't implements equals() and hashCode() in CarPK?
Thanks in advance!
Took me some time to figure it out, but you also must override equals and hashCode in Car
#Override
public boolean equals(Object obj) {
if(obj instanceof Car) {
Car that = (Car) obj;
return this.carPK.equals(that.carPK);
}
return false;
}
#Override
public int hashCode() {
return this.carPK.hashCode();
}
By the way, your hashCode's implementation in CarPK is dangerous (and false because it doesn't compile). It's too easy to generate 2 CarPK with the same hashCode but not equals
Car.CarPK pk1 = new Car.CarPK();
pk1.setChassisNumber(3);
pk1.setEngineNumber(2017);
Car.CarPK pk2 = new Car.CarPK();
pk2.setChassisNumber(1500);
pk2.setEngineNumber(520);
System.out.println(pk1.hashCode()); //prints 2020
System.out.println(pk2.hashCode()); //prints 2020
//Same hashCode, should be equal, just checking...
System.out.println(pk1.equals(pk2)); //prints false !
I recommand you this implementation where it's more difficult to generate collisions
#Override
public int hashCode() {
return Objects.hash(chassisNumber, engineNumber);
}
Your test works because you are in the same session/persistence context. Make a case where you try to load the entities in two different persistent context (again with the same values) and you'll realize that its a different java object.
That is why you must implement a business key equility, so hashCode and equals that will tell hibernate how to reason about the object equality and that should holde regardless of the state of the object (transient, attached, detached). Based on the above, an id property is not a good candidate for being the part of the hash/equals contracts as its value is state dependent
from the docs
Hibernate uses the Hibernate session to manage this uniqueness. When
you create an object with new(), and then save it into a session,
Hibernate now knows that whenever you query for an object and find
that particular object, Hibernate should return you that instance of
the object. And Hibernate will do just that. However, once you close
the Hibernate session, all bets are off. If you keep holding onto an
object that you either created or loaded in a Hibernate session that
you have now closed, Hibernate has no way to know about those objects.
So if you open another session and query for "the same" object,
Hibernate will return you a new instance. Hence, if you keep
collections of objects around between sessions, you will start to
experience odd behavior (duplicate objects in collections, mainly).
I got a class used in an Android app, which is declared like this:
public static class MyData implements Comparable<MyData>
{
public MyEnum myEnum;
#Override
public int compareTo(MyData another)
{
if(this.myEnum.equals(MyEnum.Value1))
{
return 1;
}
if(another.myEnum.equals(MyEnum.Value1))
{
return -1;
}
if(this.myEnum.equals(MyEnum.Value2))
{
return 1;
}
if(another.myEnum.equals(MyEnum.Value2))
{
return -1;
}
return 0;
}
}
I defined a list: List<MyData> myList = new LinkedList<MyData>();
After adding items to the list I call: Collections.sort(myList)
The problem is that when I debug, I see the compareTo method being called after the sort method is invoked, however it doesn't enter the first if even that it should. I even put the Boolean expression in Eclipse in the "Expressions" view and it returned true, but the code simply jumps to the return 0; and the list is not being sorted like I want to.
Why is that?
Eventually I changed that enum class member to an int member which was initialized with the ordinal value inside the Enum.
Then the compareTo method was changed like this:
#Override
public int compareTo(MyData another)
{
Integer myVal = this.intVal;
Integer otherVal = another.intVal;
return myVal.compareTo(otherVal);
}