HashMap with String and Arraylist - Error Message - java

I am getting an error message.
Suspicious call to java.util.Map.ContainsValue Given object cannot
contain instance of String (Except ArrayList)
This is a small version of the program that I am working with. Could someone suggest how to fix this? Please post code. I am not a strong programer.
import java.util.ArrayList;
import java.util.HashMap;
public class Main {
public static void main(String[] a) {
HashMap<String,ArrayList> map = new HashMap<String,ArrayList>();
//hashMap.put(key, new ArrayList());
map.put("key1", new ArrayList());
map.get("key1").add("value2");
//System.out.println(map.containsKey("key1"));
System.out.println(map.containsValue("value2"));
}
}

You have an HashMap that has Strings for keys and ArrayLists for values:
HashMap<String,ArrayList> map = new HashMap<String,ArrayList>();
You then try and see if it contains a value which is a String
System.out.println(map.containsValue("value2"));
That's very suspicious. Because it can't ever have that.

You can try this example,
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class Main {
public static void main(String[] a) {
HashMap<String, List<String>> map = new HashMap<String, List<String>>();
map.put("key1", new ArrayList());
map.get("key1").add("value2");
// System.out.println(map.containsKey("key1"));
System.out.println(map.containsValue(Arrays.asList(new String[]{"value2"})));
}
}

public class Main {
public static void main(String[] a) {
HashMap<String, List<String>> map = new HashMap<String, List<String>>();
map.put("key1", new ArrayList());
map.get("key1").add("value2");
for (List<String> value : map.values()) {
System.out.println(value.contains("value2"));
}
}
}

The message is pretty clear. The values of your map are of type ArrayList. You pass a String to containsValue() instead of an ArrayList. So you get an error.
BTW, a good habit to get into is to define the types for variables in the least restrictive way possible, say declare map as type Map instead of HashMap
Map<String,List> map = new HashMap<String,ArrayList>();
That way you can substitute different implementations as needed with minimal ripple effect. In this case, by declaring map as just a Map, you are free to switch to TreeMap in the future.

As you are a self-professed "not strong" programmer, I think it would be helpful to point out a couple of other things.
Even in the best of circumstances, this is going to involve testing (on average) half of the values in the hash table. If the value is not there, then it will test all of them. If there are many entries in the map, this will be expensive.
Bear in mind that equality of Lists is defined to be based on pair-wise testing the list elements using the Object.equals(Object).
If you are actually trying to find "value2" in one of the lists, then this approach won't work. You need a nested loop; e.g. #kingdavies solution. But the point is that that will be even more expensive.
There is a hint of "code smell" about this code snippet. I can't be sure without looking at the real application, but a mapping from a String to a list of Objects smells of "object denial"; i.e. trying to use generic collection types to avoid creating a custom class.

Related

How does type-casting fail but work fine after constructor

I am exploring Java and while trying type-casting I ran into the following issue, the casting failed when I called the method the first time but after constructing the List it worked fine.
Looking for a clear explanation to this please :)
Thanks
import java.util.*;
import java.util.Set;
import java.util.HashMap; // import the HashMap class
public class Main{
public static void main(String[] args)
{
Set<Map<String, ?>> rows = new HashSet();
HashMap<String, String> map = new HashMap<String, String>();
map.put("1","one");
map.put("2","two");
HashMap<String, String> mapp = new HashMap<String, String>();
mapp.put("3","three");
mapp.put("4","four");
rows.add(map);
rows.add(mapp);
//WHY THE FOLLOWING DOESN'T WORK ?????
//printItems((List<Map<String, ?>>) rows);
//BUT THE FOLLOWING WORKS FINE
List<Map<String, ?>> listedRows = new ArrayList<>(rows);
printItems(listedRows);
}
public static void printItems(List<Map<String, ?>> items) {
for (Map<String, ?> str: items)
System.out.println(str);
}
}
It doesn't work because the run-time type of rows is HashSet and HashSet does not implement the List interface.
It works when you create listedRows because the run-time type of that object is ArrayList which does implement the List interface.
In your example, you could just just use the Collection abstraction in printItems since all you need to do is iterate through items. This will allow you to invoke the method with a Set or List (or any other Collection) without having to cast or recreate an object.
public static void printItems(Collection<Map<String, ?>> items) {
for (Map<String, ?> str: items)
System.out.println(str);
}
On casting if you are interested:
Casting generally only works when you have an object with a compile-time type that is a parent class of the run-time type and you need to coerce the compile-time type of the object to be the run-time type so that you can invoke a method that is specific to the run-time type.
For example (albeit contrived):
public void addTenObjects(List l) {
if (l instanceof ArrayList)
((ArrayList)l).ensureCapacity(10)
for (int i = 0; i < 10; i++)
l.add(new Object())
}
Both Set and List interfaces extend Collection, but they don't extend each other. I.e. they are sibling, therefore you can't convert one into another via casting.
Since your method printItems simply printing the contents of the argument passed into it, you can use instead of it Java 8 Iterable.forEach(), which expects a Consumer representing the action:
rows.forEach(System.out::println);
No need to type-cast or copy anything, and no need in introducing the method when you have a fairly simple logic like in this case.

