TreeSet replacing old values by New values - java

I am trying to insert multiple objects in a TreeSet . The problem is one object inserts fine, but when I insert second object and tries to view both objects, I get the second object twice. How can I prevent my old values from getting replaced..
This is my POJO
public Class Question implements Comparable<Question>
{
.....
public int compareTo(Question q) {
if(vote>q.vote)
return -1;
else
return 1;
}
And this is the function which inserts object into the treeSet
public void save(String ques, String sessionId, Question question)
{
question.setQuesId(++quesId);
question.setQues(ques);
question.setSessionId(sessionId);
question.setVote(0);
setDemo.add(Question);
}
Note: "setDemo" is my treeSet object
Any advices are appreciated.
Thankyou

First of all, your comparator should handle the case in which the votes are equal.
public int compareTo(Question q) {
if(vote>q.vote) {
return -1;
} else if (vote<q.vote){
return 1;
} else {
return 0;
}
}
Secondly, make sure you override equals properly in Question.
If both the compareTo method returns 0 and the equals returns true, TreeSet will override your value, since it will detect the two values as duplicates. Otherwise, you should not get any replacements.
Although TreeSet does not use hashCode internally, I would suggest overriding that as well, since you might want to change the implementation from TreeSet to HashSet in the future.

Related

searching keys of subclass in HashMap

just tried to do something like:
public class GameMap {
protected HashMap<Sector, Integer[]> mapping;
protected void loadMapFromFile(String fileLocation, int numPlayers) {
.
//Other stuff
.
.
case "ALIENSECT":
Integer[] someValue = {5};
mapping.put(new AlienSector(row, col), someValue);
break;
}
public justATestMethod() {
System.out.println(mapping.containsKey(new Sector(6, 'L')));
}
Where AlienSector is a subclass of Sector.
But when I try to do this in another class:
mappa.justATestMethod();
The result is "false".
Instead if I rewrite the method "justATestMethod()" like this:
System.out.println(mapping.containsKey(new AlienSector(6, 'L')));
Result is "true".
I obtain "true" also changing this lines of "loadMapFromFile" method:
case "ALIENSECT":
Integer[] someValue = {5};
mapping.put(new AlienSector(row, col), someValue);
break;
This way:
case "ALIENSECT":
mapping.put(new Sector(row, col), new Integer[1]);
Integer[] aCazzo = {5};
mapping.put(new AlienSector(row, col), aCazzo);
break;
That is first filling the HashMap with Sector objects keys and then assigning keys of AlienSector objects.
Someone could explain me why this happens? AlienSector is a subclass of Sector, why Java doesn't recognize the presence of a Sector in the HashMap keys if I simply instantiate a subclass of it without first instantiate the key with an istance of the superclass "Sector" itself?
You are storing an AlienSector in the HashMap, and then trying to retrieve it with another Sector created using the same parameters. When you try to retrieve an object from a HashMap it is looking for an object that is 'equal' to the one you stored. But by default Java does not recognize two objects as 'equal' just because they have the same members. By default they are only equal if they are the same object (Strings, Integers etc. are special cases).
What you need to do is tell Java that two objects with the same parameters are 'equal' by overriding the 'equals' method. From your test results it looks like yo uhave done this for AlienSector. But you will need to do this for both Sector and AlienSector, and arrange it so the objects are considered equal even if they have different classes i.e. an AlienSector is considered equal to a Sector with the same members, and a Sector is considered equal to an AlienSector with the same members. There are tutorials on how to do this.
You will also need to override the hashCode() method to make sure that any two objects that would be considered 'equal' also return the same hashCode. HashMap uses hashCode a filter, deciding that things with different hashCodes can never be equal.
The details of all this are too long to put in an answer like this.
by the way, if you used the same object in the containsKey call, instead of creating a new one, you would find it worked.
You can use the class below to perform this behaviour.
One caveat to note is that if it is a large map, it may not be particularly performant, but for most cases the size of the map will be small so there is no real performance impact with this.
NOTE: JDK8+ code
Essentially we override the regular hashmap class methods for containsKey and get to do the appropriate searching.
import java.util.HashMap;
import java.util.Optional;
public class PolymorphicHashMap<K extends Class<?>,V> extends HashMap<K,V> {
#Override
public boolean containsKey(Object key) {
return findEntry((K)key).isPresent();
}
#Override
public V get(Object key) {
var entry = findEntry((K)key);
return entry.map(Entry::getValue).orElse(null);
}
private Optional<Entry<K,V>> findEntry(K key) {
return entrySet().stream()
.filter(e -> e.getKey().isAssignableFrom(key))
.findFirst();
}
}
HashMap uses the hashCode() function in order to lookup and store key/value pairs.
I believe you need to have both the superclass/subclass return the same hashcode in order to be able to lookup the keys of subclass in the HashMap.
public class AlienSector {
public int hashcode() {
//
// Generate a hashcode unique to this AlienSector object here
//
}
}
public class Sector {
public int hashCode() {
return super.hashCode(); // Return the same hashcode as the super class
}
}
As pointed out in the comment, the rule is that if you override the hashCode() function, you need to override the equals() function as well (and vice-versa)

custom equals() method does not work properly

I've implemented the Apriori algorithm. it works pretty well, but I ran into a strange problem: I've defined a Rule class to maintain the generated rules.
Here it is:
public class Rule
{
private Set<Integer> left;
private Set<Integer> right;
private LookupArtist lookupArtist;
public Rule(LookupArtist lookupArtist){
left = new HashSet<>();
right = new HashSet<>();
this.lookupArtist = lookupArtist;
}
#Override
public boolean equals(Object another){
Rule rule = (Rule) another;
if(this.left.equals(rule.getLeft()) && this.right.equals(rule.getRight()))
return true;
else
return false;
}
#Override
public String toString(){
/* print the object */
}
public void addToLeft(Integer toAdd){
left.add(toAdd);
}
public void addToRight(Integer toAdd){
right.add(toAdd);
}
public Set<Integer> getLeft(){
return left;
}
public Set<Integer> getRight(){
return right;
}
}
I also implemented the equals() method in a different way just to try:
#Override
public boolean equals(Object another){
Rule rule = (Rule) another;
boolean flag = true;
for(Integer artist : left){
if(flag)
if(!rule.left.contains(artist))
flag=false;
}
if(flag)
for(Integer artist : right){
if(flag)
if(!rule.right.contains(artist))
flag=false;
}
return flag;
}
The LookupArtist object is used to map the integers to some Strings.
The problem is that when I print out the rules I found that some rules appear two times. I also found in debug mode some replicated rules, so it isn't be a print problem. The rules are saved in a map like this:
static Map<Rule, Float> rules;
.
.
.
Rule rule = new Rule(lookupArtist);
for(int j=0;j<i;j++){
rule.addToLeft(a[j]);
}
for(int j=i;j<a.length;j++){
rule.addToRight(a[j]);
}
if(!rules.containsKey(rule)){
rules.put(rule, getRuleConfidence(rule));
}
Any idea where the problem can be?
When using a HashSet for storing objects of a class that has a custom equals implementation, you must have a matching custom implementation for hashCode.
If two objects are equal (according to the custom equals implementation), they must have the samehashCode. In the code you posted, I don't see an overriding ofhashCodein theRule` class.
When you add an instance to the HashSet, hashCode method is used to determine the index in the hash table in which the instance will be stored. Then, the linked list of instances stored in this index is iterated to see if the instance is already there. When iterating over that list, equals is used. If two objects that are equal are mapped by hashCode to different indices in the HashSet, the duplication won't be detected, since they would be stored in separate linked lists.
This is stated in the Javadoc of equals :
* Note that it is generally necessary to override the <tt>hashCode</tt>
* method whenever this method is overridden, so as to maintain the
* general contract for the <tt>hashCode</tt> method, which states
* that equal objects must have equal hash codes.
And in the Javadoc of hashCode :
* <li>If two objects are equal according to the <tt>equals(Object)</tt>
* method, then calling the <code>hashCode</code> method on each of
* the two objects must produce the same integer result.
You should always override hashCode when you override equals and vice versa.
Add something like this to your Rule class:
#Override
public int hashCode() {
return left.hashCode()
^ right.hashCode()
^ lookupArtist.hashCode();
}
Here is a good answer explaining why it's important to override both.
Also, your equals method can be written as
#Override
public boolean equals(Object another){
Rule rule = (Rule) another;
return left.equals(rule.left)
&& right.equals(rule.right)
&& lookupArtist.equals(rule.lookupArtist);
}
A final remark: Your other attempt at the equals-implementation is not symmetrical, i.e. it's not the case that rule1.equals(rule2) if and only if rule2.equals(rule1). That's a violation of the contract of equals.
And where is your hashCode() method? It is also very important :)

Custom hashCode when using part of a List for comparision

I am trying to write a custom hashCode fn, but I am not able to figure out the correct way to do that.
public class Person {
String name;
List<String> attributes;
#Override
public boolean equals(Object o) {
// Persons are equal if name is equal & if >= 2 of attributes are equal
// This I have implemented
}
#Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + (this.name == null ? 0 : this.name.hashCode());
//Not sure what to do here to account for attributes
return result;
}
}
I want the hashCode fn to be such that:
"If object1 and object2 are equal according to their equals() method, they must also have the same hash code"
Not sure how to do that?
As Oli points out in the comments, you cannot solve this by implementing equals() and relying on a Set to de-duplicate for you. Weird things could happen.
Thus you must resort to coding this yourself. Add the first item from your list into your new de-duplicated list. Then for each remaining item in your original list, compare it with those already present in your de-duplicated list and only add it if it passes your non-duplicate test.
Easiest way to fulfill the contract of the equals/hashcCode methods is to return a constant:
#Override
public int hashCode() {return 13;}
Otherwise your solution with a hash code based only on name will work.

