How do I grab an index from an array in a HashMap? - java

I've got a HashMap<Object, String[]> I just want to grab the 0 index position from the String[]. How do I do that?
Can I just do this? mMap.get(position)[0]?

Yes, you can do what you've indicated, provided position is a key in the map.

HashMap doesn't have a 0index and it doesn't have a String[]
You cannot do what you ask because it doesn't make sense.
Can I just do this? mMap.get(position)[0]?
You can. Have you tried this to see if it works? Note: it will fail if map.get() returns null

A map is not an array. It's a dictionary of keys that map to items. So there is no ordered index in the Map part, there's a lookup key. This means that a Map has no "next" item.
In the event that you stored a String[] in the map, then you could get the first element of the String array like so:
String first = ((String[])mMap.get(lookupKey))[0];
Since you are using generics, your compiler will make the casting unnecessary, simplifying the answer to
String first = mMap.get(lookupKey)[0];
Note that this is not using the position to access the item stored in the Map, it's using the lookup key. In addition, there is a casting of the returned Object into a String[] (because we stored a String[] in the map earlier), and then there is a dereferncing of the first ('0') element.

Here is a little demo program that does what you're asking.
import java.util.Map;
import java.util.HashMap;
public class FirstElementInHashMap {
public static void main(String[] args) {
Map<Object,String[]> mMap = new HashMap<Object,String[]>();
mMap.put("myKeyA", new String[] { "myValue1", "myValue2", "myValue3" });
mMap.put("myKeyB", new String[] { "myValue4", "myValue5", "myValue6" });
mMap.put("myKeyC", new String[] { "myValue7", "myValue8", "myValue9" });
Object position = "myKeyB";
String[] strings = mMap.get(position);
// make sure position exists in the Map and contains a non-empty array
// so we don't throw an NullPointerException
String firstStringInArray = null;
if (strings != null && strings.length > 0) {
firstStringInArray = strings[0];
}
System.out.println(firstStringInArray);
}
}
The output of the above program is:
myValue4

Related

Suspicious call to 'LinkedHashMap.get'

