Safely pass HashMap values, without value collection escaping - java

If I have a hashmap like this:
private final Map<String, Collection<String>> descriptions = new HashMap<>();
How do I pass the values safely to an alien method?
If I do this:
myOtherObject.outputDesc(descriptions.values());
then myOtherObject could change the values.
Would this be a safe way of doing this?
myOtherObject.outputDesc(new ArrayList<>(descriptions .values()));

Creating a copy of the collection as you suggested is a way you could go. But the extra effort for copying the list is not needed. Java provides a more convenient way for preventing value changes:
myOtherObject.outputDesc(Collections.unmodifiableCollection(descriptions.values()));

Related

Are there performance benefits to creating a Hashmap with defined types vs. Objects?

When should I do one, and when should I do the other, especially in cases where both suffice? For example, consider if I need a Hashmap of type <String, String>. Is there any reason to do a hashmap of type <Object, Object>?
Are there performance benefits/penalties for either, or is it an issue of clarity?
I suppose you are using HashMap like this:
Map map = new HashMap();
map.put("aKey","value");
String v =(String)map.get("aKey");
You can use it like this:
Map<String,String> map= new HashMap<String,String>();
map.put("aKey","value");
String v = map.get("aKey");
Because it is used as a polymorphic reference. You want String I may want my own custom Class. So make it generic they have used Objects. However you can use generics to avoid cast.
That depends on how you initialize it. According to the JavaDocs, the HashMap can take the data types of what it is storing, so if you do this:
Map hashMap = new HashMap();
map.put("hello", "abc");
You would need to cast to get back your keys and data. However, if you do something like so:
Map<String, String> hashMap = new HashMap<>();
map.put("hello", "abc");
You no longer need to cast the objects that you have. You can take a look here for more information on Generics.
//If you dont make generic then you have to cast the object
HashMap myMap = new HashMap();
// If you make it generic then you dont have to cas the object
HashMap<String, String> myMap2 = new HashMap<String, String>();
An additional clarification is that .... objects are never stored inside a HashMap. Instead the reference/identity of the object is kept inside.
On retrieval, actual reference is picked from the location (given by HashMap) and provided to caller.
Purpose of Java generics is to apply compile time checks only; it has noting to do with run-time.
What if reference points to an Integer and type-cast is expecting a String?
Generics simplify the programming and helps in avoiding Class Cast errors at run-time.

Loading map with keys that are not present, else load default

Here is an example method:
public void loadStuff(Map<String, Object> someMap) {
Map<String, Object> myMap = new HashMap<String, Object>();
//I now load defaults here first
myMap.put("One", someObject);
myMap.put("two", someObject);
myMap.put("three", someObject);
//Now I put the entire someMap so that only those keys that are present in someMap are overridden in myMap and others remain default.
myMap.putAll(someMap);
}
Now, is there a better way of doing these redundant puts as the number of defaults in my scenario are a lot.
Consider creating an initial map with your defaults in, and then use:
// Alternatively, you could use clone()
Map<String, Object> myMap = new HashMap<String, Object>(defaults);
myMap.putAll(someMap);
Aside from anything else that means you can load the "default map" from a properties file or whatever.
If you really don't like the fact that it will put each value twice, you could write a loop to check for each key - but I'd personally just use the above code. It's simple and it should work fine.
Are you wanting to preload a single answer for just a few items, or are you wanting a default for all unfound keys? If you want to change the default answer from null to something else, see this question. If you're wanting to preload some items, then you'll need to put all of them, though it's best not to embed the values in code like that; use a for loop instead that iterates over a single official list of the keys.
If you are going to be initializing blank copies of this Map frequently, it will make more sense to have a template Map that each myMap is constructed from; either a HashMap wrapped as unmodifiable or a Guava ImmutableMap are good choices there. Constructing from a preexisting Map instead of copying all of the elements into the new HashMap is much more efficient since the new one knows how big to make itself.

How can we make a HashMap-typed parameter not editable?

Sorry my title may be misleading.
This is actually from one of my recent JAVA interviews, the interviewer asked me this question: if we have a parameter that is of type HashMap, how can we make sure that in the accepting method, there is no way the user can modify this HashMap (i.e., get() method)
I was saying using final during the interview, which the interviewer didn't appreciate at all, and I've searched online for this topic for a while still have no clue.
Could experts help? Thanks
It sounds like you may be coming from a C++ background, where making the parameter const would indeed prevent the method from modifying the object. In Java, final only prevents assigning to that variable; you can still call methods that modify the object itself.
So, for example:
void callingMethod() {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(1, 2);
badMethod(map);
// NullPointerException
System.out.println(map.get(1).intValue());
}
void badMethod(final HashMap<Integer, Integer> map) {
map.clear();
}
There are several ways to prevent this:
Make a defensive copy before calling the method
Wrap the object in an unmodifiable wrapper using Collections
Use an explicitly immutable type such as those provided by Guava
Using of final doesn't prevent from calling methods on the reference. It only makes the reference itself unchangeable. So your answer was wrong.
My suggestion is to use generic (only for such interviews, not in programs!):
Map<String, String> map = new HashMap< String, String >();
map.put("a", "b"); // OK
Map< ? extends String, ? extends String > mapRO = new HashMap< String, String >();
mapRO.put("a", "b"); // Compile error
String value = mapRO.get("a"); // OK
As you can see, hiding generic types with ? extends ... prevent "writing" methods (like put) to be called, because they usually need the type to be fully defined. But you can still call clear(), so it's rather very pool security concept.
Also you can use some wrapper, that would throw an exceptions on all "writing" method calls.
I think that is immutable or unmodifiable map.
Collections#unmodifiableCollection
It returns an unmodifiable view of the specified map. This method
allows modules to provide users with "read-only" access to internal
maps. Query operations on the returned map "read through" to the
specified map, and attempts to modify the returned map, whether direct
or via its collection views, result in an
UnsupportedOperationException.
Reference
Wrap the map you are passing using the Collections.unmodifiableMap
Collections.unmodifiableMap(myMap) will return the read-only map. You should work on the Map object returned by this.
How about creating an unmodifiable Collection from that Hashmap like here?
You would then do:
yourMethod(Collections.unmodifiableMap(yourHashmap));
Using final only means that you wont be able to overwrite that reference with another reference in this block, it does not prevent any method calls on that object.
There are 2 ways
1) this.map = Collections.unmodifiableMap(map);
2) defensive copying: this.map = new HashMap(map);

