I have a map that stores my ClassA as a key and some exception as a value.
I also have a list that contains ClassB objects.
ClassA has an entry X (Long) and ClassB has an entry Y(ClassY) and it has field X (String) too.
Now I should find in map where ClassA.getX == ClassB.getY.getX
But problem is I can search ın map only by key and key object must be ClassA. Otherwise it returns null.
Here is my iteration:
list = listModelNewSc;
for (int i = 0; i < exceptionMap.size(); i++) {
for (int k = 0; k < list.getSize(); k++) {
if (((ClassA) exceptionMap.get(i)).getX() == Long
.parseLong((((CLassB) list.getElementAt(k)).getY().getX()))) {
Listitem itemAtIndex = list.getItemAtIndex(i);
if (itemAtIndex != null) {
System.out.print("FOUND");
}
}
}
}
The only way to do this with the setup you described is to iterate through all the keys in the map until you find the one you want.
Alternatively, you could have have a second map with ClassA.getX as the key (mapping to the same value).
Essentially it's a trade off, the first solution is slower but uses less memory, the second solution is faster but uses more memory (you have two copies of the map).
Iterate over keyset or entryset.
A solution, haven't tried but tell me if it works :)
As far as I know when you call Map.get(...) it uses the keys' equals(o) and hashCode() methods. As a solution to your problem you could override these methods of ClassA in a kinda "wrong" way, like this:
#Override
public int hashCode() {
// If you know that the code of a key instance has no logic value anywhere else, give constant value here and in ClassB
return 99827;
}
#Override
public boolean equals(Object o) {
// This will (maybe) make Map.get(ClassB) work with ClassA as a key
if (o instanceof ClassB) {
ClassB cb = (ClassB) o;
return this.getX() == cb.getY().getX();
} else if (o instanceof ClassA) {
// ...
}
return false;
}
So then you'll just do exceptionMap.get(classB) and hopefully get the exception.
Related
I have a class called Varfoo that stores variables. I have another class called Replacement that uses a hashmap to replace the x into 2. With the forget method, it's meant to forget replacing x with 2.
Varfoo x = new VarFoo("x");
Replacement s = new Replacement();
s.put(new VarFoo("x"), new IntFoo(2));
x.applyReplacement(s);
s.forget(x);
Here's the forget method:
public boolean forget(VarFoo var) {
if (var == null) {
throw new NullPointerException();
} else {
if (replacementMap.containsKey(var)) {
replacementMap.remove(var);
return true;
} else {
return false;
}
}
}
It will result to null because I've removed the key itself, not what I intended. How do I revert it back to how it was?
Equals and hashcodes of Varfoo:
#Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof VarFoo))
return false;
if (o == this)
return true;
return name.equals(((VarFoo) o).name);
}
#Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
You can make a new implementation of Map, that contains two (e.g.) HashMaps. In normal mode it forwards every operation to map1. This is the original map. map2 is null.
When you make a savepoint, you assign an empty map to map2. get operations now go first to map2 and then, if not found, to map1. put operations go only to map2. When you call forget, you assign again null to map2.
Of course, you must implement all the other methods of the Map interface. But this should be a simple task. Take care of removes, if needed (maybe you will need a Set of removed keys.
Hint: You can use java.util.AbstractMap as a base for your implementation.
Consider that I would like to create a class which manages a TreeSet of custom objects with two keys: A String instance identifier, and a long order identifier. The Long value would be used to determine the order of the elements in the list, while the string would be used to determine if two of these elements are duplicates. To clarify, here is what the methods would look like for the custom object
#Override
public boolean equals(Object o) {
if(o == null)
return false;
if(!(o instanceof CustomObject))
return false;
//Determined by string_id values ONLY. Used in TreeSet implementation
CustomObject obj = (CustomObject) o;
return obj.string_id.equals(this.string_id);
}
#Override
public int compareTo(Object another) {
if(another == null || !(another instanceof CustomObject))
return -1;
CustomObject other = (CustomObject) another;
if(other.long_id > this.long_id)
return -1;
else if(other.long_id < this.long_id)
return 1;
else
return 0;
}
Can my TreeSet function this way? I ask, because while testing this, I've found that my implementation only keeps the latest entry, and discards the rest. I'm looking to find out if this is simply an error with my implementation, or if I'm not properly using the TreeSet class and need to refactor my approach.
I have Set of that structure. I do not have duplicates but when I call:
set.add(element) -> and there is already exact element I would like the old to be replaced.
import java.io.*;
public class WordInfo implements Serializable {
File plik;
Integer wystapienia;
public WordInfo(File plik, Integer wystapienia) {
this.plik = plik;
this.wystapienia = wystapienia;
}
public String toString() {
// if (plik.getAbsolutePath().contains("src") && wystapienia != 0)
return plik.getAbsolutePath() + "\tWYSTAPIEN " + wystapienia;
// return "";
}
#Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof WordInfo)) return false;
return this.plik.equals(((WordInfo) obj).plik);
}
#Override
public int hashCode() {
return this.plik.hashCode();
}
}
Do a remove before each add:
someSet.remove(myObject);
someSet.add(myObject);
The remove will remove any object that is equal to myObject. Alternatively, you can check the add result:
if(!someSet.add(myObject)) {
someSet.remove(myObject);
someSet.add(myObject);
}
Which would be more efficient depends on how often you have collisions. If they are rare, the second form will usually do only one operation, but when there is a collision it does three. The first form always does two.
If the set already contains an element that equals() the element you are trying to add, the new element won't be added and won't replace the existing element. To guarantee that the new element is added, simply remove it from the set first:
set.remove(aWordInfo);
set.add(aWordInfo);
I was working on a problem where I had a set then I wanted to replace/override some of the objects with objects from another set.
In my case what I ended up doing was creating a new set and putting the overrides in first then adding the current objects second. This works because a set won't replace any existing objects when adding new objects.
If you have:
Set<WordInfo> currentInfo;
Set<WorldInfo> overrides;
Instead of:
for each override, replace the object in current info
I did:
Set<WordInfo> updated = new HashSet<>();
updated.addAll(overrides);
updated.addAll(currentInfo);
Try something as follows (this will only make sense if the equals and hashCode depends on one field, but the other fields could have different values):
if(!set.add(obj)) {
//set already contains the element (not the same object though)
set.remove(obj); //remove the one in the set
set.add(obj); //add the new one
}
Check out the documentation for the Set.add method
If this set already contains the element, the call leaves the set unchanged and returns false.
Check the HashSet code within the JDK.
When an element is added and is a duplicate, the old value is replaced.
Folk think that the new element is discarded, it's wrong.
So, you need no additional code in your case.
UPDATED---------------------
I re-read the code in JDK, and admit a mistake that I've made.
When put is made, the VALUE is replaced not the KEY from an HashMap.
Why am I talking about Hashmap??!! Because if you look at the HashSet code, you will notice:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
So the PRESENT value is replaced with the new one as shown in this portion of code:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
But I agree, the key isn't replaced, and since the keys represent the HashSet's values, this one is said to be "untouched".
Why cannot I retrieve an element from a HashSet?
Consider my HashSet containing a list of MyHashObjects with their hashCode() and equals() methods overridden correctly. I was hoping to construct a MyHashObject myself, and set the relevant hash code properties to certain values.
I can query the HashSet to see if there "equivalent" objects in the set using the contains() method. So even though contains() returns true for the two objects, they may not be == true.
How come then there isn’t any get() method similar to how the contains() works?
What is the thinking behind this API decision?
If you know what element you want to retrieve, then you already have the element. The only question for a Set to answer, given an element, is whether it contains() it or not.
If you want to iterator over the elements, just use a Set.iterator().
It sounds like what you're trying to do is designate a canonical element for an equivalence class of elements. You can use a Map<MyObject,MyObject> to do this. See this Stack Overflow question or this one for a discussion.
If you are really determined to find an element that .equals() your original element with the constraint that you must use the HashSet, I think you're stuck with iterating over it and checking equals() yourself. The API doesn't let you grab something by its hash code. So you could do:
MyObject findIfPresent(MyObject source, HashSet<MyObject> set)
{
if (set.contains(source)) {
for (MyObject obj : set) {
if (obj.equals(source))
return obj;
}
}
return null;
}
It is brute-force and O(n) ugly, but if that's what you need to do...
You can use HashMap<MyHashObject, MyHashObject> instead of HashSet<MyHashObject>.
Calling containsKey() on your "reconstructed" MyHashObject will first hashCode() - check the collection, and if a duplicate hashcode is hit, finally equals() - check your "reconstructed" against the original, at which you can retrieve the original using get()
Complexity is O(1) but the downside is you will likely have to override both equals() and hashCode() methods.
It sounds like you're essentially trying to use the hash code as a key in a map (which is what HashSets do behind the scenes). You could just do it explicitly, by declaring HashMap<Integer, MyHashObject>.
There is no get for HashSets because typically the object you would supply to the get method as a parameter is the same object you would get back.
If you know the order of elements in your Set, you can retrieve them by converting the Set to an Array. Something like this:
Set mySet = MyStorageObject.getMyStringSet();
Object[] myArr = mySet.toArray();
String value1 = myArr[0].toString();
String value2 = myArr[1].toString();
The idea that you need to get the reference to the object that is contained inside a Set object is common. It can be archived by 2 ways:
Use HashSet as you wanted, then:
public Object getObjectReference(HashSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
for (Xobject o : set) {
if (obj.equals(o))
return o;
}
}
return null;
}
For this approach to work, you need to override both hashCode() and equals(Object o) methods
In the worst scenario we have O(n)
Second approach is to use TreeSet
public Object getObjectReference(TreeSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
return set.floor(obj);
}
return null;
}
This approach gives O(log(n)), more efficient.
You don't need to override hashCode for this approach but you have to implement Comparable interface. ( define function compareTo(Object o)).
One of the easiest ways is to convert to Array:
for(int i = 0; i < set.size(); i++) {
System.out.println(set.toArray()[i]);
}
If I know for sure in my application that the object is not used in search in any of the list or hash data structure and not used equals method elsewhere except the one used indirectly in hash data structure while adding. Is it advisable to update the existing object in set in equals method. Refer the below code. If I add the this bean to HashSet, I can do group aggregation on the matching object on key (id). By this way I am able to achieve aggregation functions such as sum, max, min, ... as well. If not advisable, please feel free to share me your thoughts.
public class MyBean {
String id,
name;
double amountSpent;
#Override
public int hashCode() {
return id.hashCode();
}
#Override
public boolean equals(Object obj) {
if(obj!=null && obj instanceof MyBean ) {
MyBean tmpObj = (MyBean) obj;
if(tmpObj.id!=null && tmpObj.id.equals(this.id)) {
tmpObj.amountSpent += this.amountSpent;
return true;
}
}
return false;
}
}
First of all, convert your set to an array. Then, get the item by indexing the array.
Set uniqueItem = new HashSet();
uniqueItem.add("0");
uniqueItem.add("1");
uniqueItem.add("0");
Object[] arrayItem = uniqueItem.toArray();
for(int i = 0; i < uniqueItem.size(); i++) {
System.out.println("Item " + i + " " + arrayItem[i].toString());
}
If you could use List as a data structure to store your data, instead of using Map to store the result in the value of the Map, you can use following snippet and store the result in the same object.
Here is a Node class:
private class Node {
public int row, col, distance;
public Node(int row, int col, int distance) {
this.row = row;
this.col = col;
this.distance = distance;
}
public boolean equals(Object o) {
return (o instanceof Node &&
row == ((Node) o).row &&
col == ((Node) o).col);
}
}
If you store your result in distance variable and the items in the list are checked based on their coordinates, you can use the following to change the distance to a new one with the help of lastIndexOf method as long as you only need to store one element for each data:
List<Node> nodeList;
nodeList = new ArrayList<>(Arrays.asList(new Node(1, 2, 1), new Node(3, 4, 5)));
Node tempNode = new Node(1, 2, 10);
if(nodeList.contains(tempNode))
nodeList.get(nodeList.lastIndexOf(tempNode)).distance += tempNode.distance;
It is basically reimplementing Set whose items can be accessed and changed.
If you want to have a reference to the real object using the same performance as HashSet, I think the best way is to use HashMap.
Example (in Kotlin, but similar in Java) of finding an object, changing some field in it if it exists, or adding it in case it doesn't exist:
val map = HashMap<DbData, DbData>()
val dbData = map[objectToFind]
if(dbData!=null){
++dbData.someIntField
}
else {
map[dbData] = dbData
}
I have a code to return an arrayList with the duplicates of an ArrayList
but seems it's not working, I am comparing all items in the array...
public ArrayList<ObjectList> duplicates(ArrayList<ObjectList> someObjectsList) {
ArrayList<ObjectList> ret = new ArrayList<ObjectList>();
for ( ObjectList aSomeObjectsList: someObjectsList) {
String field1 = aSomeObjectsList.get1();
String field2 = aSomeObjectsList.get2();
String field3 = aSomeObjectsList.get3();
String field4 = aSomeObjectsList.get4();
for (ObjectList someObject : ret) {
if (
field1.trim().equals(someObject.get1())&&
field2.trim().equals(someObject.get2())&&
field3.trim().equals(someObject.get3())&&
field4.trim().equals(someObject.get4())
){
ret.add(aSomeObjectsList);
}
}
}
return ret;
}
But i guess I am doing something wrong because it doesn't return anything, and I know it has duplictates under this 4 field criteria
Thanks in advance
for (Object someObject : ret) {
if (
field1.trim().equals(someObject.get1())&&
field2.trim().equals(someObject.get2())&&
field3.trim().equals(someObject.get3())&&
field4.trim().equals(someObject.get4())
){
ret.add(aSomeObjectsList);
}
}
The above loop wouldn't work, since it has the size of zero.
Here you go,
public Set<ObjectList> duplicates(ArrayList<ObjectList> someObjectsList) {
Set<ObjectList> originals = new HashSet<ObjectList>();
Set<ObjectList> duplicates = new HashSet<ObjectList>();
for ( ObjectList aSomeObjectsList: someObjectsList) {
boolean added = originals.add(aSomeObjectsList);
if(!added){
duplicates.add(aSomeObjectsList);
}
}
return duplicates;
}
This would work, provided your ObjectList class have the correct implementation of hashCode() and equals() methods.
Disclaimer: This implementation will not provide the information about how many times a particular object was duplicated in the provided list. It will just tell you that a particular object was duplicated. I assumed that that was your real intention. If you wanna count, how many times, you have to modify the code accordingly.
Hint/Suggestion: You should override the equals() method and place your field equality check in there instead, once and for all.
This shouldn't compile - if aSomeObjectsList is an Object then it doesn't have methods get1(), get2(), etc.
Your logic won't work because you aren't checking each element in your input List against the other elements in the input List; rather, you're trying to check the return List.
Also, this is not a really efficient way to check for duplicates in a collection. A better way would be to use a HashMap, where you could check set membership in roughly constant time. If you have to use a List, then sort it first (assuming your objects have a natural ordering) and check adjacent members for equality.
Barring those two, just use List.contains().
Here's a way you can do this. I have defined a basic class ObjectList that shows a way to implement equals and hashCode. Note that this assumes that all the internal variables are non-null. If these variables can contain null then you will need to check for that when computing the equals/hashCode. Also, the objects in this class must also themselves properly implement equals/hashCode.
public class ObjectList {
private int h;
private Object obj1;
private Object obj2;
private Object obj3;
private Object obj4;
#Override
public boolean equals(final Object o) {
if (!(o instanceof ObjectList))
return false;
final ObjectList that = (ObjectList) o;
return that.obj1.equals(obj1) && that.obj2.equals(obj2)
&& that.obj3.equals(obj3) && that.obj4.equals(obj4);
}
#Override
public int hashCode() {
// caches the hashcode since it could be costly to recompute every time
// but this assumes that your object is essentially immutable
// (which it should be if you are using equals/hashCode. If this is not
// true and you want to just temporarily use this when doing the duplicate
// test, move the h variable definition from the object level to this method
// and remove this if statement.
if (h != 0)
return h;
h = obj1.hashCode();
h = h * 31 + obj2.hashCode();
h = h * 31 + obj3.hashCode();
h = h * 31 + obj4.hashCode();
return h;
}
}
public Collection<ObjectList> duplicates(
final Collection<ObjectList> someObjectsList) {
final Set<ObjectList> unique = new HashSet<ObjectList>(someObjectsList);
final ArrayList<ObjectList> ret = new ArrayList<ObjectList>(someObjectsList);
for (final ObjectList o : unique) {
ret.remove(o);
}
// The ret list now contains the duplicate instances; instances
// with more than two occurrences will occur multiple times still in
// this list.
return ret;
// If you want a list of unique duplicate instances then, comment out the above
// return and uncomment this one.
// return new HashSet<ObjectList>(ret);
}
Using Collection<ObjectList> is better, if you can do that, for both the parameter and returned value so you can vary the implementations (ArrayList, Set, etc).