Java class object not equal even they are equal [duplicate] - java

This question already has answers here:
Why does this .equals() code example return "false"? [duplicate]
(3 answers)
Closed 2 years ago.
Hey i have two java class object, whose key and values are same but when I check ob1.equals(obj2) its return false.
here is the code :
Category expected = new Category("01","lorem","custom");
ResponseEntity<List<LinkedHashMap>> response = restTemplate.exchange("/api/categories", HttpMethod.GET,
null, new ParameterizedTypeReference<List<LinkedHashMap>>() {});
LinkedHashMap result = response.getBody().get(0); // which is same as expected object
//check if equals
private boolean areEqual(LinkedHashMap result, Category expected) {
String catId = (String) obj.get("category_id"); //is 01
String name = (String) obj.get("category_name"); // is lorem
String sec = (String) obj.get("section_name"); // is custom
DefaultCategory temp = new Category(catId, name, sec);
return temp.equals(expected); //<--------- returning false, even they are equal
}
The api return this category
#GetMapping("categories")
public ResponseEntity<List<Category>> getDefaultCategories() {
List<Category> categories = new ArrayList();
categories.add(new Category("01","lorem","custom"));
return new ResponseEntity<>(categories, HttpStatus.OK);
}

Standard .equals(...) checks two objects are same instance or not. If you want to compare two objects with their fields. You can override equals method like below.
public class DefaultCategory {
private String catId;
private String name;
private String sec;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DefaultCategory)) return false;
DefaultCategory other = (DefaultCategory) o;
if(!Objects.equals(other.catId, catId)) return false;
if(!Objects.equals(other.name, name)) return false;
if(!Objects.equals(other.sec, sec)) return false;
return true;
}
}

Because expected is a child class and temp is a super class or vice versa. You are comparing two different objects(types). Cast you temp to Category and you will be fine. Or override your equals to allow mixed type comparison.

Related

Java Set of type POJO not able to add elements

I've a data object class:
public class MyDataObject {
private String value;
private String text;
private Set<MyDataObject> child;
// Getter & setters
// Constructor
public MyDataObject(final String value, final String text) {
this.value = value;
this.text = text;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyDataObject that = (MyDataObject) o;
return value.equals(that.value) &&
text.equals(that.text) &&
Objects.equals(child, that.child);
}
#Override
public int hashCode() {
return Objects.hash(value, text, child);
}
}
I'm adding 2 child elements in one of my MyDataObject object.
Say:
MyDataObject myDataObject = new MyDataObject("USA", "United States");
Set<MyDataObject> childSet = new TreeSet<>(Comparator.comparing(MyDataObject::getText));
childSet.add(new MyDataObject("NY", "Bronx")); // This is added
childSet.add(new MyDataObject("NY", "Manhattan")); // This is not being added, returing false.
myDataObject.setChild(childSet);
I've overridden hashcode and equals method as well to consider child element.
What am I missing?
The issue you mentioned can be reproduced by Comparator.comparing(MyDataObject::getValue). Nevertheless, both the comparators Comparator.comparing(MyDataObject::getText) and Comparator.comparing(MyDataObject::getValue) are inconsistent with the equals. See the javadoc of TreeSet where this is mentioned.
final Comparator<MyDataObject> myDataObjectComparator = Comparator.comparing(MyDataObject::getValue);
final MyDataObject myDataObject1 = new MyDataObject("NY", "Bronx");
final MyDataObject myDataObject2 = new MyDataObject("NY", "Manhattan");
System.out.println(myDataObject1.equals(myDataObject2));
System.out.println(myDataObjectComparator.compare(myDataObject1, myDataObject2));
Note that the equals say that the objects are not equal while compareTo gives 0. For the same two objects.
Your Comparator is not consistent with equals.
That is: you have selected a Comparator for your Set that returns 0 in cases where your equals(Object) method returns false.
I am changed a little your code. childSet.add expect object but you send two strings. This resolve work for me.
MyDataObject myDataObject = new MyDataObject("USA", "United States");
Set<MyDataObject> childSet = new TreeSet<>(Comparator.comparing(MyDataObject::getText));
MyDataObject myDataObject1 = new MyDataObject("NY", "Bronx");
MyDataObject myDataObject2 = new MyDataObject("NY", "Manhattan");
childSet.add(myDataObject1); // This is added
childSet.add(myDataObject2); // This is not being added, returing false.
myDataObject.setChild(childSet);
myDataObject.getChild()
.stream()
.map(child -> child.getValue() + " " + child.getText())
.forEach(System.out::println);

Java 8 Filtering Custom Class Properties

