I am trying to create a Set of Strings which is filled with the keys from a Hashtable so a for-each loop can iterate through the Set and put defaults in a Hashtable. I am still learning Java but the way I am trying to do it isn't valid syntax. Could someone please demonstrate the proper way of doing this and explain why my way doesn't work and theirs does.
private Hashtable<String, String> defaultConfig() {
Hashtable<String, String> tbl = new Hashtable<String, String>();
tbl.put("nginx-servers","/etc/nginx/servers");
tbl.put("fpm-servers","/etc/fpm/");
tbl.put("fpm-portavail","9001");
tbl.put("webalizer-script","/usr/local/bin/webalizer.sh");
tbl.put("sys-useradd","/sbin/useradd");
tbl.put("sys-nginx","/usr/sbin/nginx");
tbl.put("sys-fpmrc","/etc/rc.d/php_fpm");
tbl.put("www-sites","/var/www/sites/");
tbl.put("www-group","www");
return tbl;
}
//This sets missing configuration options to their defaults.
private void fixMissing(Hashtable<String, String> tbl) {
Hashtable<String, String> defaults = new Hashtable<String, String>(defaultConfig());
//The part in error is below...
Set<String> keys = new Set<String>(defaults.keySet());
for (String k : keys) {
if (!tbl.containsKey(k)) {
tbl.put(k, defaults.get(k));
}
}
}
Set is not a class, it is an interface.
So basically you can instantiate only class implementing Set (HashSet, LinkedHashSet orTreeSet)
For instance :
Set<String> mySet = new HashSet<String>();
Set is an interface. You cannot instantiate an interface, only classes which implement that interface.
The interface specifies behaviour, and that behaviour can be implemented in different ways by different types. If you think about it like that, it makes no sense to instantiate an interface because it's specifying what a thing must do, not how it does it.
HashMap's keySet() method already creates the set you need, so simply:
Set<String> keys = defaults.keySet();
This is a view of the keys in defaults, so its contents will change when changes are made to the underlying (defaults) map. Changes made to keys will be reflected in the map, as well, but you can only remove...not add...keys from the map.
If you need a copy of the keys that doesn't interact with the original map, then use one of the types suggested, as in:
Set<String> keys = new HashSet( defaults.keySet() );
Related
I have a large enum in Java and I want to break it down into subsets (I think I can do this with EnumSet). I then want to be able to create a hashmap or enummap of only the elements in the EnumSet. How can I achieve this please?
public enum Test { ENUM1, ENUM2, ENUM3, ENUM4, ENUM5 }
EnumSet<Test> testSet = EnumSet.range(ENUM2, ENUM4);
HashMap<testSet, String> testHashMap; <--- Compilation Failure
Edit:
I want to be able to create the hashset to only have Keys for the available enums of the EnumSet and assign a unique string value to each enum in the EnumSet.
Thanks!
testSet is an instance of the EnumSet. You want to define the key type to be EnumSet<Test> instead:
HashMap<EnumSet<Test>, String> testHashMap = new HashMap<>();
...
testHashMap.put(testSet, "string");
It sounds like what you want is a HashMap<Test, String>, and you're going to have to enforce the restriction to the specified range yourself. You can't encode that restriction at the type system level in Java.
I don't think you can get compile time checking as in your example - unless you actually split the enum into multiple smaller enums. At runtime there are some options.
If possible, I would create an unmodifiable view of a map EnumMap<Test, String>. This essentially restricts the map keys to whatever Enum constants you choose.
The actual map is still mutable. If the strings need to change under your control, you can do that internally but only make the immutable map public. If everyone needs to be able to change the strings, you could use something like an unmodifiable EnumMap<Test, StringHolder> and pre-fill it with the the desired enum ranges and and empty holder (StringHolder is a simple pogo, with getter and setter for a single string value. Kind of like a mutable string).
Another option would be to your your own Map type (backed by a HashMap or EnumMap). Whenever a new key is created, check testSet and throw an exception if the key isn't in there.
You'll have to extend HashMap unfortunately. Each time an element is added, you'll have to check it against an EnumSet.
public class EnumSetMap<E, V> extends HashMap<E, V> implements Map<E, V> {
private EnumSet<E> allowed;
public EnumSetMap(EnumSet<E> allowed) {
this.allowed = allowed;
}
#Override
public V put(E key, V value) {
if (allowed.contains(element))
throw new IllegalArgumentException("Key must be in the Enum set.");
return super.put(E, V);
}
}
Your example code would become:
HashMap<Test, String> = new EnumSetMap<Test, String>(testSet);
Take two instances of the same class A, called foo and bar, where:
foo != bar
foo.equals(bar) == true
In other words, foo and bar are different instances but with the same hash code. Now take an instance of Map<A,B> called "map", where foo is a key in map. Is it possible to retrieve foo from Map, using bar? Currently I iterate through the key set and compare every key but is there a faster way? There don't seem to be any methods in Map for retrieving keys.
I am willing to try any data structure that implements Map or can work like a map.
Why do I want to do this? I'm trying to avoid retaining any more instances than necessary. Once I find foo I can release bar.
Thanks in advance...
You can use Apache Commons Collections ™. It has Bidirectional Maps BidiMap.
These represent maps where the key can lookup the value and the value can lookup the key with equal ease.
BidiMap bidi = new TreeBidiMap();
bidi.put("SIX", "6");
bidi.get("SIX"); // returns "6"
bidi.getKey("6"); // returns "SIX"
bidi.removeValue("6"); // removes the mapping
BidiMap inverse = bidi.inverseBidiMap(); // returns a map with keys and values swapped
See also
Commons Collections user guide
HashMap actually does have a method to retrieve the entry but it is package-private. I am not really sure why it isn't public to be honest. I don't think it exposes anything. You can, of course, call it with reflection.
Map<String, String> map = new HashMap<String, String>();
map.put(new String("hello"), "world!");
Method method = (
HashMap.class.getDeclaredMethod("getEntry", Object.class)
);
method.setAccessible(true);
#SuppressWarnings("unchecked")
Map.Entry<String, String> entry = (Map.Entry<String, String>)(
method.invoke(map, new String("hello"))
);
System.out.println(entry.toString().replace("=", " ")); // hello world
The reflection probably makes it not useful in the scenario you've described but I guess it could be useful to others. I wouldn't recommend using it.
Just using the existing map to find duplicztes doesn't seem to be enough. Use a second map, where you put(key,key) pairs. Then:
map.get(key) == null if the key is not already there
map.get(key) == firstObjectAllocated otherwise
This way may faster.
Iterator<A> mapKeyIterator=map.keySet().iterator();
while (mapKeyIterator.hasNext()){
A key;
if((key=mapKeyIterator.next()).equals(bar)) {
return key;
}
}
You can refer to the source code of HashMap
If based on your implementation:
foo.hashCode() == bar.hashCode() and foo.equals(bar)
Then, yes you could directly get the value for key foo by
map.get(bar)
updated: sorry misunderstanding your question before
if you want to keep the key, why you don't just keep the key cached. Then to retrieve the key is hash tree mapping, should be fast.
HashMap<K,V> map = new HashMap<K,V>();
HashMap<K,K> cacheKeys = new HashMap<K,K>();
cacheKeys.put(foo,foo);
map.put(foo,value);
//now you have var bar; you could retrieve the cached key
bar = cacheKeys.get(bar);//this will make bar = foo; the real bar will be gc
//then get the value
val = map.get(bar);
How can I empty a map contents if I keep getting an UnsupportedOperationException when I call clear() method on the map instance ?
You shouldn't. There is something wrong in the contracts of the collaborating objects and their methods. You expect a modifiable map, but get an unmodifiable view. You should redefine the contracts and/or adopt any of the implementations accordingly. If you are not in control of the one, who is passing you that map, then there is probably a reason for it passing you an unmodifiable view.
If the Map is an UnmodifiableMap (for instance, gotten from Collections.unmodifiableMap()), there's little you can (and should) do to modify it, except creating a new reference and clearing it.
Map unmodifiable = Collections.unmodifiableMap(m);
Map newMap = new HashMap(unmodifiable);
newMap.clear();
unmodifiable = newMap;
That said, it is actually possible to modify an UnmodifiableMap. It turns out that Collections.UnmodifiableMap is nothing more than a class implementing the Map interface, and wrapping the actual Map. It delegates on the wrapped Map for its read-only methods, and throws UnsupportedOperationExceptions in its implementations of methods that alter the map. It is possible to get the wrapped Map by reflection, and therefore modify it.
DISCLAIMER: Take into account that this must be used with extreme caution. If the unmodifiable map is provided by a a library or similar, it might be making assumptions about its nature, and violateing its contract can lead to unexpected/undesirable results. Also, this might be relying on internals that might be changed without notice or differ between versions.
// Creates a standard map
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("1", "a");
myMap.put("2", "b");
// Creates an unmodifiableMap. Actually a wrapper for myMap.
Map unmodifiableMap = Collections.unmodifiableMap(myMap);
System.out.println("Size of unmodifiable map is: " + unmodifiableMap.size());
try{
// Get classes declared in java.util.Collections
Class[] classes = Collections.class.getDeclaredClasses();
for(Class cl : classes) {
// Look for the UnmodifiableMap class
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
// The wrapped Map is in an attribute called "m"
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
// Get the wrapped map
Object obj = field.get(unmodifiableMap);
Map map = (Map) obj;
// modify it
map.clear();
}
}
}
catch (Exception e){ System.out.println(); }
System.out.println("Size of unmodifiable map is: " + unmodifiableMap.size());
(credit to this blog post)
A a = new A(); //classA { }
HashMap<String, Object> hm = new Hashmap<String,Object>();
hm.put("A", a);
My question is, How can i put the Object itself instead of "A" in same declaration?
hm.put(`a??`, a);
You simply cannot do that, the language prohibits it. It would only be possible if your class A is a subclass of String which is not possible, since String is declared as final in Java.
With respect to you interview question: It's not possible due to the generic type parameter that was chosen for the declaration. You can read more about that in Bounded Type Parameters.
A a = new A(); //classA { }
Map<A, A> hm = new Hashmap<A, A>();
hm.put(a, a);
But I do not see any point of putting a->a
If the class held a non-changing decent String field, you could use that.
// the id property must be a String, immutable and unique for each instance!
myMap.put(a.getId(), a);
If you want to make any object as a key in your HashMap, then that object has to be immutable.. Because, you don't want anyone to change your key, after you add them to your HashMap..
Just imagine, if your keys are changed after insertion, you won't ever be able to find your inserted value..
But if your key is immutable, then if anyone tries to change your keys, he will actually create a new one for himself, but you will still have yours..
That is what happens in case you use String as your key in HashMap(They can't be changed).. So, if you want your object to be a key, either you make your class a subclass of String (that you can't do), or, just make your class immutable..
This is actually possible using a raw type, like this:
Object key = ...;
Object value = ...;
Map<String, Integer> map = new HashMap<>();//a normal map
Map rawMap = map; // here is the raw type
rawMap.put(key, value); // it works!
This runs fine, but problems arise when you try to use the generic map later:
Integer value = map.get(key);// ClassCastException (unless value actually is an Integer)
That's why you were told that it's a "dirty trick". You shouldn't use it.
Is there a way to add a key to a HashMap without also adding a value? I know it seems strange, but I have a HashMap<String, ArrayList<Object>> amd I want to first be able to create keys as needed and then check if a certain key exists and, if so, put the appropriate value, namely the ArrayList<Object>
Was that confusing enough?
Since you're using a Map<String, List<Object>>, you're really looking for a multimap. I highly recommend using a third-party library such as Google Guava for this - see Guava's Multimaps.
Multimap<String, Object> myMultimap = ArrayListMultimap.create();
// fill it
myMultimap.put("hello", "hola");
myMultimap.put("hello", "buongiorno");
myMultimap.put("hello", "สวัสดี");
// retrieve
List<String> greetings = myMultimap.get("hello");
// ["hola", "buongiorno", "สวัสดี"]
Java 8 update: I'm no longer convinced that every Map<K, SomeCollection<V>> should be rewritten as a multimap. These days it's quite easy to get what you need without Guava, thanks to Map#computeIfAbsent().
Map<String, List<Object>> myMap = new HashMap<>();
// fill it
myMap.computeIfAbsent("hello", ignored -> new ArrayList<>())
.addAll(Arrays.asList("hola", "buongiorno", "สวัสดี");
// retrieve
List<String> greetings = myMap.get("hello");
// ["hola", "buongiorno", "สวัสดี"]
I'm not sure you want to do this. You can store null as a value for a key, but if you do how will be able to tell, when you do a .get("key") whether the key exists or if it does exist but with a null value? Anyway, see the docs.
You can put null values. It is allowed by HashMap
You can also use a Set initially, and check it for the key, and then fill the map.
Yes, it was confusing enough ;) I don't get why you want to store keys without values instead just putting empty arraylists instead of null.
Adding null may be a problem, because if you call
map.get("somekey");
and receive a null, then you do not know, if the key is not found or if it is present but maps to null...
//This program should answer your questions
import java.util.*;
public class attemptAddingtoHashMap { //Start of program
//MAIN METHOD #################################################
public static void main(String args[]) { //main begins
Map<String, ArrayList<Object>> hmTrial = new HashMap<String, ArrayList<Object>>();
ArrayList alTrial = new ArrayList();//No values now
if (hmTrial.containsKey("first")) {
hmTrial.put("first", alTrial); }
else {hmTrial.put("first",alTrial);}
//in either case, alTrial, an ArrayList was mapped to the string "first"
//if you choose to, you can also add objects to alTrial later
System.out.println("hmTrial is " + hmTrial); //empty now
alTrial.add("h");
alTrial.add("e");
alTrial.add("l");
alTrial.add("l");
alTrial.add("o");
System.out.println("hmTrial is " + hmTrial);//populated now
} //end of main
//#############################################################################################################
} //end of class
//Note - removing objects from alTrial will remove the from the hashmap
//You can copy, paste and run this code on https://ide.geeksforgeeks.org/