Comparing TreeMap contents gives incorrect answer - java

I use a TreeMap as a 'key' inside another TreeMap
ie
TreeMap<TreeMap<String, String>, Object>
In my code 'object' is a personal construct, but for this intance I have used a string.
I have created a pair of TreeMaps to test the TreeMap.CompareTo() and TreeMap.HashCode() methods. this starts with the following...
public class TreeMapTest
public void testTreeMap()
{
TreeMap<String, String> first = new TreeMap<String, String>();
TreeMap<String, String> second = new TreeMap<String, String>();
first.put("one", "une");
first.put("two", "deux");
first.put("three", "trois");
second.put("une", "one");
second.put("deux", "two");
second.put("trois", "three");
TreeMap<TreeMap<String, String>, String> english = new TreeMap<TreeMap<String, String>, String>();
TreeMap<TreeMap<String, String>, String> french = new TreeMap<TreeMap<String, String>, String>();
english.put(first, "english");
french.put(second, "french");
From here I now call the the english item to see if it contains the key
if (english.containsKey(second))
{
System.out.println("english contains the key");
//throws error of ClassCastException: Java.util.TreeMap cannot be cast to
//Java.Lang.Comparable, reading the docs suggests this is the feature if the key is
//not of a supported type.
//this error does not occur if I use a HashMap structure for all maps, why is
//this key type supported for one map structure but not another?
}
However I should note that both HashMap and TreeMap point to the same HashCode() method in the AbstractMap parent.
My first thought was to convert my TreeMap to a HashMap, but this seemed a bit soppy! So I decided to apply the hashCode() method to the 2 treemap objects.
int hc1 = first.hashCode();
int hc2 = second.hashCode();
if(hc1 == hc2)
{
systom.out.printline("values are equal " + hc1 + " " + hc2);
}
prints the following
values are equal 3877431 & 3877431
For me the hashcode should be different as the key values are different, I can't find details on the implementation difference of the hashCode() method between HashMap and TreeMap.
Please not the following.
changing the Keys only to HashMap doesn't stop the ClassCastException error. Changing all the maps to a HashMap does. so there is something with the containsKey() method in TreeMap that isn't working properly, or I have missunderstood - can anyone explain what?
The section where I get the hashCode of the first and second map objects always produces the same output (no matter if I use a Hash or Tree map here) However the if(english.ContainsKey(second)) doesn't print any message when HashMaps are used, so there is obviously something in the HashMap implementation that is different for the compareTo() method.
My principle questions are.
Where can I find details of the types of keys for use in TreeMap objects (to prevent future 'ClassCastException' errors).
If I can't use a certain type of object as a key, why am I allowed to insert it as a key into the TreeMap in the first place? (surely if I can insert it I should be able to check if the key exists?)
Can anyone suggest another construct that has ordered inster / retrieval to replace my TreeMap key objects?
Or have I potentially found strange behaviour. From my understanding I should be able to do a drop in replacement of TreeMap for HashMap, or have I stumbled upon a fringe scenario?
Thanks in advance for your comments.
David.
ps. the problem isn't a problem in my code as I use a personal utility to create a hash that becomes dependent on the Key and Value pairs (ie I calculate key hash values differently to value hash values... sorry that if is a confusing sentence!) I assume that the hashCode method just sums all the values together without considering if a item is a key or a value.
pps. I'm not sure if this is a good question or not, any pointers on how to improve it?
Edit.
from the responses people seem to think I'm doing some sort of fancy language dictionary stuff, not a surprise from my example, so sorry for that. I used this as an example as it came easily to my brain, was quick to write and demonstrated my question.
The real problem is as follows.
I'm accessing a legacy DB structure, and it doesn't talk nicely to anything (result sets aren't forward and reverse readable etc). So I grab the data and create objects from them.
The smallest object represents a single row in a table (this is the object that in the above example I have used a string value 'english' or 'french' for.
I have a collection of these rowObjects, each row has an obvious key (this is the TreeMap that points to the related rowObject).
i don't know if that makes things any clearer!
Edit 2.
I feel I need to elaborate a little further as to my choice of originaly using
hashMap<HashMap<String,string>, dataObject>
for my data structure, then converting to TreeMap to gain an ordered view.
In edit 1 I said that the legacy DB doesn't play nicely (this is an issue with the JDBC.ODBC I suspect, and I'm not about to acquire a JDBC to communicate with the DB). The truth is I apply some modifications to the data as as I create my java 'dataObject'. This means that although the DB may spit out the results in ascending or descending order, I have no way of knowing what order they are inserted into my dataObject. Using a likedHashMap seems like a nice solution (see duffymo's suggestion) but I later need to extract the data in an ordered fashion, not just consecutively (LinkedHashMap only preserves insertion order), and I'm not inclined to mess around with ordering everything and making copies when I need to insert a new item in between 2 others, TreMap would do this for me... but if I create a specific object for the key it will simply contain a TreeMap as a member, and obviously I will then need to supply a compareTo and hashCode method. So why not just extent TreeMap (allthough Duffymo has a point about throwing that solution out)!

This is not a good idea. Map keys must be immutable to work properly, and yours are not.
What are you really trying to do? When I see people doing things like this with data structures, it makes me think that they really need an object but have forgotten that Java's an object-oriented language.
Looks like you want a crude dictionary to translate between languages. I'd create a LanguageLookup class that embedded those Maps and provide some methods to make it easier for users to interact with it. Better abstraction and encapsulation, more information hiding. Those should be your design objectives. Think about how to add other languages besides English and French so you can use it in other contexts.
public class LanguageLookup {
private Map<String, String> dictionary;
public LanguageLookup(Map<String, String> words) {
this.dictionary = ((words == null) ? new HashMap<String, String>() : new HashMap<String, String>(words));
}
public String lookup(String from) {
return this.dictionary.get(from);
}
public boolean hasWord(String word) {
return this.dictionary.containsKey(word);
}
}
In your case, it looks like you want to translate an English word to French and then see if the French dictionary contains that word:
Map<String, String> englishToFrenchWords = new HashMap<String, String>();
englishToFrenchWords.put("one", "une");
Map<String, String> frenchToEnglishWords = new HashMap<String, String>();
frenchToEnglishWords.put("une", "one");
LanguageLookup englishToFrench = new LanguageLookup(englishToFrenchWords);
LanguageLookup frenchToEnglish = new LanguageLookup(frenchToEnglishWords);
String french = englishToFrench.lookup("one");
boolean hasUne = frenchToEnglish.hasWord(french);

Your TreeMap is not Comparable so you can't add it to a SortedMap and its not immutable so you can't add it to a HashMap. What you could use an IdentityMap but suspect an EnumMap is a better choice.
enum Language { ENGLISH, FRENCH }
Map<Language, Map<Language, Map<String, String>>> dictionaries =
new EnumMap<>(Language.class);
Map<Language, Map<String, String>> fromEnglishMap = new EnumMap<>(Language.class);
dictionaries.put(Language.ENGLISH, fromEnglishMap);
fromEnglishMap.put(Language.FRENCH, first);
Map<Language, Map<String, String>> fromFrenchMap = new EnumMap<>(Language.class);
dictionaries.put(Language.FRENCH, fromFrenchMap);
fromEnglishMap.put(Language.ENGLISH, second);
Map<String, String> fromEnglishToFrench= dictionaries.get(Language.ENGLISH)
.get(Language.FRENCH);

To the problem why Hashmap works and Treemap does not:
A Treemap is a "sorted map", meaning that the entries are sorted according to the key. This means that the key must be comparable, by implementing the Comparable interface. Maps usually do NOT implement this, and I would highly suggest you do not create a custom type to add this feature. As duffymo mentions, using maps as keys is a BAD idea.

Related

Confusing Java data structure - static HashMap of own class

I am a Java beginner. Recently I try to study the Java code of my company system and I come up with a very confusing data structure. It is a static variable of HashMap, but the HashMap is type of it class...
This is the example.
public class CustomerList{
static ByteHashMap<CustomerList> Element1 = new HashMap<CustomerList>();
static ByteHashMap<CustomerList> Element2 = new HashMap<CustomerList>();
static ByteHashMap<List<CustomerList>> Element1 = new HashMap<List<CustomerList>>();
....
}
Can anyone explain to me the purpose of this kind of data structure? And in what perspective should I understand this data structure?
edit: The ByteHashMap is come from a open source library
I doubt what you published is valid code because Map or HashMap takes 2 parameter types i.e. Map<K, V>.
If you are actually asking about the practice to set a static structure of its own type on the class level, then yes its OK. It's a convenient way to keep a global collection of a certain class. One sample usage is when you want to ensure == equality when object equals and to return the unique instance.
In first example of HashMap, we will create and add object into our Map. Always use Generics, if you are not working in Java 1.4. Following code will create HashMap with keys of type String and values of type Integer with default size and load factor.
HashMap<String, Integer> cache = new HashMap<String, Integer>();
alternatively you can create HashMap from copying data from another Map or Hashtable as shown in below example:
Hashtable<Integer, String> source = new Hashtable<Integer,String>();
HashMap<Integer, String> map = new HashMap(source);
You can also supply load factor (percentage of size, which if fulled trigger resize of HashMap) and initialiCapacity while creating instance by using overloaded constructor provided in API. Adding elements, also called put operation, requires key and value object. Here is an example of adding key and value in Java HashMap:
map.put(21, "Twenty One");
map.put(21.0, "Twenty One"); //this will throw compiler error because 21.0 is not integer
for more details please refer
http://java67.blogspot.in/2013/02/10-examples-of-hashmap-in-java-programming-tutorial.html

Big database, many maps?

As part of a student project I am building a large database, which could theoretically contain millions of objects.
I'm starting with the firstname (i.e. Person fName = "John").
My plan is to convert "John" to a hashcode, convert the hashcode into an Integer, then store it in the map as - (as integer comparisons are faster).
Now here is my problem - To make the iteration faster, I want to have separate static maps, accessed depending on the first letter of the name. Like
public class FirstNameList {
private static Map<Integer, String> a = new HashMap<Integer, String>();
private static Map<Integer, String> b = new HashMap<Integer, String>();
private static Map<Integer, String> c = new HashMap<Integer, String>();
// etc
public void addFName(String word) {
if (word.length() == 0)
throw new IllegalArgumentException("No name entered");
word = word.toLowerCase();
char x = word.charAt(0);
Integer i = word.hashCode();
x-correctMap.put(i, word);
}
However it does not feel very efficient to have 26 if statements to pick the correct list. Does anyone know a way to pick the correct map? Or just better ideas in general?
I have three suggestions: the first is a direct answer to your question:
First, you could store your maps in a map with the initial character as a key, then simply look up the correct map in the "master map."
Second, this won't be any faster -- likely slower -- than simply storing everything in a single map. Never assume you're more clever than the authors of a library class without measurements to prove there's a problem.
Third, you said "database", so why not use a database instead of all this? Postgres and MySQL are both free and easy to use, and will serve your needs well.
You could nest your maps, key of first map is for letter.
You could have a list of maps, but you likely don't want to use a map at all. Something like TreeMap or a trie would work better, and allow you to have a single data structure.
Also, taking the hashcode of a string and then hashing is likely to be no faster than just using a String.

comparing hashmap data

I was wondering if it is possible to compare items in multiple hashMaps to each other:
HashMap<String,String> valueMap = new HashMap<String, String>();
HashMap<String,Integer> formulaMap = new HashMap<String, Integer>();
What I would basically like to do is something like:
if(the second string in valueMap is the same as the first string in formulaMap){
}
Is there a short way to achieve this or do I have to compare the strings before they are included into the hashMaps. My Integer at this stage of the program is required to take a null value. I can achieve my goals with a multi-dimensional array, but a solution like this would be more elegant and less time consuming.
By using a LinkedHashMap you can have a map that respects the insertion order of different values. Everything you have to do is iterate over the entrySet of the map until you reach the position you're looking for.
Plus: If you also need ordering, you can have a look at the TreeMap which inserts elements in order based on a criteria defined by you (You can pass a Comparator as a parameter for the map).
This order will apply to the keys of the map tough, so if you need value ordering you're going to have to come up with a little more complex solution (as in sorting the entry set directly and adding the values to another map, for example).

Return all close match values in a Map

I have map where each key is a String.
To access a value I can use the .get method of Map. If I want to return anything that matches the key for example : "one, onetwo, onetwothree" , get all values that contain the String "two" so in this case return "onetwo, onetwothree". Is this possible using a Map ?
Im currently using a List and iterating over each String and checking if the String contains the value I am searching for.
There is no such method on any of the Map classes (afaik). You can iterate the keys then check the containment of the fragment, or use a completely different data structure. A trie-map would do it I guess.
Edit:
What you currently doing should be just fine for 99% of all cases. If you are processing extreme amounts of data, use full text indexing. (Which can be done with Suffix trees)
Using a HashMap you will have to iterate over all keys and as soon as a key matches your pattern, then collect that value and keep going until you are done.
Using a TreeMap you can get the keys in sorted order so you could use perhaps this property for a more efficient search.
But I think you should switch to a different data structure. A trie as #zeller also points out seems to do what you want
as others said there are no methods in the java collections API to acheive this.
this is how you do it by iterating over keys of a map
HashMap<String, String> map = new HashMap<String, String>();
map.put("onesample", "1");
map.put("onetwo", "2");
map.put("onetwothree", "3");
for(Entry<String, String> en: map.entrySet()) {
if(en.getKey().contains("two")){
System.out.println(en.getKey());
}
}
}

Java sorting collection/api

I am wondering what API or collection would be best to use for using scanner to search through a document, count the number of times a word appears and create an alphabetical list of both that and for each word a sublist of how many times it is followed by another word.
This is for a class, so please just point me in the right direction as I am completely new to Java and packages, but I don't want any actual coding tips, thank you.
I imagine you could do something like that with Map<String, Map<String, Integer>>. Essentially what you'll have a word, which is associated with a map that contains all the successive words along with their frequency (i.e., the number of times they appear). So what you'd have is:
Map<String, Map<String, Integer>> frequencyTable = new HashMap<String, Map<String, Integer>>();
For sorting, you could create a class that holds a word and its frequency. Then you can use a TreeSet with a comparator (or implement compareTo on your class) to enforce ordering. Then your map would look like this:
Map<String, TreeSet<Frequency>> frequencyTable = new HashMap<String, TreeSet<Frequency>>();
Assuming Frequency is the class that holds information about the string and the number of times it appears. The only difficulty here is looking up the word each time you need to update its frequency because you will have to iterate over the set.
See if its about Sorting........
Here are you few options...
Use Collections.sort(T t) along with Comparable interface, if you want to sort in only
one way.
Use Collection.sort(T t, Comparator c) along with Comparator interface to sort in more than one way.
If uniqueness is important, you can also use TreeSet with comparator.

Categories

Resources