Double table in Java

I need a double table from which I get two values from a key or index. I have seen this question already and I want to know what would be a better approach considering also performance.
1) Create a HashMap on this way:
HashMap<Integer, HashMap<String, String>> = ...;
I don't know how to put values inside this the put method, I have this and Eclipse gives me an error prueba.put(0, new Hashtable<"Hi", "Bye">); As you can see I have never used something like this before I am sure is a simple question.
2) Create a HashMap on this way:
HashMap<Integer, YourFancyDatatype>
So I create a class which pack the two or more values I want to have in one Object inside a single key or index.
Which would perform better ? Also if you can help me about how to use number 1) approach. The HashMap will have about 20000 entries.
Thank you very much for your time and help :)
You would want something with a single key and a collection of values. I would suggest using Apache's MultiMap, as they already implement this functionality for you.
Your first approach uses the same datastructure as provided by the Guava's HashBasedTable so you can use it instead.
But if you want the best performance you could try to use something based on arrays (e.g. Guava's ArrayTable)
Anyway I suggest to make some simple performance tests to check which solution performs better.
It you want to do an "in-line" put, you can do this:
prueba.put(0, new HashMap<String, String>() {{put("Hi", "Bye");}});
This employs an anonymous subclass of HashMap that has an instance block that loads the values.
Note that this will create one extra class for the JVM (called MyClass$1 or similar).
I don't know how to put values inside this the put method, I have this
and Eclipse gives me an error prueba.put(0, new Hashtable<"Hi",
"Bye">); As you can see I have never used something like this before I
am sure is a simple question.
Firstly, Hashtable<String, String> is not a subtype of HashMap<String,String>. your HashMap expects a HashMap<String, String> as a value. either insert a hashmap into values or change your hashmap declaration to :
HashMap<Integer, ? extends Map<String, String>> = ...;
however your 2nd approach is more object oriented. so i'd recommend using 2nd approach
The second one would probably be easier in your case in this way
HashMap<Integer, HashMap<String, FancyDataType>> h= ...;
this is how you'll have to insert the data
h=HashMap<Integer, FancyDataType> new Hashtable<Integer,FancyDataType>();
numbers.put(0, new FancyDataType("o","x"));
numbers.put(1, new FancyDataType("t","y"));
numbers.put(1, new FancyDataType("q","z"));
/// ...so one for all 20000
Assuming FancyDataType is something like
class FancyDataType{
String k,v;
FancyDataType(String k,String v){
this.k=k;this.v=v;
}
}

ImmutableMap.of() workaround for HashMap in Maps?

There are utility methods to create ImmutableMap like Immutable.of(Key, value) and its overload.
But such methods don't exist for HashMap or LinkedHashMap in Maps class.
Is there any better way to do this or Guava assumes such a map is always a constant map and ImmutableMap is best option to go with and don't need to provide a utility for HashMap.
Why would you want those for a regular HashMap or LinkedHashMap? You can just do this:
Map<String, Object> map = Maps.newHashMap();
map.put(key, value);
The thing with ImmutableMap is that it is a little bit more cumbersome to create; you first need to make a Builder, then put the key-value pairs in the builder and then call build() on it to create your ImmutableMap. The ImmutableMap.of() method makes it shorter to write if you want to create an ImmutableMap with a single key-value pair.
Consider what you'd have to write if you wouldn't use the ImmutableMap.of() method:
ImmutableMap<String, Object> map = ImmutableMap.builder()
.put(key, value);
.build();
Try Maps.newHashMap(ImmutableMap.of(...))
Maps.newHashMap(Map map)
The difference is that for an immutable map, you have to provide everything up-front, because you can't change it after construction. For mutable maps, you can just create the map and then add the entries. Admittedly this makes it slightly harder to create a map in a single expression, but that doesn't tend to be a problem where you'd want a mutable map anyway, in my experience.
cannot you use the copyOf method of ImmutableMap described here?
it should be something like
Map newImmutableMap = ImmutableMap.copyOf(yourMap);
ImmutableMap.of() returns a hash based immutable map without order.
If you need ordered immutable map, ImmutableSortedMap.of() is a choice.
ImmutableSortedMap provides methods such as firstKey(), lastKey(), headMap(K) and tailMap(K);
Both classes provide copyOf(Map) method.

Categories