2

I have this code (below) that when I print the treemap, I can clearly see the key,value pairs. Every key has a value (no null values in the output). When I get the first key, it will give me the key, but when I try to get the value based on the key, it will return null.

TreeMap<String, Double> cosinesimilarityvalues = simvalfordoc.returnsortedcosinesimilarityvalues();
System.out.println(cosinesimilarityvalues);
String topkey = cosinesimilarityvalues.firstKey();      
System.out.println(topkey);
Double topvalue = cosinesimilarityvalues.get(topkey);
System.out.println(topvalue);
topones.put(topkey, topvalue);

Here is part of an output :

{article04_C9,article08_C12=0.0, article04_C9,article18_C10=0.0, article04_C9,article07_C1=0.0, article04_C9,article03_C10=0.0, article04_C9,article01_C10=0.0, article04_C9,article07_C10=0.0, article04_C9,article17_C10=0.0, article04_C9,article10_C10=0.0, article04_C9,article05_C10=0.0, article04_C9,article11_C10=0.0, article04_C9,article02_C10=0.0, article04_C9,article13_C10=0.0, article04_C9,article02_C13=5.676594773265355E-4, article04_C9,article02_C11=6.228132014119322E-4, article04_C9,article06_C10=6.732460014209593E-4, article04_C9,article12_C10=0.0011438670619737105, article04_C9,article03_C3=0.0011907203907551985, article04_C9,article03_C11=0.0012323612320990097}

So I should be getting article04_C9,article08_C12 as firstKey() (which I do) but when I go to retrieve the value associated with that key, it returns null.

Here is the code that I'm using to populate the treemap

HashMap<String, Double> cosinesimilarityvalues = new HashMap<String, Double>();
TreeMap<String, Double> sortedcosinesimilarityvalues = new TreeMap<String, Double>();
public void comparecosinesimilarityvalues(List<tfidfvalues> matrix, tfidfvalues currentvector) {
    String articlename = currentvector.returnarticlename();
    String foldername = currentvector.returnfoldername();
    ArrayList<Double> tfidfval = currentvector.returntfidfvaluesforrow();
    articlefolder = articlename+ "_" + foldername;
    CosineSimilarity calculator = new CosineSimilarity();
    for(int i = 0; i < matrix.size(); i++) {
        String compvectorarticlename = matrix.get(i).returnarticlename();
        String compvectorfoldername = matrix.get(i).returnfoldername();
        ArrayList<Double> compvector = matrix.get(i).returntfidfvaluesforrow();

        Double cosinesimilarity = calculator.CosineSimilarityCalc(tfidfval, compvector);

        String comparingwhat = compvectorarticlename + "_" + compvectorfoldername;

        String comparingthese = articlefolder + "," + comparingwhat;
        cosinesimilarityvalues.put(comparingthese, cosinesimilarity);
    }

    Iterator<Map.Entry<String, Double>> iterator = cosinesimilarityvalues.entrySet().iterator();
    while(iterator.hasNext()) {
        Map.Entry<String, Double> entry = iterator.next();
        if((entry.getValue() > 0.989 && entry.getValue() < 1) || entry.getValue() > 1) {
            iterator.remove();
        }
    }

    sortedcosinesimilarityvalues = sortMapByValue(cosinesimilarityvalues);
}

public TreeMap<String, Double> returnsortedcosinesimilarityvalues() {
    return sortedcosinesimilarityvalues;
}

Here is the function I am using to sort by value...if that helps

from : https://www.programcreek.com/2013/03/java-sort-map-by-value/

class ValueComparator implements Comparator<String>{

    HashMap<String, Double> map = new HashMap<String, Double>();

    public ValueComparator(HashMap<String, Double> map){
        this.map.putAll(map);
    }

    @Override
    public int compare(String s1, String s2) {
        if(map.get(s1) >= map.get(s2)){
            return 1;
        }else{
            return -1;
        }   
    }
}

public TreeMap<String, Double> sortMapByValue(HashMap<String, Double> map){
    Comparator<String> comparator = new ValueComparator(map);
    //TreeMap is a map sorted by its keys. 
    //The comparator is used to sort the TreeMap by keys. 
    TreeMap<String, Double> result = new TreeMap<String, Double>(comparator);
    result.putAll(map);
    return result;
}