Two HashMaps "sharing" the same data?

I have a problem, probably because of my inexperience about HashMaps. Basically, I have a class that contains two HashMap variables:
private Map <String, List<String>> definiciones = new <String, List<String>> HashMap();
private Map <String, List<String>> sinonimos = new <String, List<String>> HashMap();
They store Strings in their Lists when I call their respective functions. The problem is that, for example, when I add a String to the List inside definiciones Map, it also appears somehow in sinonimos.
public void agregarDefinicionAPalabra(String palabra, String definicion) throws PalabraInvalida {
System.out.println(definiciones.get(palabra).size());
if(definicion.equals("")) {
JOptionPane.showMessageDialog(null, "La definición no puede estar vacía");
throw new PalabraInvalida("La definición no puede estar vacía");
}
if (definiciones.containsKey(palabra)) {
definiciones.get(palabra).add(definicion);
} else {
throw new PalabraInvalida("No se ha encontrado la palabra solicitada");
}
System.out.println(sinonimos.get(palabra).get(0));
}
The method adds a String to the definition List, as you can see, in the end I put a print to confirm this idea that I just wrote. Indeed, when I run this method, the sinonimos List gets the definiciones element that I added.
Obviously there is something that I don´t know due to my inexperience in this topic, any suggestions?
Thank you!
You probably put the same list object into both of your HashMaps. In Java, all objects are referenced by their memory address. When you put a list into your HashMap, it's really just storing the memory address of the list.
As an analogy, if I gave the street address of my home to two friends, and both of them wrote down my address, I don't suddenly get two houses. If one of my friends leaves a package on my doorstep, and then the other friend comes by, they will see the package still there.
So, in the code that is creating the Lists, make sure you create distinct, separate lists for the two Maps.
I think you must be using the same List object for the value in the definiciones and sinonimos maps. For example, you must be doing something like:
String palabara = "example";
List<String> list = new ArrayList<>();
definiciones.set(palabra, list);
sinonimos.set(palabra, list);
What happens here is that you now have the same list as the value in both maps. So when you call definiciones.get(palabra) and sinonimos.get(palabra), both of these will give you references to the same list in memory.
What you need to do instead is:
String palabara = "example";
definiciones.set(palabra, new ArrayList<>());
sinonimos.set(palabra, new ArrayList<>());
Diagram
The other two Answers are correct. I'll add this graphic to make visual the problem and solution.
Copy the list
Or if you want the second list to be based on contents of the first, make a copy. To make a copy, feed the first list to the constructor of the second list.
sinonimos.set( palabra , new ArrayList<>( firstArrayList ) ) ;
An entirely new and separate list is created. But the elements in both lists point to the same content objects. So be aware of those element objects being mutable or immutable.

Java - Initialize a HashMap of HashMaps