Storing Object and Updating it in Sorted Tree Set

I am using a Tree Set to store some Signals as objects in my Tree Set and also want to update an object If the same signal comes again. So far I tried something but the problem is I am not able to get the complete object when I try to print it and Secondly I don't know if there is any way to update an abject and save it back to the set...
Here is my code
Signal Class (Signal.java)
public class Signal implements Comparable<Signal>{
String source;
String name;
int occurance;
public void setSource(String source){
this.source = source;
}
public void setName(String name){
this.name = name;
}
public void setOccurance(int occurance){
this.occurance = occurance;
}
public String getSource(){
return this.source;
}
public String getName(){
return this.name;
}
public int getOccurnace(){
return this.occurance;
}
#Override
public int compareTo(Signal arg0) {
// TODO Auto-generated method stub
return 0;
}
}
My Main Class
public class SortedSetTest {
public static void main(String[] args) {
// Create the sorted set
SortedSet<Signal> set = new TreeSet<Signal>();
//Create a Signal object for each new signal
Signal sig = new Signal();
sig.setSource("Source");
sig.setName("Signal Name");
sig.setOccurance(1);
// Add elements to the set
set.add(sig);
System.out.println(set);
The above print show me as object...How Can I see the set as String?
// Iterating over the elements in the set
Iterator<Signal> it = set.iterator();
while (it.hasNext()){
Here I want to print each object
For example take the first object and print the object (Signal) Source, Name and occurance and so on till the end of the set reaches.
}
}
}
The lines you're looking for:
while (it.hasNext()){
Signal sig = (Signal)it.next();
System.out.println(sig.getName());
System.out.println(sig.getOccurance());
// do the same with source or whatever property
}
You need to override toString in your Signal object to obtain a string that is more user-friendly than the default implementation in the Object class.
Also note that because all your Signals are equal based on your implementation of compareTo (always return 0) you won't be able to add more than one Signal to your set.
You are correctly printing the set.
You just haven't implemented the Signal.toString() method to represent a Signal object the way you want it.
About updating objects in a TreeSet: the object must not be modified in such a way that the output of compareTo is changed. If it needs to be updated in this way, remove it (using Set.remove(object), update it and add it back.
That said, your compareTo() method always returns 0. It should return arg0.occurence - occurence if you want higher occurence values to come before lower ones.
This is because returning a value less than 0 means this comes before the argument. 0 means equal ordering and > 1 means this comes after the argument.
Also, if you implement compareTo() you define the natural ordering of the class. It is strongly recommended to also override equals() and return true if and only if compareTo() returns 0. See http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html (search for 'consistent with equals'). With the above implementation of compareTo(), this would mean returning true only when they both have the same occurence value. If you don't want to implement equals this way (it is probably not correct) then you should write a class that implements Comparator<Signal> and pass that to the TreeSet constructor.
First of all you need to override toString method,insteadof using mutator methods try to use a constructor which implements all your instance variables and the third thing is TreeSet uses a
Red-Black tree structure,and guarantees that the elements will be ascending order,according to the natural order.So in compareTo() method you can compare one of your variables to get the solution.

Anything wrong about my Intset Class?

I design new IntSet Class that use ArrayList. first, i extends Intset by ArrayList and i start implement method. i face some problem in my union() method. here is my code...
public class IntSet extends ArrayList<Integer>{
private static final long serialVersionUID = 1L;
private ArrayList<Integer> intset;
public IntSet(){
this.intset = new ArrayList<Integer>();
}
public IntSet(ArrayList<Integer> intset){
this.intset = intset;
}
public void insert(int x){
this.intset.add(x);
}
#Override
public Integer remove(int x){
int index = intset.indexOf(x);
this.intset.remove(index);
return 1;
}
#Override
public int size(){
return this.intset.size();
}
#Override
public Integer get(int index){
return this.intset.get(index);
}
public boolean member(int x){
if(intset.indexOf(x)==-1) return false;
else return true;
}
public IntSet union(IntSet a){
IntSet intersectSet = new IntSet();
intersectSet.insert(0);
intersectSet.insert(1);
System.out.println(intersectSet.size());
System.out.println(intersectSet.contains(1));
for(int i=0; i<a.size(); i++){
}
return intersectSet;
}
public String toString(){
if(intset.size()==0) return "[]";
String s = "[" + intset.get(0).toString();
for(int i=1; i<intset.size(); i++){
s += "," + intset.get(i).toString();
}
return s += "]";
}
}
In method
union(IntSet a);
I constract new Intset object then add 2 value (0, 1) into intersectSet variable.
intersectSet.insert(0);
intersectSet.insert(1);
then i print size of intersectSet it shown me 2 that is correct!
but when i need to check that there is 1 in intersectSet or not? it shown me false.
System.out.println(intersectSet.contains(1));
In fact it should show me true because in intersectSet have integer 1.
anything wrong about my code and should i extends ArrayList for IntSet class?
Some suggestions on the class design:
Don't have your class extend ArrayList. A "set" really shouldn't be extending List. However, you should probably implement Set. This will have the added bonus of the compiler telling you what methods you need to implement for a set.....
For fastest performance (but more work!), you may want to use an internal array rather than an ArrayList.
Consider making the structure immutable, with functions that return a new copy rather than mutating the set in place. Depending on your usage, this may be a better solution, especially if you are mostly dealing with small, non-changing sets.
Again depending on your usage, you may want to override hashCode and equals to implement value based equality
When you construct the Intset with an ArrayList, you should ideally defensively copy (clone) the ArrayList. You don't want you set to change if someone mutates the original ArrayList.
The problem here is that you actually have 2 ArrayLists. The IntSet class IS A ArrayList, but this class contains a second ArrayList intset. Get rid of one of these ArrayLists. To demonstrate this add this second line:
System.out.println(intersectSet.contains(1));
System.out.println(intersectSet.intset.contains(1));
this will output:
false
true
So you are going to have to make a choice, do I inherit from ArrayList or do I contain an ArrayList. Of course what I am getting at here is Item 16 from Effective Java, Favor composition over inheritance.
You are both extending ArrayList and managing your own internal ArrayList object, this means that for all the methods which you have overridden you are interacting with your intset member variable, otherwise you are interacting with the inherited internal representation used by the ArrayList superclass. If you override the contains method you will get the correct behaviour.
I suggest that you drop the subclassing of ArrayList and instead implement the List or Set interfaces, although this depends on the exact problem you've been asked to solve.
you need to override contains method.
public boolean contains(Object o) {
return intset.contains(o);
}
and the rest of ArrayList methods that related to its elements.
and i doesn't seems to me a good solution. you may try better approach.

Categories