I'm not sure what I'm doing wrong. Please help!

Thanks!


UPDATE

I was able to retrieve the top key and top value via

Map.Entry<String, Double> entry1 = cosinesimilarityvalues.firstEntry();
String topkey = entry1.getKey();
Double topvalue = entry1.getValue();

but I have no idea why this works and the other method does not work. Although my code now works, I wish I could find out what the difference is!

jaewhyun
  • 71
  • 2
  • 11
  • 1
    Can you add the data here...not the image...just print the map data and add here – utkarsh31 Nov 16 '17 at 03:51
  • just edited it! – jaewhyun Nov 16 '17 at 03:57
  • If you are referring to the top block of the code with sysout's then that work's just fine with the "hard-coded" values you posted. Can you recheck the data once which you appended here? the topkey is article04_C9,article01_C10 – utkarsh31 Nov 16 '17 at 04:23
  • It doesn't work - I have the treemap which I printed above, but if I tried to get the value for article04_C9,article08_C12, it returns null. However, from the treemap printout we can clearly see that the value is 0.0 – jaewhyun Nov 16 '17 at 04:30
  • In what scenario are you getting "article04_C9,article08_C12" as the top value? – utkarsh31 Nov 16 '17 at 04:33
  • I just need to retrieve the key and value so that I could put it into a different hashmap. – jaewhyun Nov 16 '17 at 04:35
  • Can you do an equals check before `Double topvalue = cosinesimilarityvalues.get(topkey);` of topkey which you see when you print the output and what you are getting in the topkey. – utkarsh31 Nov 16 '17 at 04:50
  • Stackoverflow tells me to not extend discussions in comments - but I can check the outputs manually, and it does get the exact key. – jaewhyun Nov 16 '17 at 04:54
  • Add an equals clause and check maybe you missed something...this is an unusual scenario...you can append your previous comment with yes or no.. – utkarsh31 Nov 16 '17 at 04:56
  • I was able to find out the solution but I have no idea why it's any different. Please check the edit with the update! – jaewhyun Nov 16 '17 at 05:00

3 Answers3

2

Usually a Map starts "misbehaving" if its internal state is inconsistent. With a TreeMap, as in your case, that would be caused by a Comparator which is not stable, meaning it doesn't always return the same result for the same input values.

Not having access to the whole code it's hard to pinpoint the cause, one thing to notice is however that your TreeMap becomes inconsistent if you modify it after the initial creation within the sort method. That's because your comparator relies on the state of the map at the time it's instantiated, any later changes are not seen by the comparator.

The firstKey() and firstEntry() methods don't use the comparator for retrieval, they simply go all the way "left" in the binary tree that backs the TreeMap. However get(key) uses the comparator to find the key in the tree, which in your case does not work correctly.

Another possible cause from this answer to a similar question is that your comparator does not respect the requirement

sgn(compare(x, y)) == -sgn(compare(y, x))

That is, if two entries A and B have the same value, comparing (A,B) with your code yields 1, and comparing (B,A) yields 1 as well, so the order of the 2 elements is not properly defined. You should also handle the case of

map.get(s1).equals(map.get(s2))

when comparing two entries and return 0. Note the usage of equals and not ==, as you don't want to compare two Double objects by reference, but by value instead.

Andrei Socaciu
  • 1,198
  • 9
  • 20
  • sorry, I'm super new to Java. If I wrote the comparator correctly, would it be this? @Override public int compare(Stirng s1, String s2) { if(map.get(s1) > map.get(s2)) { return -1; } else if(map.get(s1) < map.get(s2)) { return 1; } else { // if map.get(s1).equals(map.get(s2)) return 0; } } – jaewhyun Nov 16 '17 at 07:21
  • Also, thank you for the detailed answer! This really helps because I literally had no idea what I was doing and why I was doing it. Thank you!! – jaewhyun Nov 16 '17 at 07:30
1

Sorry not able to post comment in the question yet so have to post here as an answer. Maybe you can try first to copy the whole string to the get input to see if you still get null

Double topvalue = cosinesimilarityvalues.get(“article04_C9,article08_C12”);

I find out the problem is with your overriden compare method for custom Comparator. The below replicates your issue

    import java.util.Comparator;