I've been going through SO pages all morning trying to figure out the best way to attack my question:
What is the most efficient way to sort through an ArrayList, match on a name in the ArrayList with a name I'm pulling from a WebElement. I'm not experienced with Java, and wondering if this context it makes more sense to use HashTables, but I couldn't find an easily understandable answer on how to use them with multiple values per index:
My custom class:
public class KnowledgePermission {
public String name;
public String htmlType;
public Boolean isAllowed;
public KnowledgePermission(String name, String htmlType, Boolean isAllowed) {
this.name = name;
this.htmlType = htmlType;
this.isAllowed = isAllowed;
}
public String getName() {
return name;
}
public String getHtmlType() {
return htmlType;
}
public Boolean getIsAllowed() {
return isAllowed;
}
#Override
public boolean equals(Object obj) {
boolean result = false;
if(obj instanceof KnowledgePermission) {
KnowledgePermission otherPermission = (KnowledgePermission) obj;
result = (this.name == otherPermission.name);
}
return result;
}
#Override
public int hashCode() {
int result = 17;
result = 31 * result + name.hashCode();
result = 31 * result + htmlType.hashCode();
result = 31 * result + isAllowed.hashCode();
return result;
}
}
I'm able to use Java 8, so I've looked at filters, haven't been successful yet.
Here's the snippet after I've created a list using my class type.
What I'm trying to do is get the XPath of some browser page items, get its name via Selenium's WebDriver API, and for the one item I know should match in my permission list, access one of the other two properties - htmlType or isAllowed - and continue logic based off of that.
List<KnowledgePermission> permissionList = new ArrayList<KnowledgePermission>();
permissionList.add(new KnowledgePermission("checkbox1sName", "checkbox", true ));
permissionList.add(new KnowledgePermission("checkbox2sName", "checkbox", true ));
List<WebElement> checkboxes = driver.findElements(By.xpath("//*someXpathinfoHere//input[#type='checkbox']"));
// check the value of each checkbox and display
for(WebElement item : checkboxes) {
String elname = item.getAttribute("name");
Boolean hasBeenSelected = item.isSelected();
// find the permission in the list
System.out.println("filtering permissions list");
List<KnowledgePermission> currentPermission = permissionList.stream().filter(permission -> elname.equals(permission)).collect(Collectors.toList());
System.out.println(currentPermission);
}
All that prints out for each iteration of the loop is:
filtering permissions list
[]
So I'm guessing I'm not understanding filtering correctly here.
Any help and I'd be grateful!!
.filter(permission -> elname.equals(permission.getName()))
... is all you have to change
If elname can be null, change the order to
.filter(permission -> permission.getName().equals(elname))
Since instance.equals(null) returns false, not NullPointerException

Comparing An Entry In A Map With An Object

I have a Map in Java like so,
private HashMap<String, Object[][]> theMap;
Where the key is a String and the entry is going to be something along the line of,
theMap = new HashMap<>();
Object[][] theData = {
{Boolean.FALSE, "Text"}
};
theMap.put("Key1", theData);
Somewhere along the line I would like to check if an entry in the map is equivalent to another object. Currently I am doing it like this,
Object[][] tempData = {
{Boolean.FALSE, "Text"}
};
for(Object key: entries.keySet()) {
if(entries.get(key).equals(tempData)) {
entries.remove(key);
}
}
And it is not working.
I would prefer the comparison to be done with an object rather than with another map. I'm wondering what I'm doing wrong with this comparison here?
The reason you are not getting equality is that arrays inherit Object#equals() which is based on identity, not equality of contents. You could consider using java.util.Arrays.deepEquals(Object[], Object[]) to compare.
That is the answer to the immediate question. However, using a 2-dimensional array of Object to hold a boolean and a String is really bad code smell and indicates you need to encapsulate what you are putting in the array.
Identity vs Equivalence
Please make sure that you understand that by default the equals() method of Object checks on whether two object references are referring to the same object (identity), which is not what your code is checking.
Instead, your code is checking whether the two objects (the values you put on the map) are having the same value (equivalence).
Here are two articles about this topic:
What is the difference between identity and equality in OOP?
Overriding equals method in Java
In this particular problem of yours, I think the solution involves two steps:
Your tempData and theData does not seems to be an array
of elements of the same type (it does not appear to be a 2-dimensional
array either). Instead, it contains a Boolean value and then a
String value. In this case, I think you really should think
through what this thingy is and design a class for it (I am showing
an example below)
The class should override the equals() (and hashCode()) methods
so that you can use its equals() for equivalence checking.
Note also that your IDE (e.g. Eclipse) probably can generate a template for equals() and hashCode() for you.
Example: (here I assume your Boolean represents a condition, and your String represents a message)
class MyRecord {
private Boolean condition;
private String message;
public Boolean getCondition() {
return condition;
}
public void setCondition(Boolean condition) {
this.condition = condition;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((condition == null) ? 0 : condition.hashCode());
result = prime * result
+ ((message == null) ? 0 : message.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;
MyRecord other = (MyRecord) obj;
if (condition == null) {
if (other.condition != null)
return false;
} else if (!condition.equals(other.condition))
return false;
if (message == null) {
if (other.message != null)
return false;
} else if (!message.equals(other.message))
return false;
return true;
}
}

Using 'contains' method in a class array

I have a class like this:
public static class TiposDeHistorial
{
String CODIGO, TIPO;
public TiposDeHistorial()
{
}
public String getCODIGO()
{
return CODIGO;
}
public void setCODIGO(String CODIGO)
{
this.CODIGO = CODIGO;
}
public String getTIPO()
{
return TIPO;
}
public void setTIPO(String TIPO)
{
this.TIPO = TIPO;
}
}
and a list of it:
ArrayList<TiposDeHistorial> tiposHistorial;
So my question is: can I use tiposHistorial.contains(...) to search in a specific array field, CODIGO or TIPO, for example?
First of, you do not have an array but an ArrayList.
The contains method on a List operates with the equals method of it's stored elements (TiposDeHistorial in your case). Therefore the answer to your question is no.
Trying something like tiposHistorial.contains("a") will not work as there is a type mismatch: your list is of type TiposDeHistorial while you try to check for an element of String.
If you are using Java 8 you can use following code:
tiposHistorial.stream()
.filter(x -> "specific value for CODIGO".equals(x.getCODIGO()))
.findFirst()
.orElse(null);
It will return TiposDeHistorial object in the list containing specific CODIGO value or null otherwise.
As for your question: "contains" method just returns "true" or "false", not an object. Moreover it uses "equals" method of your object, so it will not help if you want to search using fields.
Contains method will return true only if your object equals with ur list elements objects.
You can try extending equals method and have your own criteria which can work for either CODIGO or TIPO.
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
test other = (test) obj;
if (CODIGO == null) {
if (other.CODIGO != null)
return false;
} else if (!CODIGO.equals(other.CODIGO))
return false;
return true;
}
The answers already given here are all correct, just if You don't know java streams, and would like to check if the list contains both some CODIGO and TIPO fields, for me the simplest solution would be:
ArrayList<TiposDeHistorial> tiposHistorial = new ArrayList<>();
//add elements to the list
String tipo = "TIPO"; // the TIPO value You are looking for in the list
String codigo = "CODIGO"; // the CODIGO value You are looking for in the list
boolean containsTipo = false;
boolean containsCodigo = false;
for (TiposDeHistorial element: tiposHistorial) {
if (!containsTipo && element.getTIPO().equals(tipo)) {
containsTipo = true;
}
if (!containsCodigo && element.getCODIGO().equals(codigo) ){
containsCodigo = true;
}
if (containsTipo && containsCodigo)
break;
}
By editing it just a bit, You may also find which elements of the array contain the values You are looking for, if that will be Your intention