Hello I have the following code
public static LinkedHashMap<Object, String[]> dataMap = new LinkedHashMap<>();
public static void parseDataset(int line){
String[] dataArr = dataMap.get(dataMap.keySet().toArray()[line]);
}
Since the Object I use as a Key is dynamically generated I have no knowledge about it so I have to find it before I can use it to get its value.
This code gives me the warning Suspicious call to 'LinkedHashMap.get', is that a problem and how would I get rid of the warning?
You don't need to use get: instead of converting the keys to an array, use values() instead. This works because values() iterates in the same order as the corresponding keys():
String[] dataArr = (String[]) dataMap.values().toArray()[line];
But you don't need to use toArray() either, which wastefully allocates an array containing all values, from which you only want one: you can just iterate through the values to get the thing you want:
static String[] nthItem(int n) {
int i = 0;
for (String[] value : dataMap.values()) {
if (i == n) return value;
++i;
}
throw new ArrayIndexOutOfBoundsException();
}
Or:
String[] dataArr = dataMap.values().stream().skip(line).findFirst().orElseThrow();
(Existing implementations of this sort of thing can be found in commonly-used libraries, e.g. Guava's Iterables.get)

Can I create an array of sets?

Here is what I am trying to do.
I am reading in a list of words with each having a level of complexity. Each line has a word followed by a comma and the level of the word. "watch, 2" for example. I wish to put all of the words of a given level into a set to ensure their uniqueness in that level. There are 5 levels of complexity, so ideally I'd like an array with 5 elements, each of which is a set.
I can then add words to each of the sets as I read them in. Later on, I wish to pull out a random word of a specified level.
I'm happy with everything except how to create an array of sets. I've read several other posts here that seem to agree that this can't be done exactly as I would hope, but I can't find a good work around. (No, I'm not willing to have 5 sets in a switch statement. Goes against the grain.)
Thanks.
You can use a map . Use level as key and value as the set which contains the words. This will help you to pull out the value for a given level, When a random word is requested from a level, get the value(set in this case) using the key which is the level and pick a random value from that. This will also scale if you increase the number of levels
public static void main(String[] args) {
Map<Integer, Set<String>> levelSet = new HashMap();
//Your code goes here to get the level and word
//
String word="";
int level=0;
addStringToLevel(levelSet,word,level);
}
private static void addStringToLevel(Map<Integer, Set<String>> levelSet,
String word, int level) {
if(levelSet.get(level) == null)
{
// this means this is the first string added for this level
// so create a container to hold the object
levelSet.put(level, new HashSet());
}
Set<String> wordContainer = levelSet.get(level);
wordContainer.add(word);
}
private static String getStringFromLevel(Map<Integer, Set<String>> levelSet,
int level) {
if(levelSet.get(level) == null)
{
return null;
}
Set<String> wordContainer = levelSet.get(level);
return "";// return a random string from wordContainer`
}
If you are willing to use Guava, try SetMultimap. It will take care of everything for you.
SetMultimap<Integer, String> map = HashMultimap.create();
map.put(5, "value");
The collection will take care of creating the inner Set instances for you unlike the array or List solutions which require either pre-creating the Sets or checking that they exist.
Consider using a List instead of an array.
Doing so might make your life easier.
List<Set<String>> wordSetLevels = new ArrayList();
// ...
for ( i = 0; i < 5; i++ ) {
wordSetLevels.add(new HashSet<String>());
}
wordSetLevels = Collections.unmodifiableList(wordSetLevels);
// ...
wordSetLevels.get(2).add("watch");
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Main {
private Set<String>[] process(List<String> words) {
#SuppressWarnings("unchecked")
Set<String>[] arrayOfSets = new Set[5];
for(int i=0; i<arrayOfSets.length; i++) {
arrayOfSets[i] = new HashSet<String>();
}
for(String word: words) {
int index = getIndex(word);
String val = getValue(word);
arrayOfSets[index].add(val);
}
return arrayOfSets;
}
private int getIndex(String str) {
//TODO Implement
return 0;
}
private String getValue(String str) {
//TODO Implement
return "";
}
}

Hashmap contains key

I found this program in my text book, which basically counts the occurence of each string in the String array tst.
public class Test {
private static HashMap<String, Integer> mp = new HashMap<String, Integer>();
public static void main(String[] args) {
String[] tst = new String[] { "ABC", "DEF", "DEF", "DEF","ABC", "DEF", "ABC" };
checkMap(tst);
}
public static void checkMap(String[] str) {
for (String st : str) {
if (!mp.containsKey(st)) {
mp.put(st, 1);
}
else {
Integer ct = mp.get(st);
if(ct!=null)
{
ct++;
mp.put(st, ct);
}
}
}
for (Map.Entry<String, Integer> entry : mp.entrySet()) {
System.out.println(entry.getKey() + " ocurrs " + entry.getValue()+ " times");
}
}
}
The output for the code is -
ABC ocurrs 3 times
DEF ocurrs 4 times
My question is in the if/else statement here -
if (!mp.containsKey(st)) {
mp.put(st, 1);
}
else {
Integer ct = mp.get(st);
if(ct!=null)
{
ct++;
mp.put(st, ct);
}
}
When we haven't put any entries inside the hashmap (the hashmap is empty), on what basis does this work? Apologies if this is a very basic question, but I found no answer anywhere online that explains this. I am confused with what is written in the if/else loop.
Also, this line here -
Integer ct = mp.get(st);
How can we get the value to which the key is mapped when infact the hashmap is actually empty? I am trying to relate this to an array - If you query elements of an array once its created, but not initialized, it throws a null pointer. Someone, please explain how this works for a hashmap. Once again, apologies for asking such a basic question.
Well, in this line you check whether the map contains a key
if (!mp.containsKey(st)) {
Since there is a ! before the expression, this means "if the map does not contain a key". After that, "then" block follows where you insert a key in the map with value 1 (since it does not exist).
Otherwise if the key does exist (the else block), you take the value for that key, increment it (ct++) and add it again to the map for the same key.
Let me just say that the null check (if(ct!=null)) is not necessary for this code.
General remark on this question:
How can we get the value to which the key is mapped when infact the hashmap is actually empty?
If you try to get something from the HashMap for a key that is not present in the map, the map returns null. That is true for any key you try to get from an empty map.
Can you please explain what this means though - Integer ct = mp.get(st);
map.get(key) returns a value that is stored for that key. The map itself is a collection of key-value pairs, which means: for each key there is one value in the map. So to get the value stored for that key you invoke map.get(key). If you store map.put("ABC", 10) the map will return 10 for map.get("ABC").
This is because of containsKey function checks if the hashMap contains particular key.
If the HashMap is mpty and you try to do a get on non existant key you will get a null value
st is get here by the for (String st : str) loop. It has nothing to do with the HashMap.
if (!mp.containsKey(st)) {
This tests if the HashMap does not contain the key st. If there are no items it obviously can not contain the key. Then in the else block it uses mp.get(st), which will now always succeed because it has been checked that mp contains st (actually, it does not not contain it).
The null check if (ct == null) is here because if for some reason the map contained null for the key in question. That however shouldn't be possible if the code only puts integers to the map and tests for the existence of the key, so the null check coulb be removed.
The test:
if (!mp.containsKey(st))
tests if there is no entry in the map by that key.
It is therefore logical that in the else branch, the entry exists and has a non null value... Which makes the ct == null test redundant.
And when the value exists, the code get()s the existing value, adds 1 to it (in fact it creates a new Integer but that's another story) and put()s back the new value.
Note that that code mixes autoboxing and non autoboxing. mp.put(st, 1) does autoboxing; behind the scenes it really does mp.put(st, new Integer(1)).
Similarly:
Integer ct = mp.get(st);
ct++;
is really:
Integer ct = mp.get(st);
Integer tmp = new Integer(ct.intValue() + 1);
ct = tmp;
The null check is not necessary. Either the key is contained in the map and its value is not null, or it is not contained in the map.
The reason we can be confident about the value never being null is that the map (and all its contents) is defined and used in the method, and there's no opportunity for a null to get it there.
Although the get() method will return null if passed a key that it doesn't contain, that will never happen with this code.
Anyway, the code is inelegant: All those lines can be expressed as one simple line:
mp.put(mp.containsKey(st) ? mp.get(st) + 1 : 1);

Can an array be used as a HashMap key?

If a HashMap's key is a String[] array:
HashMap<String[], String> pathMap;
Can you access the map by using a newly created String[] array, or does it have to be the same String[] object?
pathMap = new HashMap<>(new String[]{"korey", "docs"}, "/home/korey/docs");
String path = pathMap.get(new String[]{"korey", "docs"});
It will have to be the same object. A HashMap compares keys using equals() and two arrays in Java are equal only if they are the same object.
If you want value equality, then write your own container class that wraps a String[] and provides the appropriate semantics for equals() and hashCode(). In this case, it would be best to make the container immutable, as changing the hash code for an object plays havoc with the hash-based container classes.
EDIT
As others have pointed out, List<String> has the semantics you seem to want for a container object. So you could do something like this:
HashMap<List<String>, String> pathMap;
pathMap.put(
// unmodifiable so key cannot change hash code
Collections.unmodifiableList(Arrays.asList("korey", "docs")),
"/home/korey/docs"
);
// later:
String dir = pathMap.get(Arrays.asList("korey", "docs"));
No, but you can use List<String> which will work as you expect!
Arrays in Java use Object's hashCode() and don't override it (the same thing with equals() and toString()). So no, you cannot shouldn't use arrays as a hashmap key.
You cannot use a plain Java Array as a key in a HashMap. (Well you can, but it won't work as expected.)
But you could write a wrapper class that has a reference to the Array and that also overrides hashCode() and equals().
In most cases, where the Strings inside your array are not pathological and do not include commas followed by a space, you can use Arrays.toString() as a unique key. i.e. your Map would be a Map<String, T>. And the get/put for an array myKeys[] would be
T t = myMap.get(Arrays.toString(myKeys));
myMap.put(Arrays.toString(myKeys), myT);
Obviously you could put in some wrapper code if desired.
A nice side effect is that your key is now immutable. Of course, of you change your array myKeys and then try a get(), you won't find it.
Hashing of Strings is highly optimized. So my guess is that this solution, though it feels a bit slow and kludgy, will be both faster and more memory efficient (less object allocations) than #Ted Hopp solution using an immutable List. Just think about whether Arrays.toString() is unique for your keys. If not, or if there is any doubt, (e.g. the String[] comes from user input) use the List.
Like said you need a wrapper class around your array which overrides equality and hashCode.
e.g.
/**
* We can use this instance as HashKey,
* the same anagram string will refer the same value in the map.
*/
class Anagram implements CharSequence {
private final char[] anagram;
public Anagram(String anagram) {
this.anagram = anagram.toCharArray();
Arrays.sort(this.anagram);
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Anagram that = (Anagram) o;
return Arrays.equals(this.anagram, that.anagram);
}
#Override
public int hashCode() {
return Arrays.hashCode(this.anagram);
}
#Override
public int length() {
return anagram.length;
}
#Override
public char charAt(int index) {
return anagram[index];
}
#Override
public CharSequence subSequence(int start, int end) {
return new String(anagram).subSequence(start, end);
}
#Override
public String toString() {
return Arrays.toString(anagram);
}
}
Otherwise declare your map as IdentityHashMap, then the user knows we need to use the same instance for your CRUD.
Ted Hopp is right it will have to be same object.
For information see this example:
public static void main(String[] args) {
HashMap<String[], String> pathMap;
pathMap = new HashMap<String[], String>();
String[] data = new String[]{"korey", "docs"};
pathMap.put(data, "/home/korey/docs");
String path = pathMap.get(data);
System.out.println(path);
}
When you run the above code, it will print "docs".
Since Java 9, you can use Arrays::compare method as a comparator for TreeMap that compares the contents of arrays.
Map<String[], String> map = new TreeMap<>(Arrays::compare);
String[] key1 = {"one", "two"};
String[] key2 = {"one", "two"};
String[] key3 = {"one", "two"};
map.put(key1, "value1");
map.put(key2, "value2");
System.out.println(map.size()); // 1
System.out.println(map.get(key1)); // value2
System.out.println(map.get(key2)); // value2
System.out.println(map.get(key3)); // value2
See also: How to make a Set of arrays in Java?
A running example using the Arrays utility and the hash code it provides:
String[] key1 = { "korey", "docs" };
String value1 = "/home/korey/docs";
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(Arrays.hashCode(key1), value1);
System.out.println(map);
{-1122550406=/home/korey/docs}
This approach is useful if your focus is in storing only. Retrieving using the readable (original) key is simple:
String retrievedValue = map.get(Arrays.hashCode(key1));
System.out.println(retrievedValue);
/home/korey/docs

Store associative array of strings with length as keys

I have this input:
5
it
your
reality
real
our
First line is number of strings comming after. And i should store it this way (pseudocode):
associative_array = [ 2 => ['it'], 3 => ['our'], 4 => ['real', 'your'], 7 => ['reality']]
As you can see the keys of associative array are the length of strings stored in inner array.
So how can i do this in java ? I came from php world, so if you will compare it with php, it will be very well.
MultiMap<Integer, String> m = new MultiHashMap<Integer, String>();
for(String item : originalCollection) {
m.put(item.length(), item);
}
djechlin already posted a better version, but here's a complete standalone example using just JDK classes:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Main {
public static void main(String[] args) throws Exception{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String firstLine = reader.readLine();
int numOfRowsToFollow = Integer.parseInt(firstLine);
Map<Integer,Set<String>> stringsByLength = new HashMap<>(numOfRowsToFollow); //worst-case size
for (int i=0; i<numOfRowsToFollow; i++) {
String line = reader.readLine();
int length = line.length();
Set<String> alreadyUnderThatLength = stringsByLength.get(length); //int boxed to Integer
if (alreadyUnderThatLength==null) {
alreadyUnderThatLength = new HashSet<>();
stringsByLength.put(length, alreadyUnderThatLength);
}
alreadyUnderThatLength.add(line);
}
System.out.println("results: "+stringsByLength);
}
}
its output looks like this:
3
bob
bart
brett
results: {4=[bart], 5=[brett], 3=[bob]}
Java doesn't have associative arrays. But it does have Hashmaps, which mostly accomplishes the same goal. In your case, you can have multiple values for any given key. So what you could do is make each entry in the Hashmap an array or a collection of some kind. ArrayList is a likely choice. That is:
Hashmap<Integer,ArrayList<String>> words=new HashMap<Integer,ArrayList<String>>();
I'm not going to go through the code to read your list from a file or whatever, that's a different question. But just to give you the idea of how the structure would work, suppose we could hard-code the list. We could do it something like this:
ArrayList<String> set=new ArrayList<String)();
set.add("it");
words.put(Integer.valueOf(2), set);
set.clear();
set.add("your");
set.add("real");
words.put(Integer.valueOf(4), set);
Etc.
In practice, you probably would regularly be adding words to an existing set. I often do that like this:
void addWord(String word)
{
Integer key=Integer.valueOf(word.length());
ArrayList<String> set=words.get(key);
if (set==null)
{
set=new ArrayList<String>();
words.put(key,set);
}
// either way we now have a set
set.add(word);
}
Side note: I often see programmers end a block like this by putting "set" back into the Hashmap, i.e. "words.put(key,set)" at the end. This is unnecessary: it's already there. When you get "set" from the Hashmap, you're getting a reference, not a copy, so any updates you make are just "there", you don't have to put it back.
Disclaimer: This code is off the top of my head. No warranties expressed or implied. I haven't written any Java in a while so I may have syntax errors or wrong function names. :-)
As your key appears to be small integer, you could use a list of lists. In this case the simplest solution is to use a MultiMap like
Map<Integer, Set<String>> stringByLength = new LinkedHashMap<>();
for(String s: strings) {
Integer len = s.length();
Set<String> set = stringByLength.get(s);
if(set == null)
stringsByLength.put(len, set = new LinkedHashSet<>());
set.add(s);
}
private HashMap<Integer, List<String>> map = new HashMap<Integer, List<String>>();
void addStringToMap(String s) {
int length = s.length();
if (map.get(length) == null) {
map.put(length, new ArrayList<String>());
}
map.get(length).add(s);
}

Categories