import java.util.TreeMap;
public class MyTreeMapComparator {
    public static void main(String a[]){
        //the treemap sorts by key
        TreeMap<String, String> hm = new TreeMap<String, String>(new MyComp());
         //add key-value pair to TreeMap
         hm.put("java", "language");
         hm.put("computer", "machine");
         hm.put("india","country");
         hm.put("mango","fruit");
         System.out.println(hm.get("java"));
     }
}
class MyComp implements Comparator<String>{
    @Override
    public int compare(String str1, String str2) {
        if (str1.compareTo(str2) >= 0) {return 1;}
        else {return -1;}
    }
}

Then if you change to this then it works fine

    import java.util.Comparator;
import java.util.TreeMap;
public class MyTreeMapComparator {
    public static void main(String a[]){
        //the treemap sorts by key
        TreeMap<String, String> hm = new TreeMap<String, String>(new MyComp());
         //add key-value pair to TreeMap
         hm.put("java", "language");
         hm.put("computer", "machine");
         hm.put("india","country");
         hm.put("mango","fruit");
         System.out.println(hm.get("java"));
     }
}
class MyComp implements Comparator<String>{
    @Override
    public int compare(String str1, String str2) {
        if (str1.compareTo(str2) == 0) {return 0;}
        else if (str1.compareTo(str2) > 0) {return 1;}
        else {return -1;}
    }
}
Nguyen Pham
  • 444
  • 1
  • 4
  • 14
  • Okie. Now it’s strange. Some things I can think of to try is print directly without assign to the Double variable; try get for other key as well (just need to change the hardcoded string); – Nguyen Pham Nov 16 '17 at 04:21
  • Nope - still null :( – jaewhyun Nov 16 '17 at 04:24
  • This line returns the correct key right System.out.println(topkey);? – Nguyen Pham Nov 16 '17 at 04:24
  • yep - the key is returned but the value associated with the key returns null – jaewhyun Nov 16 '17 at 04:25
  • If you use iterator to print pair by pair then how? It shows the key and 0.0? Sorry can’t replicate your code since reading from data too so just try to suggest for debugging – Nguyen Pham Nov 16 '17 at 05:12
  • I didn't use the iterator (I think?) but for some reason, if I do it via my updated code, it works. I just wish I understood why! – jaewhyun Nov 16 '17 at 05:17
  • Update my answer. Try once and let me know if it’s working. – Nguyen Pham Nov 16 '17 at 06:40
  • You are right in that I did have a problem in the comparator! So thank you :) - however, I'm not super familiar with compareTo...would you mind explaining a bit further? – jaewhyun Nov 16 '17 at 07:32
  • You can refer to [https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html?is-external=true] espcailly this part `a sorted map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal`. The compareTo (or compare) method is important and need to be correct since it will be used in a sorted Map class to compare the Key. In this case when you use get, it compares input String to the existing Key and since no case 0 is returns, it understands as no same key in the TreeMap – Nguyen Pham Nov 16 '17 at 07:54
1

Made few changes to your existing implementation of the comparator, in order to adhere to the contract of comparator implementation:

  • Replaced Generic parameter from String to Object(not sure if its really needed)
  • Type casting value of map.get(s1) & fetching double value to compare
  • Separated the >, < & ==

It seems to work using the below implementation of the comparator.

    HashMap<String, Double> map = new HashMap<String, Double>();

    public ValueComparator(HashMap<String, Double> map){
        this.map.putAll(map);
    }

    @Override
    public int compare(Object s1, Object s2) {
        if(((Double) map.get(s1)).doubleValue() > ((Double) map.get(s2)).doubleValue()){
            return 1;
        }
        else if (((Double) map.get(s1)).doubleValue() == ((Double) map.get(s2)).doubleValue()){
    return ((String)s1).compareTo(((String)s2));
         }
         else{
            return -1;
        }   
    }

For the below sample values:

treemap.put( "two",2.0);
   treemap.put( "one",1.0);
   treemap.put( "three",3.0);
   treemap.put( "six",6.0);
   treemap.put( "five",5.0);

Output:

First key is: one
Value against first key: 1.0
Akash Mishra
  • 682
  • 1
  • 5
  • 13