I am new to java and practicing by creating a simplistic NaiveBayes classifier. I am still new to object instantiation, and wonder what to do to initialize a HashMap of HashMaps. When inserting new observations into the classifier, I can create a new HashMap for an unseen feature name in a given class, but do I need to initialize?
import java.util.HashMap;
public class NaiveBayes {
private HashMap<String, Integer> class_counts;
private HashMap<String, HashMap<String, Integer>> class_feature_counts;
public NaiveBayes() {
class_counts = new HashMap<String, Integer>();
// do I need to initialize class_feature_counts?
}
public void insert() {
// todo
// I think I can create new hashmaps on the fly here for class_feature_counts
}
public String classify() {
// stub
return "";
}
// Naive Scoring:
// p( c | f_1, ... f_n) =~ p(c) * p(f_1|c) ... * p(f_n|c)
private double get_score(String category, HashMap features) {
// stub
return 0.0;
}
public static void main(String[] args) {
NaiveBayes bayes = new NaiveBayes();
// todo
}
}
Note this question is not specific to Naive Bayes classifiers, just thought I would provide some context.
Yes, you need to initialize it.
class_feature_counts = new HashMap<String, HashMap<String, Integer>>();
When you want to add a value to class_feature_counts, you need to instantiate it too:
HashMap<String, Integer> val = new HashMap<String, Integer>();
// Do what you want to do with val
class_feature_counts.put("myKey", val);
Recursive generic data structures, like maps of maps, while not an outright bad idea, are often indicative of something you could refactor - the inner map often could be a first order object (with named fields or an internal map), rather than simply a map. You'll still have to initialize these inner objects, but it often is a much cleaner, clearer way to develop.
For instance, if you have a Map<A,Map<B,C>> you're often really storing a map of A to Thing, but the way Thing is being stored is coincidentally a map. You'll often find it cleaner and easier to hide the fact that Thing is a map, and instead store a mapping of Map<A,Thing> where thing is defined as:
public class Thing {
// Map is guaranteed to be initialized if a Thing exists
private Map<B,C> data = new Map<B,C>();
// operations on data, like get and put
// now can have sanity checks you couldn't enforce when the map was public
}
Also, look into Guava's Mulitmap/Multiset utilities, they're very useful for cases like this, in particular they do the inner-object initializations automatically. Of note for your case, just about any time you implement Map<E, Integer> you really want a Guava Multiset. Cleaner and clearer.
You must create an object before using it via a reference variable. It doesn't matter how complex that object is. You aren't required to initialize it in the constructor, although that is the most common case. Depending on your needs, you might want to use "lazy initialization" instead.
Do not declare your variables with HashMap. It's too limiting.
Yes, you need to initialize class_feature_counts. You'll be adding entries to it, so it has to be a valid map. In fact, initialize both at declaration and not in the constructor since there is only one way for each to start. I hope you're using Java 7 by now; it's simpler this way.
private Map< String, Integer> classCounts = new HashMap<>();
private Map< String, Map< String, Integer>> classFeatureCounts = new HashMap<>();
The compiler will deduce the types from the <>. Also, I changed the variable names to standard Java camel-case style. Are classCounts and classFeatureCounts connected?

read text file and store in hashmap. Then sort in order

File is like this:
name1 134.2
name2 456.7
name3 265.3
...
...
I read the text file and store in HashMap
after that I want to sort in order(by the highest value) but the problem is that because I sort the values in String, I cant compare it.
So..is there a way to put the values of textfile into hashmap in double or integer form?
import java.io.*;
import java.util.*;
class Test
{
public static void main(String[] args) throws FileNotFoundException {
Scanner scanner = new Scanner(new FileReader("score.txt"));
HashMap<String, String> map = new HashMap<String, String>();
while (scanner.hasNextLine()) {
String[] columns = scanner.nextLine().split("\t\t");
map.put(columns[0], columns[1]);
}
System.out.println(map);
}
}
Yes, use HashMap<String,Double> and when putting in the values, convert them to double using Double.parseDouble().
(You can do the same with Float rather than Double, but using Double makes so much more sense usually).
The HashMap does not guarantee order:
This class makes no guarantees as to the order of the map; in
particular, it does not guarantee that the order will remain constant
over time.
This would mean that sorting your hash map would be useless. If you need to load key value pairs like those, you could consider implementing your own class which has the required fields and implements the Comparible interface.
This will allow you to load your objects and then just call Collections.sort() to sort the list of custom objects.

Is this correct usage of a hashtable in Java

package datastrcutures;
import java.util.*;
public class java_hashtable {
public static void ratingofcity() {
Hashtable CityRating = new Hashtable();
CityRating.put("New York", "8");
CityRating.put("Sandton", "9");
}
}
I think you have a typo there, your object type has to be Hashtable instead of Hasttable And you should use Java Generics
Instantiate your hash table object like this:
Hashtable<String, String> cityRating = new Hashtable<String, String>();
And as Java naming convention I would suggest having your object name start with a lower case letter.
The question Is this correct usage of a hashtable is very subjective.
A Map is used to storing sets of keys and values like in your example.
However - things you should consider in your design:
Do I need my map to be Thread-Safe? if not, use a HashMap
Can you have the same rating for two cities? If not, maybe an array would be better?
If the answer to the above question is "yes" - do you need to get all the cities with the same rating? at which case you might need to come up with another data structure, or simply maintain two maps (one of City -> Rating and one of Rating -> City)
Is the rating OK with being a String - wouldn't you prefer an Integer so you can compare them?
Also, a few notes not related to the "design" of this question:
Prefer declaring the interface of the Collection you use instead of the implementation - it makes code changes easier and makes your API more robust.
Use generics - it makes code type safe and easier to read. e.g. Map<String, Integer> cityRatings = new Hashtable<String, Integer>();
import java.util.*;
public class java_hashtable {
public static void ratingofcity() {
Hashtable<String, String> cityRating = new Hashtable<String, String>();
CityRating.put("New York", "8");
CityRating.put("Sandton", "9");
}
}
Hashtable is declared in a wrong way. The changes which I made must work now.

Categories

Resources