Since the equals function in array only check the instance, it doesn't work well with Set.
Hence, I wonder how to make a set of arrays in Java?
One possible way could be put each array in an object, and implement equals function for that class, but will that decrease the performance too much?
Don't use raw Arrays unless you absolutely have to because of some legacy API that requires an Array.
Always try and use a type safe ArrayList<T> instead and you won't have these kind of issues.
If you make your Set be an instance of TreeSet, you can specify a custom Comparator which will be used for all comparisons (even equality).
You could create a wrapper class for your array and override hashcode and equals accordingly.
For example:
public class MyArrayContainer {
int[] myArray = new int[100];
#Override
public boolean equals(Object other) {
if (null != other && other instanceof MyArrayContainer) {
MyArrayContainer o = (MyArrayContainer) other;
final int myLength = myArray.length;
if (o.myArray.length != myLength) {
return false;
}
for (int i = 0; i < myLength; i++) {
if (myArray[i] != o.myArray[i]) {
return false;
}
}
return true;
}
return false;
}
#Override
public int hashCode() {
return myArray.length;
}
}
Since Java 9, you can use Arrays::compare method as a comparator for TreeSet that compares the contents of arrays.
Set<String[]> set = new TreeSet<>(Arrays::compare);
String[] val1 = {"one", "two"};
String[] val2 = {"one", "two"};
String[] val3 = {"one", "two"};
set.add(val1);
set.add(val2);
System.out.println(set.size()); // 1
System.out.println(set.contains(val1)); // true
System.out.println(set.contains(val2)); // true
System.out.println(set.contains(val3)); // true
See also: Check if an array exists in a HashSet<int[]>
It is better to use lists for this problem.
Also, use trusted sources to ensure that Java is properly configured on your system; Read the complete information in the documentation
Since the ArrayList class already wraps an array, you can extend it and override the equals and hashCode methods. Here is a sample:
public MyArrayList extends ArrayList<MyClass> {
#Override
public boolean equals(Object o) {
if (o instanceof MyArrayList) {
//place your comparison logic here
return true;
}
return false;
}
#Override
public int hashCode() {
//just a sample, you can place your own code
return super.hashCode();
}
}
UPDATE:
You can even override it for a generic use, just changing the code to:
public MyArrayList<T> extends ArrayList<T> {
//overrides the methods you need
#Override
public boolean equals(Object o) {
if (o instanceof MyArrayList) {
//place your comparison logic here
return true;
}
return false;
}
}
A class that extends Set and override the equals method could do it.
Related
I have two arraylist , based on certain equality criteria i am setting values to one list,
below is the code , how to avoid nested for loop, how to optimize operation using map or any other way ,
ArrayList<TriggerTerm> triggerTerms = triggerDaoImpl.getTriggerTerm(.....);
ArrayList<Term> terms = baseDaoImpl.getTerm(.....);
for (TriggerTerm tt : triggerTerms) {
for (Term t : terms) {
if (tt.getCodeNumber().equals(t.getCodeNumber()) &&
tt.getTermNumber().equals(t.getTermNumber())) {
tt.setStartDate(t.getStartDate());
}
}
}
I would suggest if we can make is a relationship between Term and TriggerTerm class (Seems it would be), it should be like this;
class Terms {
private String codeNumber;
private String termNumber;
private String startDate;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Terms)) return false;
Terms terms = (Terms) o;
if (!codeNumber.equals(terms.codeNumber)) return false;
return termNumber.equals(terms.termNumber);
}
#Override
public int hashCode() {
int result = codeNumber.hashCode();
result = 31 * result + termNumber.hashCode();
return result;
}
}
class TriggerTerms extends Terms {
// Inherit setters and getters from the super class as well as `equals` and `hashCode` function.
}
I've omitted getters and setters from the code. Also, please not the equals and hashCode implementation. I've only used codeNumber and termNumber variables.
If there are no duplicate values in both triggerTerms and terms lists, use java.util.HashSet rather than a List. Because following example I'm using contains() function. In ArrayList time complexity of contains() implementation is O(n) as well as the indexOf(), but in HashSet it's O(1) thanks to its implementation. If you use
So, simply use following code snippet;
for (TriggerTerms tt : triggerTerms) {
if (terms.contains(tt)) {
tt.setStartDate(terms.get(terms.indexOf(tt)).getStartDate());
}
}
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;
}
}
I am getting a warning that watchStore.contains(s) is a suspicious call to java.util.Collection#contains. How can I fix it? I want to use contains() to find a particular object with the matching serial number.
public Watch findWatchBySerialNumber(long srch) {
long s = srch;
Watch watch = null;
for(int i = 0; i < watchStore.size(); i++) {
watch = watchStore.get(i);
if(watchStore.contains(s)) {
System.out.print("item found");
return watch;
}
}
System.out.print("item not found");
return null; // watch is not found.
}
Presuming that Watch is the class, watchStore is a List<Watch>, and that a field serialNo exists on Watch...
public Optional<Watch> findWatchBySerialNumber(long serial) {
return watchStore.stream()
.filter(w -> w.getSerialNo() == serial)
.findFirst();
}
If you're not using Java 8, the code is close, but a bit more dangerous since you have the chance to return null. If you can use Guava's Optional, that'd be a better choice here.
public Watch findWatchBySerialNumber(long serial) {
for(Watch w : watchStore) {
if(w.getSerialNo() == serial) {
return w;
}
}
return null;
}
Your contains isn't going to work since your list doesn't contain Longs, it contains Watchs. This is also why the compiler sees it as dubious; contains accepts an Object but it will return false if what you're looking for doesn't have a comparable equals for what's in your list.
You have to iterate over the entirety of your collection to find it in this scenario, especially since you're looking for a specific property on those objects as opposed to a specific, easy-to-provide value.
please how can I fix that. I want to use the contain() to find a
particular object with the matching serial number.
In that case override Watch's equals() to use serialNumber field for comparison.
Then add constructor that accepts serialNumber.
public class Watch {
private final long serialNumber;
public Watch(long serialNumber) {
this.serialNumber = serialNumber;
}
#Override
public boolean equals(Object obj) {
return obj == this ||
(obj instanceof Watch && ((Watch)obj).serialNumber == serialNumber);
}
#Override
public int hashCode() {
return (int)serialNumber;
}
}
Replace if(watchStore.contains(s)){ with if(watchStore.contains(watchToFind)){ where Watch watchToFind = new Watch(s);
you can use contains method from org.apache.commons.lang.ArrayUtils package.
Checks if the value is in the given array.
The method returns false if a null array is passed in.
Parameters:
array the array to search through
valueToFind the value to find
Returns:
true if the array contains the object
long [] imageHashes= {12l,13l,14l,15l};
System.out.println(ArrayUtils.contains(imageHashes, 13l));
I am having trouble with HashSets. My program performs a Breadth First Search and I made a hashset to keep track of visited states. States are represented by int[] array. However, HashSet's contains method does not seem to be working as expected. Basically, it is not filtering out int[] arrays that should already have been added.
Here are the relevant parts of my code:
private boolean notVisitedAndNotNull(PuzzleState nextPS) {
if (nextPS != null && !this.visited.contains(nextPS.getStateArray))
return true;
return false;
}
private void addToQueue(PuzzleState nextPS) {
if (notVisitedAndNotNull(nextPS))
queue.add(nextPS);
}
private boolean solveByBFS() {
queue.clear();
queue.add(this.initialState);
long startTime = System.currentTimeMillis();
while(!queue.isEmpty()) {
if (queue.size() > maxQueueSize)
maxQueueSize = queue.size();
this.currentState = queue.poll();
if (this.currentState.equals(finalState)) {
System.out.println("Successful! Ending Time: " + startTime);
return true;
}
visited.add(this.currentState.getStateArray()); //this adds int[] array
this.addToQueue(this.currentState.moveUp());
this.addToQueue(this.currentState.moveDown());
this.addToQueue(this.currentState.moveRight());
this.addToQueue(this.currentState.moveLeft());
}
return false;
}
I apologize for posting so much code. I searched a bit and it seems that in order for HashSet to work correctly, I would have to implement hashCode and equals of HashSet. I'm not sure if this is possible for an int[] array. Is it better to just use a HashMap then and use toString method on int[] array to serve as a key?
Ok, I got this working now. Here is the code I added if anyone is interested:
#Override
public boolean equals(Object o) {
if (o instanceof PuzzleState) {
return (Arrays.equals(((PuzzleState) o).getStateArray(), this.getStateArray()));
}
return false;
}
#Override
public int hashCode() {
return Arrays.hashCode(this.getStateArray());
}
Array equality is by reference only. You are asking if the set contains exactly the same array object, it does not examine the contents of the array to see if a "similar" array exists.
Related:
Java: How to test on array equality?
equals vs Arrays.equals in Java
I'm using inside an iterative algorithm an HashSet that is dynamically enlarged at each algorithm iteration by adding new objects (via method add). Very frequently I check if a generated object has been already put inside the HashSet by using the contains method. Observe that the HashSet may include several thousand objects.
Here follows a citation from the doc about class HashSet:
"This class offers constant time performance for the basic operations (add, remove, contains and size), assuming the hash function disperses the elements properly among the buckets."
Apart from other considerations provided inside the doc (not reported for simplicity), I see that add and contains are executed in constant time.
Please, can you suggest another data structure in Java that provides better performance for the "contains" operation with respect to my problem?
Classes from Apache Commons or Guava are also accepted.
The performance of HashSet.contains() will be as good as you can get provided your objects have a properly implemented hashCode() method. That will ensure proper distribution among the buckets.
See Best implementation for hashCode method
As other answers already stated "constant time" is the best runtime-behaviout you can get.
If you will get it does depend on your hashcode-implementation, but since you use the NetBeans suggestion you shouldn't be too bad there.
As to how to keep the "constant time" as small as possible:
try to allocate your HashSet large enough from the very beginning to avoid costly rehash-operations
You can cache your calculated hashcode the first time hashCode() is called and return the cached value later on. There should be no need to add some triggering-mechanism to clear the cache on object-updates, since your relevant fields should be immutable - if they aren't you are bound to run into trouble using HashSet anyway.
You can let your object remember if it has been put in that hashset. Just have a boolean field to store if it was added to the hash set. Then you don't need to call contains on the HashSet but just read the field value of your object. This method will only work if the object is put in exactly one hashset that will check the boolean field.
It might be extended to a constant number of hashsets using java.util.BitSet in the object contained in the hashset where every hashset can be identified by a unique integer when the number of hashsets is known before the algorithm starts.
Since you are saying that you are calling contains frequently, it makes sense to replace newly generated objects with equal existing objects (object pooling), since the overhead of that will amortize by having contains being only a single field read.
As requested here is some sample code. The special set implementation is about 4 times faster than a normal hash set on my machine. However the question is how well this code reflects your use case.
public class FastSetContains {
public static class SetContainedAwareObject {
private final int state;
private boolean contained;
public SetContainedAwareObject(int state) {
this.state = state;
}
public void markAsContained() {
contained = true;
}
public boolean isContained() {
return contained;
}
public void markAsRemoved() {
contained = false;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + state;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SetContainedAwareObject other = (SetContainedAwareObject) obj;
if (state != other.state)
return false;
return true;
}
}
public static class FastContainsSet extends
HashSet<SetContainedAwareObject> {
#Override
public boolean contains(Object o) {
SetContainedAwareObject obj = (SetContainedAwareObject) o;
if (obj.isContained()) {
return true;
}
return super.contains(o);
}
#Override
public boolean add(SetContainedAwareObject e) {
boolean add = super.add(e);
e.markAsContained();
return add;
}
#Override
public boolean addAll(Collection<? extends SetContainedAwareObject> c) {
boolean addAll = super.addAll(c);
for (SetContainedAwareObject o : c) {
o.markAsContained();
}
return addAll;
}
#Override
public boolean remove(Object o) {
boolean remove = super.remove(o);
((SetContainedAwareObject) o).markAsRemoved();
return remove;
}
#Override
public boolean removeAll(Collection<?> c) {
boolean removeAll = super.removeAll(c);
for (Object o : c) {
((SetContainedAwareObject) o).markAsRemoved();
}
return removeAll;
}
}
private static final Random random = new Random(1234L);
private static final int additionalObjectsPerIteration = 10;
private static final int iterations = 100000;
private static final int differentObjectCount = 100;
private static final int containsCountPerIteration = 50;
private static long nanosSpentForContains;
public static void main(String[] args) {
Map<SetContainedAwareObject, SetContainedAwareObject> objectPool = new HashMap<>();
// switch comment use different Set implementaiton
//Set<SetContainedAwareObject> set = new FastContainsSet();
Set<SetContainedAwareObject> set = new HashSet<>();
//warm up
for (int i = 0; i < 100; i++) {
addAdditionalObjects(objectPool, set);
callSetContainsForSomeObjects(set);
}
objectPool.clear();
set.clear();
nanosSpentForContains = 0L;
for (int i = 0; i < iterations; i++) {
addAdditionalObjects(objectPool, set);
callSetContainsForSomeObjects(set);
}
System.out.println("nanos spent for contains: " + nanosSpentForContains);
}
private static void callSetContainsForSomeObjects(
Set<SetContainedAwareObject> set) {
int containsCount = set.size() > containsCountPerIteration ? set.size()
: containsCountPerIteration;
int[] indexes = new int[containsCount];
for (int i = 0; i < containsCount; i++) {
indexes[i] = random.nextInt(set.size());
}
Object[] elements = set.toArray();
long start = System.nanoTime();
for (int index : indexes) {
set.contains(elements[index]);
}
long end = System.nanoTime();
nanosSpentForContains += (end - start);
}
private static void addAdditionalObjects(
Map<SetContainedAwareObject, SetContainedAwareObject> objectPool,
Set<SetContainedAwareObject> set) {
for (int i = 0; i < additionalObjectsPerIteration; i++) {
SetContainedAwareObject object = new SetContainedAwareObject(
random.nextInt(differentObjectCount));
SetContainedAwareObject pooled = objectPool.get(object);
if (pooled == null) {
objectPool.put(object, object);
pooled = object;
}
set.add(pooled);
}
}
}
Anothe Edit:
using the following as the Set.contains implementation makes it about 8 times faster than a normal hashset:
#Override
public boolean contains(Object o) {
SetContainedAwareObject obj = (SetContainedAwareObject) o;
return obj.isContained();
}
EDIT:
This technique has a bit with the class enhancement of OpenJPA in common. The enhancement of OpenJPA enables a class to track its persistent state which is used by the entity manager. The suggested method enables an object to track if itself is contained in a set which is used by the algorithm.