CaseInsensitive Map Key Java [duplicate]

This question already has answers here:
Case insensitive string as HashMap key
(14 answers)
Closed 6 years ago.
I have a Map<String, Object> in which I need the String key to be case insensitive
Currently I am wrapping my String objects in a Wrapper class I've called CaseInsensitiveString the code for which looks like this:
/**
* A string wrapper that makes .equals a caseInsensitive match
* <p>
* a collection that wraps a String mapping in CaseInsensitiveStrings will still accept a String but will now
* return a caseInsensitive match rather than a caseSensitive one
* </p>
*/
public class CaseInsensitiveString {
String str;
private CaseInsensitiveString(String str) {
this.str = str;
}
public static CaseInsensitiveString wrap(String str) {
return new CaseInsensitiveString(str);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if(o.getClass() == getClass()) { //is another CaseInsensitiveString
CaseInsensitiveString that = (CaseInsensitiveString) o;
return (str != null) ? str.equalsIgnoreCase(that.str) : that.str == null;
} else if (o.getClass() == String.class){ //is just a regular String
String that = (String) o;
return str.equalsIgnoreCase(that);
} else {
return false;
}
}
#Override
public int hashCode() {
return (str != null) ? str.toUpperCase().hashCode() : 0;
}
#Override
public String toString() {
return str;
}
}
I was hoping to be able to get a Map<CaseInsensitiveString, Object> to still accept a Map#get(String) and return the value without having to do Map#get(CaseInsensitiveString.wrap(String)). In my testing however, my HashMap has returned null whenever I have tried to do this but it does work if I wrap the String before calling get()
Is it possible to allow my HashMap to accept both String and CaseInsensitiveString parameters to the get method and work in a caseInsensitive fashion regardless of if the String is wrapped or not, and if so, what am I doing wrong?
for reference my test code looks like this:
Map<CaseInsensitiveString, String> test = new HashMap<>();
test.put(CaseInsensitiveString.wrap("TesT"), "value");
System.out.println(test.get("test"));
System.out.println(test.get(CaseInsensitiveString.wrap("test")));
and returns:
null
value
You can do it like this:
Map<String, Object> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
See this question.
However, note the performance implications of using a TreeMap instead of a HashMap, as mentioned by Boris in the comments.
This is as expected:
See this:
Map<CaseInsensitiveString, String> test = new HashMap<>();
This line tells MAP to accept only CaseInsensitiveString objects, when you pass another object to the map it treats as unknown key and returns null.
You can get your required behavior by changing this to :
Map<Object, String> test = new HashMap<>();

Categories

Resources