Getting Value from a sorted TreeMap - java

I have a sorted map
Map<RangeValues,String> cIndexes = new TreeMap(new StartIndexComparator());
where key is an object of the class containing two integer values.
public class RangeValues {
private int startIndex;
private int endIndex;
}
So, when i am trying to access the value from that map, I am getting null as a value.
Set<RangeValues> keySet = cIndexes.keySet();
RangeValues[] keys = keySet.toArray(new RangeValues[keySet.size()]);
for(int index = 0; index < keys.length-1; index++)
{
**cIndexes.get(keys[index]) // this is giving null**
int nextIndex = keys[index+1].getStartIndex();
}
Implementation of comparator is
public class StartIndexComparator implements Comparator<RangeValues> {
#Override
public int compare(RangeValues r1, RangeValues r2) {
if(r1.getStartIndex() > r2.getStartIndex())
{
return 1;
}
else
{
return -1;
}
}
}
filling of map
RangeValues range = new RangeValues();
range.setStartIndex();
range.setEndIndex();
cIndexes.put(range,conjunctions.get(conjIndex));
How can I get the value from that map using object as key.

You need to override equals and hashCode methods in your object that is being used as a key
public class RangeValues {
private int startIndex;
private int endIndex;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + endIndex;
result = prime * result + startIndex;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RangeValues other = (RangeValues) obj;
if (endIndex != other.endIndex)
return false;
if (startIndex != other.startIndex)
return false;
return true;
}
}
The above equals and hashCode methods assume that both startIndex and endIndex will take part in deciding whether two objects of your class are equal.

You are probably not fulfilling the equals / hashCode contract in your RangeValues class.
Threfore, there is no guarantee that cIndexes.get(keys[index]) will not return null.

Related

compareTo with objects returns a false while it is true

I am trying to check whether my levelorder of my Binary Search Tree is equal to the other one. To do this, I tried to make a compareTo method. I only give equal values to the method, but it keeps on saying the condition is false. When I place breakpoints, I see that the values are still equal. I am probably not understanding it correctly. Does anyone know how to solve this?
Here is what I did, as you can see below, the compareTo returns a 1 instead of a 0:
import edu.princeton.cs.algs4.BST;
import java.util.*;
public class MyBST implements Comparable<MyBST>{
private Object e;
public MyBST(Object e){
this.e = e;
}
private Object getE(){
return e;
}
public static void main(String[] args) {
int size = 4;
Random r = new Random();
Set<Integer> tes = new LinkedHashSet<>(size);
Stack<Integer> stack = new Stack<>();
while (tes.size() < size) {
tes.add(r.nextInt(10));
}
System.out.println("possible combinations");
Set<Stack<Integer>> combos = combos(tes, stack, tes.size());
Object[] arr = combos.toArray();
List<String> d = new ArrayList<>();
for (Object s : arr) {
String b = s.toString();
b = b.replaceAll("\\[", "").replaceAll("\\]", "");
d.add(b);
}
int index = 0;
do {
BST<String, Integer> bst1 = new BST<String, Integer>();
BST<String, Integer> bst2 = new BST<String, Integer>();
String key1 = d.get(index);
String key2 = d.get(index);
key1 = key1.replaceAll(" ", "");
String[] m = key1.split(",");
key2 = key2.replaceAll(" ", "");
String[] n = key2.split(",");
System.out.println("1e order");
for (int j = 0; j < m.length; j++) {
System.out.println(m[j]);
bst1.put(m[j], 0);
}
System.out.println("2e order");
for (int j = 0; j < n.length; j++) {
System.out.println(n[j]);
bst2.put(n[j], 0);
}
System.out.println("levelorder 1e BST");
MyBST e = new MyBST(bst1.levelOrder());
MyBST y = new MyBST(bst2.levelOrder());
System.out.println(bst1.levelOrder());
System.out.println("levelorder 2e BST");
System.out.println(bst2.levelOrder());
System.out.println(e.compareTo(y) + "\n");
index++;
} while (index < arr.length - 1);
}
public static Set<Stack<Integer>> combos(Set<Integer> items, Stack<Integer> stack, int size) {
Set<Stack<Integer>> set = new HashSet<>();
if (stack.size() == size) {
set.add((Stack) stack.clone());
}
Integer[] itemz = items.toArray(new Integer[0]);
for (Integer i : itemz) {
stack.push(i);
items.remove(i);
set.addAll(combos(items, stack, size));
items.add(stack.pop());
}
return set;
}
#Override
public int compareTo(MyBST o) {
if (this.e == o.e) {
return 0;
}
else
return 1;
}
}
Here you can find the BST.java class: BST.java
And the output is something like:
The breakpoint at the compareTo method says:
When you're using the == operator you're actually checking to see if the references point to the same object in memory. From your debugging screenshot you can see that they are not. this.e points to object Queue#817 while o.e points to Queue#819.
If all you want to do is test for equality, then just override equals and hashCode. You can do it like this (rest of class omitted):
public class MyBST {
private Object e;
public MyBST(Object e) {
this.e = e;
}
public Object getE(){
return e;
}
#Override
public int hashCode() {
return Objects.hashCode(e);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof MyBST))
return false;
MyBST me = (MyBST) obj;
if (e == null) {
if (me.e != null)
return false;
} else if (!e.equals(me.e))
return false;
return true;
}
}
Implementing Comparable is more involved since you need to check for less, equal, or greater than other instances of MyBST. Unfortunately, the only field in MyBST is an Object which does not tell you anything about its actual fields. So without specific fields with which to test you need to ensure that the Object you pass also implements Comparable. Then you can declare your class like this. Rest of class omitted.
It simply says that
MyBST is comparable.
And the object that is passed in the constructor is comparable.
class MyBST<T extends Comparable<? super T>> implements Comparable<MyBST<T>>{
private T e;
public MyBST(T e){
this.e = e;
}
public T getE(){
return e;
}
#Override
public int compareTo(MyBST<T> o) {
return e.compareTo(o.e);
}
}
The other alternative is to simply pass the actual object type and store it as such, not as Object. Then just implement Comparable in MyBST and use the appropriate fields of the passed object. Lets say the object was an Apple object, you could do this.
class Apple {
String type;
int weight;
}
class MyBST implements Comparable<MyBST> {
private Apple apple;
public MyBST(Apple apple) {
this.apple = apple;
}
#Override
public int compareTo(MyBST e) {
// this could be different depending on how you wanted
// to compare one apple to another. This comparison favors
// type over weight.
// check type - String class implements comparable
int ret = apple.type.compareTo(e.apple.type);
if (ret != 0) {
return ret;
}
// same type so check weight
if (apple.weight < e.apple.weight) {
return -1;
}
if (apple.weight > e.apple.weight) {
return 1;
}
return 0; // equals apples based on criteria
}
}
Finally, you have this.
private Object getE(){
return e;
}
A private getter is not usually very useful. Make it public.

