I'm trying to compare two fields (string and integer) using only the Comparable interface. It was my first time using this and I've no idea where to put the second field to compare the values.
public int compareTo(Object o) throws ClassCastException
{
int count = 0;
int compareName = this.lastName.compareTo(((SalePerson) o).getLastName());
int compareSales = Integer.compare(this.totalSales, ((SalePerson) o).getTotalSales());
if(!(o instanceof SalePerson))
{
throw new ClassCastException("A SalePerson object expected.");
}
if((this.totalSales < ((SalePerson) o).getTotalSales()))
{
count = -1;
}
else if((this.totalSales > ((SalePerson) o).getTotalSales()))
{
count = 1;
}
return count;
}
If you want to implement Comparable interface, it is unecassary to throw ClassCastException since o has to be SalePerson, otherwise you will get a compile error.
You can do it this way:
public class SalePerson implements Comparable<SalePerson>{
#Override
public int compareTo(SalePerson o) {
int totalSalesCompare = Integer.compare(this.totalSales, o.getTotalSales());
return totalSalesCompare == 0 ? this.lastName.compareTo(o.getLastName())
: totalSalesCompare;
}
}
Also, the compareTo is suggested to work with equals and hashCode:
#Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof SalePerson)) {
return false;
}
return Integer.compare(Integer.compare(this.totalSales, o.getTotalSales())) == 0
&& this.lastName.equals(o.getLastName());
}
#Override
public int hashCode() {
return this.lastName.hashCode() * 31 + this.totalSales;
}
Related
My intention was to make a caching service for a database results, that I can paginate differently based on client's requests.
So, upon the (search) request I am making a key that is composed of parameters, which are in form of two Map<String, String[]> and a:
public class DocMaintainer {
public Manipulator creator;
public Manipulator lastChange;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DocMaintainer that = (DocMaintainer) o;
return Objects.equals(creator, that.creator) &&
Objects.equals(lastChange, that.lastChange);
}
#Override
public int hashCode() {
return Objects.hash(creator, lastChange);
}
}
public class Manipulator {
public Date fromDate;
public Date toDate;
public String userId;
public String system;
public Manipulator() {
this.userId = "";
this.system = "";
this._fromJoda = new DateTime(Long.MIN_VALUE);
this._toJoda = new DateTime(Long.MAX_VALUE - DateTimeConstants.MILLIS_PER_WEEK);
}
private DateTime _fromJoda;
private DateTime _toJoda;
public DateTime get_fromJoda() {
_fromJoda = fromDate != null ? new DateTime(fromDate) : _fromJoda;
return _fromJoda;
}
public DateTime get_toJoda() {
_toJoda = toDate != null ? new DateTime(toDate) : _toJoda;
try {
_toJoda = _toJoda.plusDays(1);
} catch (Exception e) {
System.out.println(e);
}
return _toJoda;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Manipulator that = (Manipulator) o;
return Objects.equals(fromDate, that.fromDate) &&
Objects.equals(toDate, that.toDate) &&
Objects.equals(userId, that.userId) &&
Objects.equals(system, that.system);
}
#Override
public int hashCode() {
return Objects.hash(fromDate, toDate, userId, system);
}
}
As you can see I intended to use hashing to create a "key":
public class SearchKey {
public int conjunctionHash;
public int disjunctionHash;
public int maintainerHash;
public SearchKey(int conjunctionHash, int disjunctionHash, int maintainerHash) {
this.conjunctionHash = conjunctionHash;
this.disjunctionHash = disjunctionHash;
this.maintainerHash = maintainerHash;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SearchKey searchKey = (SearchKey) o;
return conjunctionHash == searchKey.conjunctionHash &&
disjunctionHash == searchKey.disjunctionHash &&
maintainerHash == searchKey.maintainerHash;
}
#Override
public int hashCode() {
return Objects.hash(conjunctionHash, disjunctionHash, maintainerHash);
}
}
a key-object is used directly as a caching key in a singleton service:
#Named
#Singleton
public class SearchCacheSrv {
private Map<SearchKey, ValidMainteinersList<FindDTO>> cache = new HashMap<>();
public ValidMainteinersList<FindDTO> getCached(SearchKey searchKey) {
if (cache.containsKey(searchKey))
return cache.get(searchKey);
else
return new ValidMainteinersList<FindDTO>();
}
public SearchKey makeAkey(Map<String, String[]> conjunction,
Map<String, String[]> disjunction,
DocMaintainer maintainer) {
return new SearchKey(conjunction.hashCode(), disjunction.hashCode(), maintainer.hashCode());
}
public ValidMainteinersList<FindDTO> cache(SearchKey searchKey, ValidMainteinersList<FindDTO> findDTOS) {
return cache.put(searchKey, findDTOS);
}
public void clearCache() {
cache.clear();
}
}
Unfortunately this is not behaving the way I expected and I'm getting different hashes/keys generated for the same parameters.
Naturally question is why?
The problem here is that the hashCode of an array does not depend on the contents, but on the reference. That means that if you have two conjunction / disjunction keys that are equal, but the contained arrays are not the same objects, then the hashcode of the keys will be different.
The solution that probably takes the least effort is replacing the arrays with ArrayLists, which do base their hashCode on the content.
I actually don't see the point of passing conjunction.hashCode(), ... to your SearchKey constructor; I never had to do it this way, but it could be my mistake.
Try passing actual values to your SearchKey class, not hashCodes, so the hashCode method always returns a consistent value.
I have a program that I have to use a HashSet for. My question arises from the fact that HashSets mainly contain one object, but if I wish to send information to the other class, it takes three objects: one string, one int, and one boolean.
The assignment says that I must use a HashSet
Constructor I am trying to send information to:
public Magic (String name, int size, boolean isVisible)
I have a class that is supposed to be sending sets of spells containing name, size, and isVisible.
Magic.go() class:
public void go()
{
int i = 0;
while (i < size) {
if (isVisible == true) {
System.out.println(name + "!");
}
i++;
}
}
Just create an object which contains all the three fields like this:
import java.util.Objects;
public class NameSizeVisible {
private final String name;
private final int size;
private final boolean isVisible;
public NameSizeVisible(String name, int size, boolean isVisible) {
this.name = name;
this.size = size;
this.isVisible = isVisible;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public boolean isVisible() {
return isVisible;
}
#Override
public int hashCode() {
return Objects.hash(name,size,isVisible);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NameSizeVisible other = (NameSizeVisible) obj;
if (isVisible != other.isVisible)
return false;
if (!Objects.equals(name, other.name))
return false;
if (size != other.size)
return false;
return true;
}
}
You can use a HashSet that stores Objects. So you would have:
HashSet<Object> set = new HashSet<>();
set.add(name);
set.add(size);
set.add(isVisible);
Then when you access the objects you just need to cast them to their respective types:
String name = "";
int size = 0;
boolean isVisible = false;
for (Object o : set) {
if (o instanceof String) {
name = (String) o;
} else if (o instanceof int) {
size = (int) o;
} else {
isVisible = (boolean) o;
}
}
Following code is a wrong implementation of java equals method. It violated the symmetric rule. But I do not know how it violated this rule. Please point it out where in this method it violated the symmetric rule.
public class WrongEquals {
private final String variable;
public WrongEquals(String variable) {
this.variable = variable;
}
#override public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof String) {
return variable.equals((String) o);
}
if (o instanceof WrongEquals) {
return variable.equals(((WrongEquals) o).variable);
}
return false;
}
#override
public int hashCode() {
return (variable == null ? 0 : variable.hashCode());
}
}
Because your WrongEquals instance can be equal to some String, but no String will be equal to any instance of WrongEquals
Look at the String implementation of equals() (as of JDK 7)
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
Obviously this implementation ensures that any equality will only exist between another String instance.
(This is just an extension and illustration of Kon's prior answer)
Try adding this main program:
public static void main(String[] args){
WrongEquals a = new WrongEquals("xyzzy");
String b = "xyzzy";
System.out.println(a.equals(b));
System.out.println(b.equals(a));
}
It will display:
true
false
See the following 2 classes, DTO and DTOWithOrdering:
public class DTO {
private final String key;
private final long recordVersionNumber;
public DTO(String key) {
this.key = key;
this.recordVersionNumber = 0;
}
public DTO(String key, long recordVersionNumber) {
this.key = key;
this.recordVersionNumber = recordVersionNumber;
}
public String getKey() {
return key;
}
public long getRecordVersionNumber() {
return recordVersionNumber;
}
#Override
public String toString() {
return "Key: " + this.key + " Record Version Number: " + this.recordVersionNumber;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DTO that = (DTO) o;
return Objects.equal(this.key, that.key) &&
Objects.equal(this.recordVersionNumber, that.recordVersionNumber);
}
#Override
public int hashCode() {
return Objects.hashCode(key, recordVersionNumber);
}
public class DTOWithOrdering extends DTO implements Comparable<DTOWithOrdering> {
public DTOWithOrdering(String key, long recordVersionNumber) {
super(key, recordVersionNumber);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DTOWithOrdering other = (DTOWithOrdering) o;
if(this.getKey().equals(other.getKey())) {
if(this.getRecordVersionNumber() == other.getRecordVersionNumber()) {
return true;
} else if(this.getRecordVersionNumber() <= other.getRecordVersionNumber()) {
return true;
} else {
return false;
}
} {
return false;
}
}
#Override
public int compareTo(DTOWithOrdering other) {
if(this.getKey().equals(other.getKey())) {
if(this.getRecordVersionNumber() == other.getRecordVersionNumber()) {
return 0;
} else if(this.getRecordVersionNumber() <= other.getRecordVersionNumber()) {
return 0;
} else {
return -1;
}
} {
return -1;
}
}
}
DTOWIthOrdering extends from DTO and overrides the equals and compareTo methods.
The problem arises with the following code snippet when I create a TreeSet<DTOWIthOrdering> and invoke contains on this
TreeSet<DTOWithOrdering> treeSet = new TreeSet<DTOWithOrdering>(keyAndVersionList);
List<DTO> results = new ArrayList<DTO>();
for (DTO diff : diffs) {
if (treeSet.contains(new DTOWithOrdering(diff.getKey(), diff.getRecordVersionNumber())) == false) {
results.add(diff);
}
}
When I run this within my program I can see that treeSet contains 2700+ entities, one of which has a key of 0b3ae620-bbcf-347d-a9b4-87e6fd765cd7 and recordVersionNumber of 4
However, one of the diff entities contains the same key with a recordVersionNumber of 0.
When the code invokes the contains method, the set returns a value of false.
Strangely, for other examples, where the keys are equal and the record version number is greater in the TreeSet it returns true!
Is there something wrong here with my logic?
Here is a quote from JavaDoc for Comparable interface:
int compareTo(T o)
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object. The implementor
must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and
y. (This implies that x.compareTo(y) must throw an exception if
y.compareTo(x) throws an exception.)
If you return -1 but never return 1, the property
sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) cannot hold true. So your implementation of this method does not conform to the specification and TreeSet can work improperly.
Can anyone let me know what goes wrong in this piece of code? I'm pulling my hair out!
There isn't any problem if I use HashMap instead of ConcurrentHashMap. The code is compiled with JDK 5.0
public class MapTest {
public Map<DummyKey, DummyValue> testMap = new ConcurrentHashMap<DummyKey, DummyValue>();
public MapTest() {
DummyKey k1 = new DummyKey("A");
DummyValue v1 = new DummyValue("1");
DummyKey k2 = new DummyKey("B");
DummyValue v2 = new DummyValue("2");
testMap.put(k1, v1);
testMap.put(k2, v2);
}
public void printMap() {
for(DummyKey key : testMap.keySet()){
System.out.println(key.getKeyName());
DummyValue val = testMap.get(key);
System.out.println(val.getValue());
}
}
public static void main(String[] args){
MapTest main = new MapTest();
main.printMap();
}
private static class DummyKey {
private String keyName = "";
public DummyKey(String keyName){
this.keyName = keyName;
}
public String getKeyName() {
return keyName;
}
#Override
public int hashCode() {
return keyName.hashCode();
}
#Override
public boolean equals(Object o) {
return keyName.equals(o);
}
}
private static class DummyValue {
private String value = "";
public DummyValue(String value){
this.value = value;
}
public String getValue() {
return value;
}
}
}
This is the output:
B
Exception in thread "main" java.lang.NullPointerException
at test.MapTest.printMap(MapTest.java:27)
at test.MapTest.main(MapTest.java:34)
DummyKey.equals method implementation is incorrect, due to that testMap.get(key) always returns null. Try this
public boolean equals(Object o) {
if (o instanceof DummyKey) {
DummyKey other = (DummyKey) o;
return keyName == null ? other.keyName == null : keyName.equals(other.keyName);
}
return false;
}
hashCode also needs a little change to be consistent with equals
public int hashCode() {
return keyName == null ? 0 : keyName.hashCode();
}
The problem comes from your equals in DummyKey.
When you call DummyValue val = testMap.get(key);, the hashcode function finds a match (both keyname of k1 and key are the same and so are their hashcode). Yet equals returns false because k1.keyname is equal to "A" which is not equal to key itself, which is actually of type DummyValue: you are not comparing properly!
Therefore, you need to modify your equals function:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DummyKey other = (DummyKey) obj;
if (keyName == null) {
if (other.keyName != null)
return false;
} else if (!keyName.equals(other.keyName))
return false;
return true;
}
Please note that if you change hashCode(), then you must change equals() as well. Otherwise, you will run into problems. If equals() returns true for two items, then their hashCode() value must be equal! The opposite is not required but preferable for better hashing performance. Here is an implementation of equals() and hashCode().
HINT: if you are using eclipse, you can utilize its source generation capability to create the correct hashCode() and equals() method for you. The only thing you need to do is to pick the instance variables that identify the object. To do so in eclipse, while your source code is open, go to the tabs in the top and choose "source", then choose "Generate hashCode() and equals()..."
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((keyName == null) ? 0 : keyName.hashCode());
return result;
}
Override
public boolean equals(Object other) {
if(this == other) return true; //for optimization
if(! other instanceof this) return false; //also covers for when other == null
return this.keyName == null ? other.keyName == null : this.keyName.equals(other.keyName);
}
As others have pointed, the problem lies in the way you override hashcode and equals.
Two options : 1) Just remove the hashcode and equals and it works fine
2) I let eclipse generate the source for hashcode and equals and it works fine. This is what my eclipse belted out for me :
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((keyName == null) ? 0 : keyName.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;
DummyKey other = (DummyKey) obj;
if (keyName == null) {
if (other.keyName != null)
return false;
} else if (!keyName.equals(other.keyName))
return false;
return true;
}