public Map<Object> map = new HashMap<Object>();
map.add(new Object ("a", "b"));
public class Object {
private String a;
private String b;
private String c;
public Object(String a, String b) {
this.a = a;
this.a = b;
this.c = "" + a + b + "";
}
public String getA() {
return a;
}
public String getB() {
return b;
}
public String getC() {
return c;
}
}
I have a hashmap, basically Object.getC() should be a + b in a string.
and in some other class, I need to get the c value and if that hashmap collection index has the exact same c value, it will delete the index:
public static void deleteChest() {
for (int i = 0; i < data.size(); i++) {
if (data.get(i).getItems().length == 0) {
Object c = new Object(data.get(i).getA(), data.get(i).getB());
map.remove(c);
}
}
}
data hashmap should have the same index number as the map hashmap, so if you're wondering what is it.
Basically, I loop through all data hashmap items (to get the A, B and then match if C is inside the other hashmap (map)).
Now the question time
How can I check if an object is contained (exists) or delete it (not by index, but by key)? How can I do this (Example above).
You should define the equals and hashCode method for your class.
Otherwise you fall back to the default implementation which checks for object identity, i.e. if the two references points to the same memory location instead of pointing to two objects containing the same data.
The first problem you have is that Map use key to retrieve value.
Set<Object> set = new HashSet();
Then when you are using Set or Map, your class must override hashcode and equals methods. If the do not do that the Set will not know how to properly compare them, to perform any operation.
This is why when you create a new Object(a,b,c) it set can not find them as it use the default value of hashcode with is not related to type members.
You should have to change the public Map<Object> map = new HashMap<Object>(); field to public Map<String,Foo> map = new HashMap<String,Foo>(); and you should change the name of your class from Object to Foo as 'mre' suggestion.
Foo Class
public class Foo {
private String a;
private String b;
private String c;
public Foo(String a, String b) {
this.a = a;
this.a = b;
this.c = getKey();
}
public String getA() {
return a;
}
public String getB() {
return b;
}
public String getC() {
return c;
}
public String getKey() {
return "" + a + b + "";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((a == null) ? 0 : a.hashCode());
result = prime * result + ((b == null) ? 0 : b.hashCode());
result = prime * result + ((c == null) ? 0 : c.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;
Foo other = (Foo) obj;
if (a == null) {
if (other.a != null)
return false;
} else if (!a.equals(other.a))
return false;
if (b == null) {
if (other.b != null)
return false;
} else if (!b.equals(other.b))
return false;
if (c == null) {
if (other.c != null)
return false;
} else if (!c.equals(other.c))
return false;
return true;
}
}
and you can add objects in map as follows
map.put(new Foo("a", "b").getKey(),new Foo("a", "b"));
now you deleteChest() method will be like
public static void deleteChest() {
Foo foo = null;
for (int i = 0; i < data.size(); i++) {
foo = map.get(new Foo(data.get(i).getA(), data.get(i).getB()).getKey())
if( foo != null) {
map.remove(foo.getKey());
}
}
}
hope this will solve your problem..
First, you code cannot be compiled for several reasons.
You called your class Object. FYI java.lang.Object is a base class for all classes. Since real class Object belongs to package java.lang you do not have to import it and therefore it is always available. I do not understand how compiler can solve this naming collision.
Interface Map has 2 parameters: K for key and V for value. Therefre line
public Map<Object> map = new HashMap<Object>();
cannot be compiled. Change it to
public Map<Object, Object> map = new HashMap<Object, Object>();
Or, better use real parameters instead of Object.
Map interface has method put that accepts 2 parameters. Therefore the following line cannot be compiled too.
map.add(new Object ("a", "b"));
Bottom line if you want to use map that associates groups of string use code like following:
Map<String, String> map = new HashMap<>();
map.put("a", "b");
To remove value by key use:
map.remove("a");
To retrieve value use String v = map.get("a")
To retrieve all values use either map.keySet() or map.entrySet().
The solution that work for me is:
map.remove("key");
Try this
if(map.containsKey(key)){
map.remove(map.get(key));
}
Related
Say I have 3 objects A, B, & C.
Object A is equal to object B & object C. But, objects B & C are NOT equal. Is it possible to have a hashcode() method that supports this concept ?
To give you a real example of this type of use case say I have this object structure
class MyObject {
String id;
// .. other fields
}
MyObject A has an id = "override".
MyObject B has an id = "1".
MyObject C has an id = "2".
The idea is that there is a special case whenever the id of "override" is found that it immediately overrides any & all other MyObject's because we only want that 1 single object. However, MyObject B & C don't have this "override" id therefore those objects should not be considered "equal".
Yes you can achieve this. In your equals method, just short-circuit with a check for "override".
if (this.id.equals("override")) return true;
Full example
public class Question_74158193 {
public static String OVERRIDE = "override";
static class MyObject {
private String id;
public MyObject(String id) { this.id = id; }
public MyObject() { this(OVERRIDE); }
public String getId() { return id; }
#Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + (id == null || id.equals(OVERRIDE) ? 0 : id.hashCode());
return hash;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (this.getClass() != o.getClass()) return false;
if (this.id.equals(OVERRIDE)) return true;
MyObject myObj = (MyObject) o;
return id.equals(myObj.id);
}
}
public static void main(String[] args) {
MyObject
a = new MyObject(),
b = new MyObject("1"),
c = new MyObject("2"),
d = new MyObject();
System.out.println(a.equals(b)); // true
System.out.println(a.equals(c)); // true
System.out.println(b.equals(c)); // false
System.out.println(a.hashCode()); // 217 - matches d
System.out.println(b.hashCode()); // 266
System.out.println(c.hashCode()); // 267
System.out.println(d.hashCode()); // 217 - matches a
}
}
I have a requirement where I have to aggregate a number of objects based on its properties. Object has around 10 properties and aggregation must be done on all its properties. For example -
If there are two objects A and B of some class C with properties p1, p2, p3,...p10, (all properties are of String type) then these two objects must be considered equal only if all its corresponding properties are equal.
For this I have two approaches in mind using HashMap in Java-
Approach 1 - Using key as Object of tyep C and Value as Integer for count and increase the count every time an existing object is found in Map otherwise create a new key value pair.
HahsMap<C, Integer>
But in this approach since I have to aggregate on all the properties, I will have to write(override) an equals() method which will check all the string properties for equality and similarly some implementation for hashCode().
Approach 2 - Using key as a single string made by concatenation of all the properties of object and value as a wrapper object which will have two properties one the object of type C and another a count variable of Integer type.
For each object(C) create an String key by concatenation of its properties and if key already exists in the Map, get the wrapper object and update its count property, otherwise create a new key, value pair.
HashMap<String, WrapperObj>
In this approach I don't have to do any manual task to use String as key and also it is considered a good practice to use String as key in Map.
Approach 2 seems easy to implement and efficient as opposed to Approach 2 every time when equals is called all the properties will be checked one by one.
But I am not sure whether Approach 2 in a standard way of comparing two objects and performing this kind of operation.
Please suggest if there is any other way to implement this requirement, like if there is any better way to implement equals() method for using it as key when all its properties should be taken into consideration when checking for equality of objects.
Example -
Class whose objects needs aggregation with hash and equals implementation in case of Approach 1
public class Report {
private String p1;
private String p2;
private String p3;
private String p4;
.
.
.
private String p10;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((p1 == null) ? 0 : p1.hashCode());
result = prime * result + ((p2 == null) ? 0 : p2.hashCode());
result = prime * result + ((p3 == null) ? 0 : p3.hashCode());
result = prime * result + ((p4 == null) ? 0 : p4.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Report))
return false;
Report other = (Report) obj;
if (p1 == null) {
if (other.p1 != null)
return false;
} else if (!p1.equals(other.p1))
return false;
if (p2 == null) {
if (other.p2 != null)
return false;
} else if (!p2.equals(other.p2))
return false;
if (p3 == null) {
if (other.p3 != null)
return false;
} else if (!p3.equals(other.p3))
return false;
if (p4 == null) {
if (other.p4 != null)
return false;
} else if (!p4.equals(other.p4))
return false;
.
.
.
if (p10 == null) {
if (other.p10 != null)
return false;
} else if (!p10.equals(other.p10))
return false;
return true;
}
}
Code For aggregation Approach 1-
Map<Report, Integer> map = new HashMap<Report, Integer>();
for(Report report : reportList) {
if(map.get(report) != null)
map.put(report, map.get(report)+1);
else
map.put(report, 1);
}
Approach 2 - With wrapper class and not implementing equals and hash for Report class.
public class Report {
private String p1;
private String p2;
private String p3;
private String p4;
public String getP1() {
return p1;
}
public void setP1(String p1) {
this.p1 = p1;
}
public String getP2() {
return p2;
}
public void setP2(String p2) {
this.p2 = p2;
}
public String getP3() {
return p3;
}
public void setP3(String p3) {
this.p3 = p3;
}
public String getP4() {
return p4;
}
public void setP4(String p4) {
this.p4 = p4;
}
Report warpper class -
public class ReportWrapper {
private Report report;
private Integer count;
public Report getReport() {
return report;
}
public void setReport(Report report) {
this.report = report;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
Code For aggregation Approach 2-
Map<String, ReportWrapper> map = new HashMap<String,
ReportWrapper>();
for(Report report : reportList) {
String key = report.getP1() + ";" + report.getP2() +
";" + report.getP3() +
";" + .....+ ";" + report.getP10();
ReportWrapper rw = map.get(key);
if(rw != null) {
rw.setCount(rw.getCount()+1);
map.put(key, rw);
}
else {
ReportWrapper wrapper = new ReportWrapper();
wrapper.setReport(report);
wrapper.setCount(1);
map.put(key, wrapper);
}
}
PSI: Here I am more concerned about, which approach is better.
Consider using the equals and hashcode methods that you can get generated from an IDE or use a tool like Lombok which will do it for you using an annotation and you don't have to write any code.
For lombok:
https://projectlombok.org/features/EqualsAndHashCode
How to use #EqualsAndHashCode With Include - Lombok
This is what IDEA generates if you want to go that route. No special process required.
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Report report = (Report) o;
return Objects.equals(prop1, report.prop1) &&
Objects.equals(prop2, report.prop2) &&
Objects.equals(prop3, report.prop3) &&
Objects.equals(prop4, report.prop4) &&
Objects.equals(prop5, report.prop5) &&
Objects.equals(prop6, report.prop6) &&
Objects.equals(prop7, report.prop7) &&
Objects.equals(prop8, report.prop8) &&
Objects.equals(prop9, report.prop9);
}
#Override
public int hashCode() {
return Objects.hash(prop1, prop2, prop3, prop4, prop5, prop6, prop7, prop8, prop9);
}
I am trying to remove an instance from my linked list however when i try searching for the object in the list it returns a value of -1 because it says its not there. what am i doing wrong. my application class is below and that calls the methods in my DataSet class
public class Application {
public static void main(String[] args) {
DataSet<String, Integer> db = new DataSet<>();
db.add("Theo", 4);
db.add("Maria", 5);
db.add("Adam", 4);
db.add("James", 5);
db.add("Charles", 7);
db.add("Nikki", 5);
db.add("Lynne", 5);
db.add("Kendal", 6);
db.add("Kerry", 5);
db.add("Janet", 5);
db.add("Gordon", 6);
db.add("Stepher", 7);
db.add("Sue", 3);
db.add("Ed", 2);
db.add("Adam", 4);
db.displayItems();
/*
System.out.println();
db.sortByFirst();
db.displayItems();
System.out.println();
db.sortBySecond();
db.displayItems();
System.out.println();
(db.findBySecond(5)).displayItems();
System.out.println();
(db.findByFirst("Adam")).displayItems();
System.out.println();
*/ System.out.println(db.remove("Adam", 4));
db.displayItems();
//System.out.println("size = " + db.size());
}
}
and the dataset is:
import java.util.LinkedList;
/**
*
* #param <T>
* #param <S>
*/
public class DataSet<T, S> {
LinkedList<Pair> datastructure = new LinkedList<>();
// Adds a new instance/item to the data structure.
public void add(T first, S second) {
Pair p = new Pair(first, second);
datastructure.add(p);
}
// Displays all itmes in the data structure.
public void displayItems() {
for (int i = 0; i < datastructure.size(); i++) {
System.out.println(datastructure.get(i));
}
}
// Removes all instances with matching criteria (first and second attribute values) and returns the number of instances removed.
public int remove(T first, S second) {
int count = 0;
Pair p = new Pair(first, second);
for (Pair datastructure1 : datastructure) {
Integer num = datastructure.indexOf(p);
System.out.println(num);
Boolean removed = datastructure.remove(p);
System.out.println(removed);
}
//will return count of how many removed
return count;
}
}
and the final class is the pair class
class Pair<T,S> {
private T first;
private S second;
public Pair(T theFirst, S theSecond) {
first = theFirst;
second = theSecond;
}
public T getFirst() {
return first;
}
public S getSecond() {
return second;
}
#Override
public String toString() {
return "(" + first + ", " + second + ")";
}
}
Like Adam pointed out the problem is that you're creating a new pair that is not in the list. What you want to do is to create an equals method in your Pair class and then iterate through your list comparing the elements using this equals method. The method should look like this:
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Pair other = (Pair) obj;
if (this.first != other.first) {
return false;
}
if (this.second != other.second) {
return false;
}
return true;
}
public int remove(T first, S second) {
int count = 0;
Pair p = new Pair(first, second);
for (Pair datastructure1 : datastructure) {
Integer num = datastructure.indexOf(p);
System.out.println(num);
Boolean removed = datastructure.remove(p);
System.out.println(removed);
}
//will return count of how many removed
return count;
}
In above remove method, you are creating a new Pair object. New object means new reference, So datastructure.indexOf(p) will always result -1.
Example:
datastructure contains three pairs:
Pair1 - reference 0x00000001 - "Theo",4
Pair2 - reference 0x00000002 - "Theo",5
Pair3 - reference 0x00000003 - "Theo",6
And we asked to remove "Theo",4. So `p` will be a new object like:
p - reference 0x00000004 - "Theo",4
This means that the reference of p will not match and it will not check the data. Modify the equals method of Pair class as follows:
#Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if(obj instanceof Pair)
{
Pair pair = (Pair)obj;
if(pair.first.equals(this.first) && pair.second.equals(this.second)){
return true;
}
}
return false;
}
I have two list of Student Objects(listA & listB) which were formed by querying from two different databases. I need to iterate one list and need to make sure it is not present in the other list.
I have used the below comparison code for the same i.e overwritten the equals method and compared using for loops.
Say List A & List B could have 5000 rows each, can you suggest if there are better ways to implement this?
Comparison code:
for (Student dataA:listA) {
for (Student dataB:listB) {
if(dataB.equals(dataA))
break;
}
}
Student Object:
public class Student {
int A;
int B;
String C;
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
Student student = (Student) obj;
return A == student.A && B == student.B && C.equals(student.C);
}
}
Edit Note: ListA & ListB can have different number of rows
I would suggest you the retainAll method:
List<Student> listC = new ArrayList();
listC.addAll(listA);
listC.retainAll(listB); //now listC contains all double students
But you should still override the equals method
You could use containsAny method from CollectionUtils (Apache Commons):
if(CollectionUtils.containsAny(listA, listB)){
break;
}
The removeAll command is the way to go, but List lookup is not efficient (linear time), so you get a O(n*m) total time (n is sizeA, m is sizeB); 5000 entries on each, it may be a bit too much.
If possible, you should change it to use Sets (and implements the hashCode and equals methods of your Student classes in case you didn't already!):
Set<Student> studentsA = new HashSet<>();
Set<Student> studentsB = new HashSet<>();
studentsA.removeAll(studentsB);
This gets you O(m*hash(n)).
The general approach is iterate through first list and check for element if it is contained in second list, if it exist add the element to the result list
Below is the complete solution
import java.util.ArrayList;
import java.util.List;
public class CompareListofObj {
public static void main(String[] args) {
List<Student> listStd1 = new ArrayList<Student>();
List<Student> listStd2 = new ArrayList<Student>();
Student std1 = new Student(1, 1, "a");
Student std2 = new Student(2, 1, "b");
Student std3 = new Student(3, 3, "c");
Student std4 = new Student(4, 4, "d");
listStd1.add(std1);
listStd1.add(std2);
listStd1.add(std3);
listStd1.add(std4);
Student std5 = new Student(1, 1, "a");
Student std6 = new Student(2, 1, "b");
Student std7 = new Student(7, 7, "c");
Student std8 = new Student(8, 8, "d");
listStd2.add(std5);
listStd2.add(std6);
listStd2.add(std7);
listStd2.add(std8);
List<Student> listResult = new ArrayList<Student>();
for (int i = 0; i < listStd1.size(); i++) {
if (listStd2.contains(listStd1.get(i))) {
listResult.add(listStd1.get(i));
} else {
}
}
for (int i = 0; i < listResult.size(); i++) {
System.out.println("common elt" + listResult.get(i).getA() + ", " + listResult.get(i).getB() + ", "
+ listResult.get(i).getC());
}
}
}
Student class
package sample;
public class Student {
int A;
int B;
String C;
public Student(int a, int b, String c) {
super();
A = a;
B = b;
C = c;
}
public int getA() {
return A;
}
public void setA(int a) {
A = a;
}
public int getB() {
return B;
}
public void setB(int b) {
B = b;
}
public String getC() {
return C;
}
public void setC(String c) {
C = c;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + A;
result = prime * result + B;
result = prime * result + ((C == null) ? 0 : C.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;
Student other = (Student) obj;
if (A != other.A)
return false;
if (B != other.B)
return false;
if (C == null) {
if (other.C != null)
return false;
} else if (!C.equals(other.C))
return false;
return true;
}
}
I am writing a class called Coord. I have created a constructor:
public final int r,c;
public Coord (int r, int c){
this.r = r;
this.c = c;
}
I also did another two methods
//Creates and returns a new Coord value with the same row/column
public Coord copy(){
Coord copy = new Coord (r,c);
return copy;
}
//Given another object, is it also a Coord with the same row and column values?
public boolean equals(Object o){
return this==o; //this may be incorrect.
}
Now I can not pass some test cases as following:
Coord c = new Coord (5,10);
#Test (timeout=2000) public void coord() {
assertEquals(c, c.copy());
assertEquals(c, c);
assertFalse(c.equals(new Coord (2,3))); // #(5,10) != #(2,3).
assertFalse(c.equals("hello")); // must work for non-Coords.
}
I think the problem may arise from my boolean equals method, but I have tried a lot I still cannot pass the test. Is there a deep equal issue here? Can someone help me?
Is there a deep equal issue here?
Well yes, your equals method just checks whether the value passed to it is the same reference. Your comment says what you want to do:
//Given another object, is it also a Coord with the same row and column values?
So that's what you need to implement:
#Override public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o.getClass() != getClass()) {
return false;
}
Coord other = (Coord) o;
return other.r == r && other.c == c;
}
I'd also encourage you to make the class final (in which case you can use instanceof instead of calling getClass()) and you need to implement hashCode() to be consistent with equals too. For example:
#Override public int hashCode() {
int hash = 23;
hash = hash * 31 + r;
hash = hash * 31 + c;
return hash;
}