Find array if it is subset of another array

This function should return true only if the parameter object is a subset of the calling object but it always returns true. How to fix it?
public boolean contains(FileCollection other) {
int i = 0;
int j = 0;
for (i = 0; i<other.files.length; i++) {
for (j = 0; j<this.files.length; j++) {
if ((other.files[i]).equals((this.files[j]))) //this refers to the equals method defined in File class
break;
}
if (j==this.files.length)
return false;
}
return true;//this method is in FileCollection class
}
(Since you didn't explicitly express what the data type of the array elements is, I'll assume it's File, inferred from comments.)
If you don't mind converting between data structures, maybe converting your arrays (temporarily) to Collections is the most simple way. For example, converting to List:
/* #param other
* #return true if the calling object contains
* all files in the parameter object, false otherwise
*/
public boolean contains(FileCollection other) {
List<File> myList = Arrays.asList(this.files);
List<File> otherList = Arrays.asList(other.files);
return myList.containsAll(otherList);
}
Based on your clarify of what to be considered as "contains" when duplicated items are allowed, I'd say you need to count the number of existence for each element. Here is how:
Based on the answer of #Eritrean , you can get and store the count to a map. I made modifications to check the count too:
public boolean contains(FileCollection other) {
Map<File,Integer> otherFrequency = Arrays.stream(other.files)
.collect(Collectors.toMap(Function.identity(), v->1,Integer::sum));
Map<File,Integer> thisFrequency = Arrays.stream(this.files)
.collect(Collectors.toMap(Function.identity(), v->1,Integer::sum));
if (thisFrequency.entrySet().containsAll(otherFrequency).entrySet()) {
for (File entry : otherFrequency.entrySet()) {
if (thisFrequency.get(entry) < otherFrequency.get(entry))
return false;
}
return true;
}
return false;
}
For other.files contains this.files to hold, every this.file must be in other.files.
for (int j = 0; j < this.files.length; j++) {
boolean found = false;
for (int i = 0; i < other.files.length; i++) {
if (other.files[i].equals(this.files[j])) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
Not knowing the class of files, probably you can do:
for (String file : this.files) {
boolean found = false;
for (String otherFile : other.files) {
if (otherFile.equals(file)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
Or even
for (String file : this.files) {
boolean found = other.files.indexOf(file) != -1;
if (!found) {
return false;
}
}
return true;
There are nicer datastructures that speed things up, and have predefined methods for things like contains.
With duplicates
Comparator<File> comparator = new Comparator<File>() {
#Override
public int compare(File lhs, File rhs) {
int cmp = lhs.getBase().compareIgnoreCase(rhs.getBase());
if (cmp == 0) {
cmp = lhs.getExtension().compareIgnoreCase(rhs.getExtension());
}
if (cmp == 0) {
cmp = Long.compare(lhs.getSize(), rhs.getSize());
}
return cmp;
}
};
Arrays.sort(this.files, comparator);
Arrays.sort(other.files, comparator);
int otherI = 0;
for (File file : this.files.length) {
boolean found = false;
while (otherI < other.files.length) {
int comparison = comparator.compare(other.files[otherI], file);
++otherI;
if (comparison >= 0) {
found = comparison == 0;
break;
}
}
if (!found) {
return false;
}
}
return true;
By sorting both arrays you can synchronize the comparison at locations in both arrays. The above handles duplicates.
Apart from the #renyuneyun suggestion to convert your arrays into Lists you could also make use of the String contains method
public boolean contains(FileCollection other) {
String myList = Arrays.toString(this.files);
String otherList = Arrays.toString(other.files);
return myList.contains(otherList);
}
Of course both of these suggestions are not the optimum solutions from the complexity point of view, but are for sure the shortests :)
What about using a map with File as key and frequency as value:
public boolean contains(FileCollection other) {
Map<File,Integer> otherFrequency = Arrays.stream(other.files)
.collect(Collectors.toMap(Function.identity(), v->1,Integer::sum));
Map<File,Integer> thisFrequency = Arrays.stream(this.files)
.collect(Collectors.toMap(Function.identity(), v->1,Integer::sum));
return thisFrequency.entrySet().containsAll(otherFrequency.entrySet());
}
Only this answer works for me: (Credit to #Joop Eggen for the Comparator part)
public boolean contains(FileCollection other) {
Comparator<File> comparator = new Comparator<File>() {
#Override
public int compare(File lhs, File rhs) {
int cmp = lhs.getBase().compareToIgnoreCase(rhs.getBase());
if (cmp == 0) {
cmp = lhs.getExtension().compareToIgnoreCase(rhs.getExtension());
}
if (cmp == 0) {
cmp = Long.compare(lhs.getSize(), rhs.getSize());
}
if (cmp == 0) {
cmp = Long.compare(lhs.getPermissions(), rhs.getPermissions());
}
return cmp;
}
};
Arrays.sort(this.files, comparator);
Arrays.sort(other.files, comparator); //THIS AND THE COMPARATOR SORT THE ARRAYS BASED ON ALL FILE ATTRIBUTES
int i = 0;
int j = 0;
if (this.files.length<other.files.length)
return false;
while (i<other.files.length && j<this.files.length) {
if (!(this.files[j].equals(other.files[i])))
j++;
else {
j++;
i++;
}
}
if (i<other.files.length)
return false;
else
return true;
}

How to exclude some fields on TreeSet.contains but sort on all fields

Have some problem with TreeSet or I just realize that it doesn't work as I expected.
I need to exclude some fields when I check if the value already exists, but on sort it should use all the fields.
It looks like TreeSet.contains() use compare or compareTo (comparator, comparable) and not equals().
Here is some example:
import java.util.Comparator;
import java.util.TreeSet;
public class sorter {
static class A {
String name;
int counter;
public A(String a, int counter) {
this.name = a;
this.counter = counter;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.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;
A other = (A)obj;
if (name == null) {
if (other.name != null) return false;
} else if (!name.equals(other.name)) return false;
return true;
}
}
public static class MyComparator implements Comparator<A> {
#Override
public int compare(A a, A b) {
int c = b.counter - a.counter;
if (c == 0) {
return a.name.compareTo(b.name);
};
return c;
}
}
public static void main(String[] args) {
TreeSet<A> set = new TreeSet<>(new MyComparator());
set.add(new A("a", 1));
if (set.contains(new A("a", 2))) {
System.out.println("'a' already exists, do count + count");
}
}
Feels like I breaking some law here, and should re-design it in some way?
Is it possible to achieve what I try to do with a TreeSet or should I go for a simple list?
In someway I have unique items and a list doesn't feel perfect either.
Any ideas?
You could change,
public int compare(A a, A b) {
int c = b.counter - a.counter;
if (c == 0) {
return a.name.compareTo(b.name);
};
return c;
}
to be
public int compare(A a, A b) {
if (Objects.equals(a, b))
return 0;
int c = b.counter - a.counter;
if (c == 0) {
return a.name.compareTo(b.name);
};
return c;
}
This way, if they are "equal" in your sense of equal, the TreeSet will exclude them. Otherwise you sort as you wish. As a note, the Objects.equals() will do the null checking for you.
It is using comparator to find equality.
Hash based collection only used equals and hashcode for equality.
But collection like tree using comparator for equality.
If two objects are equal then compare method should return true and vice versa.
set.contains(new A("a", 2)) its false because compare method wont return true`set.contains(new A("a", 1))` will return true because that method will return true.

Hashcode/Equals override with number range rather than direct equals?

Let's say I have an object that has a number range as two properties, a start and an end to define a numeric band. I want to load these objects into a HashMap. But when I look up on the hashcode and equals with a key object, I want to match on a given number that falls into the range. So I want the hashcode and equals to take any number in a key, and return the object where it falls between the startRange and endRange. So if an object has a startRange of 7 and endRange of 14, passing 9 in a key would retrieve that object. How do I do this? The equals would be straightforward as I would use >= and <=, but I don't want to break the hashcode... I know I can iterate item by item but I'd like to avoid that for performance reasons.
public class MyClass {
private final int marketID;
private final int startRange;
private final int endRange;
...
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + marketID;
/*I don't want to match on startRange and endRange, I want to fall between it! */
result = prime * result + endRange;
result = prime * result + startRange;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyClass other = (MyClass) obj;
if (endRange!= other.endRange)
return false;
if (marketID != other.marketID)
return false;
if (startRange!= other.startRange)
return false;
return true;
}
}

HashMap in java cannot hash MyObject

I have defined a simple private class named SetOb which contains an int and a Set data structure. I have a HashMap in the 'main' method with SetOb as Key and Integer as value. Now as you can see in the main method, when I feed the HashMap with a SetOb instance and then look for an instance with exactly the same value, it returns 'null'. This has happened with me quite a few times before when I use my own defined data structures like SetOb as Key in HashMap. Can someone please point me what am I missing ?
Please note that in the constructor of SetOb class, I copy the Set passed as argument.
public class Solution {
public static Solution sample = new Solution();
private class SetOb {
public int last;
public Set<Integer> st;
public SetOb(int l , Set<Integer> si ){
last = l;
st = new HashSet<Integer>(si);
}
}
public static void main(String[] args) {
Map<SetOb, Integer> m = new HashMap< SetOb, Integer>();
Set<Integer> a = new HashSet<Integer>();
for(int i =0; i<10; i++){
a.add(i);
}
SetOb x = sample.new SetOb(100, a);
SetOb y = sample.new SetOb(100, a);
m.put(x,500);
Integer val = m.get(y);
if(val!= null) System.out.println("Success: " + val);
else System.out.println("Failure");
}
}
Your x and y are not the same object instances hence contains is not able to match y against x, which ends up not finding the matching key/value in the Map.
If you want the match to succeed, please implement(override) hasCode & equals method in SetOb which will compare the field values.
Sample methods(Eclipse generated) as below:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + last;
result = prime * result + ((st == null) ? 0 : st.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;
SetOb other = (SetOb) obj;
if (last != other.last)
return false;
if (st == null) {
if (other.st != null)
return false;
} else if (!st.equals(other.st))
return false;
return true;
}
The default implementation of hashCode uses object identity to determine the hash code. You will need to implement hashCode (and equals) in your private class if you want value identity. For instance:
private class SetOb {
public int last;
public Set<Integer> st;
public SetOb(int l , Set<Integer> si ){
last = l;
st = new HashSet<Integer>(si);
}
#Override
public boolean equals(Object other) {
if (other.class == SetOb.class) {
SetOb otherSetOb = (SetOb) other;
return otherSetOb.last == last && otherSetOb.st.equals(st);
}
return false;
}
#Override
public int hashCode() {
return 37 * last + st.hashCode();
}
}
SetOb needs to override the hashCode() and thus the equals() methods.
Hash-based collections use these methods to store (hashCode()) and retrieve (hashCode()) and equals()) your objects.

Categories

Resources