Using Collections.unmodifiableMap(...), I'm trying to return an unmodifiable view of a map. Let's say I have the following method,
public final Map<Foo, Bar> getMap(){
...
return Collections.unmodifiableMap(map);
}
Why is it legal elsewhere to do the following,
Map<Foo, Bar> map = getMap();
map.put(...);
This doesn't throw an UnsupportedOperationException like I thought it would. Can someone please explain this, or suggest how I can successfully return a truly unmodifiable map?
Are you sure you're not masking your exceptions somehow? This works absolutely fine, in that it throws UnsupportedOperationException:
import java.util.*;
public class Test {
public static void main(String[] args) {
Map<String, String> map = getMap();
map.put("a", "b");
}
public static final Map<String, String> getMap(){
Map<String, String> map = new HashMap<String, String>();
map.put("x", "y");
return Collections.unmodifiableMap(map);
}
}
I suggest you print out map.getClass() on the return value of the method - I would expect it to be an UnmodifiableMap.
I created a small test program and my program threw an 'UnsupportedOperationException' when I tried to put data in.
code:
import java.util.*;
public class TestUnmodifiableMap
{
Map<Integer, String> myMap;
public TestUnmodifiableMap()
{
myMap = new HashMap<Integer, String>();
}
public final Map<Integer, String> getMap()
{
return Collections.unmodifiableMap(myMap);
}
public static void main(String[] args)
{
TestUnmodifiableMap t = new TestUnmodifiableMap();
Map<Integer, String> testMap = t.getMap();
testMap.put(new Integer("1"), "Hello");
}
}
What else are you doing in your class?
There must be something else wrong. There's no way you can put something in that map after you wrapped it as an unmodifiable map.
I would also suggest to return
return Collections.<Foo, Bar>unmodifiableMap(map);
otherwise you will get "unchecked" warnings when compiling your code with -Xlint:unchecked.
Related
Please advise me the correct approach to concatenate the map into single map returned by two classes, shall i make one another class which will do the concatenation , my class structure is looks like
First come the interface
public interface masterCardRule {
public Map<String, List<NTM>> exceute(String jobCode,
String clientlogo) throws Exception;
}
then come the two classes ,first one named masterCardBusinessANFRuleImpl
public class masterCardBusinessANFRuleImpl implements masterCardRule {
// **contains all setters and getters and below the method
// executed that returns map after logic **
public Map<String, List<NTM>> exceute(String jobCode,
String ClientId) throws Exception {
buisness logic
}
}
and then comes the second class named masterCardBusinessCNFRuleImpl
public class masterCardBusinessCNFRuleImpl implements masterCardRule {
// **contains all setters and getters and below the method
// executed that returns map after logic **
public Map<String, List<NTM>> exceute(String jobCode,
String ClientId) throws Exception {
buisness logic
}
}
now please advise how can i combine the concatenation of map into one , so shall i introduce the top level class over it,so that user will call that class and then further these two classes will be called and then sum of there map by adding the result of two individual map into the final map and returned to the caller, so that caller will get a single map by simply calling a single class, please advise how can i do that .
rite now i am calling these two classes as
Map<String, List<NTM>> issuerNTMMap1 = MasterCardADDCNF.exceute(projectMapping.getSpJobCode(), keyInfoModel.getClientLogo());
Map<String, List<NTM>> issuerNTMMap2 = MasterCardADDANF.exceute(projectMapping.getSpJobCode(), keyInfoModel.getClientId());
If you use java8 you can make a stream of the two maps, map them to entrysets then flatmap them to stream of entries then collect them to a Map by entry keys merging the values(lists) with merge function in the tomap() method in the Collectors class.
Sorry for mistakes, I'm posting from the phone
package com.example.demo;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class A {
public static void main(String[] args) {
class B {
}
class C extends B{}
Map<String,List<B>> map1=new HashMap<>();
map1.put("one", Arrays.asList(new B(),new B()));
map1.put("two", Arrays.asList(new B()));
Map<String,List<B>> map2=new HashMap<>();
map2.put("one",Arrays.asList(new C(),new C()));
map2.put("three",Arrays.asList(new C()));
Stream.of(map1,map2)
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(val1, val2)->{
val1.addAll(val2);
return val1;
}
));
}
}
for Java7:
package com.example.demo;
import java.util.*;
public class A {
public static void main(String[] args) {
class B {
}
class C extends B {
}
Map<String, List<B>> map1 = new HashMap<>();
map1.put("one", Arrays.asList(new B(), new B()));
map1.put("two", Arrays.asList(new B()));
Map<String, List<B>> map2 = new HashMap<>();
map2.put("one", Arrays.asList(new C(), new C()));
map2.put("three", Arrays.asList(new C()));
for (Map.Entry<String, List<B>> entry : map2.entrySet()) {
String key = entry.getKey();
List<B> vals = map1.get(key);
if (vals == null || vals.size() == 0) {
map1.put(key, entry.getValue());
} else {
vals.addAll(entry.getValue());
}
}
}
}
if you sure that one map is much smaller then the other there is a sense to iterate through the smallest one, you can check sizes before iterations
I think you should change your interface to
public Map<String, **Collection**<NTM>> exceute(String jobCode,String clientlogo) throws Exception;
You can return from Rule implementation Map<String, Set<NTM>>
Then provide an Aggregator Implementation which will implement the same interface
and would call your Rule classes.
For combining the map in your Aggregator, you can do something like
for (String key: map1.keySet()){
if(map2.contains(key){
map2.get(key).addAll(map1.get(key));
}
}
return map2;
Look for the putAll method is the HashMap class.
You can essentially do issuerNTMMap1.putAll(issuerNTMMap2) and return issuerNTMMap1.
Apache Commons Collection provides a MultiValueMap class:
A MultiValueMap decorates another map, allowing it to have more than one value for a key.
Consider changing your return type from Map<String, List<NTM>> to MultiValueMap<String, NTM>. In this case you could simply write:
issuerNTMMap1.putAll(issuerNTMMap2);
I am new to hash mapping and I was trying to created a nested hash map on one side of the class and create another class to call it out, so here's how my code looks like
public class Hash {
private HashMap<String, HashMap<String, String>> wow = new HashMap<String, HashMap<String, String>>();
public void SetHash(){
wow.put("key", new HashMap<String, Object>());
wow.get("key").put("key2", "val2");
}
public HashMap GetMap(){
return wow;
}
}
And on the other class which is the main class it will be like this:
public static void main(String[] args) {
Hash h = new Hash();
h.SetHash();
System.out.println(h.GetMap.get("key").get("key2"));
}
But when I place the second get, there's an error, so I am not sure if this is possible or if I should actually place the hash directly at the main class.
GetMap is a method, not an attribute, so you have to refer it with parenthesis ():
h.GetMap().get("key")
Now, second error. Your Map<String, Map<String, String> named wow contains a values that are objects of the type Map<String, String> so, before the get, you need get the map:
Map<String, String> m = (HashMap<String, String>) h.GetMap().get("key");
And then you can print it:
System.out.println(m.get("key2"));
if you want an ONELINER (is not really clear, but check explanation in comments):
System.out.println(((HashMap<String, String>) h.GetMap().get("key")).get("key2"));
// ↑ casting parenthesis ↑ (
// ↑ this say group IS a map and allow get() ↑
// ↑ system.out.println parenthesis ↑
NOTE: change also this declaration
wow.put("key", new HashMap<String, Object>());
By
wow.put("key", new HashMap<String, String>());
FINAL CODE:
public class Q37066776 {
public static void main(String[] args) {
Hash h = new Hash();
h.SetHash();
Map<String, String> m = (HashMap<String, String>) h.GetMap().get("key");
System.out.println(m.get("key2"));
}
}
class Hash {
private HashMap<String, HashMap<String, String>> wow = new HashMap<String, HashMap<String, String>>();
public void SetHash() {
wow.put("key", new HashMap<String, String>());
wow.get("key").put("key2", "val2");
}
public HashMap GetMap() {
return wow;
}
}
WORKING ONLINE DEMO
but you can always
Do it better! :=)
As pointed by Andrew
you can change return of the method,
But also many other things like:
using less concrete objects (Map instead of HashMap)
follow conventions (GetMap() would be getMap())
Make Hash a static class with static block
If I had to rewrite your code, my result would be like this:
public class Q37066776 {
public static void main(String[] args) {
System.out.println(Hash.getMap().get("key").get("key2"));
}
}
class Hash {
private static Map<String, Map<String, String>> wow = new HashMap<String, Map<String, String>>();
static {
wow.put("key", new HashMap<String, String>());
wow.get("key").put("key2", "val2");
}
public static Map<String, Map<String, String>> getMap() {
return wow;
}
}
You have 3 errors:
GetMap is a method - you need to write GetMap().
you declared the inner Map as HashMap<String, String> - you cannot initialize the inner map to: wow.put("key", new HashMap<String, Object>());
Change it to wow.put("key", new HashMap<String, String>());
In order to access the inner map from the main - you must declare the returned value of GetMap to be Map<String, HashMap<String, String>> instead of just raw type. Otherwise, the outer class won't know that the outer map value is also a hash map.
Instead of using nested maps, you should use google's Guava Table:
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html
I am newbie in Java 8 Streams. Please advice, how to Convert Stream Stream<HashMap<String, Object>> to HashMap Array HashMap<String, Object>[] ?
For example, I has some stream in code:
Stream<String> previewImagesURLsList = fileNames.stream();
Stream<HashMap<String, Object>> imagesStream = previewImagesURLsList
.map(new Function<String, HashMap<String, Object>>() {
#Override
public HashMap<String, Object> apply(String person) {
HashMap<String, Object> m = new HashMap<>();
m.put("dfsd", person);
return m;
}
});
How I can do something like
HashMap<String, Object>[] arr = imagesStream.toArray();
?
Sorry my bad English.
The following should work. Unfortunately, you have to suppress the unchecked warning.
#SuppressWarnings("unchecked")
HashMap<String, Object>[] arr = imagesStream.toArray(HashMap[]::new);
The expression HashMap[]::new is an array constructor reference, which is a kind of method reference. Method references provide an alternative way to implement functional interfaces. You can also use a lambda expression:
#SuppressWarnings({"unchecked", "rawtypes"})
HashMap<String, Object>[] array = stream.toArray(n -> new HashMap[n]);
Before Java 8, you would have used an anonymous inner class for that purpose.
#SuppressWarnings({"unchecked", "rawtypes"})
HashMap<String, Object>[] array = stream.toArray(new IntFunction<HashMap[]>() {
public HashMap[] apply(int n) {
return new HashMap[n];
}
});
I have a following scenario:
public class MapTest {
String name = "guru";
/**
* #param args
*/
public static void main(String[] args) {
MapTest mapTest = new MapTest();
Map map = new HashMap();
map.put("name", mapTest.name);
System.out.println(mapTest.name);
map.put("name", "raj");
System.out.println(mapTest.name);
}
}
output is:
guru
guru
is there any way that I can get the output as
guru
raj
ie. I want the HashMap map and the member variable name to in sync.
Thanks.
You can't do that. That's not the way Java works. When you write:
map.put("name", mapTest.name);
that's putting the current value of mapTest.name into the map. After the argument has been evaluated, it's completely independent of the original expression.
If you need to do something like this, you would have some sort of mutable wrapper class - you'd put a reference to the wrapper into the map, and then you can change the value within the wrapper, and it doesn't matter how you get to the wrapper, you'll still see the change.
Sample code:
import java.util.*;
class StringWrapper {
private String value;
StringWrapper(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return value;
}
}
public class Test {
public static void main(String[] args) throws Exception {
Map<String, StringWrapper> map = new HashMap<String, StringWrapper>();
StringWrapper wrapper = new StringWrapper("Original");
map.put("foo", wrapper);
System.out.println(map.get("foo"));
wrapper.setValue("Changed");
System.out.println(map.get("foo"));
}
}
You appear to be confused about how maps work. For a better idea, try printing out the map.
map.put("name", mapTest.name);
System.out.println(map);
map.put("name", "raj");
System.out.println(map);
You will get:
{"name"="guru"}
{"name"="raj"}
Note that mapTest.name == "guru" always as you never modify it.
Java maps just don't support anything like this. When you do
map.put("name", mapTest.name);
You put a reference to the object referenced by mapTest.name in the map. When you do
map.put("name", "raj");
You put a reference to the new String object in the map. The reference to mapTest.name isn't in the map anymore.
Out of curiosity, why don't you want to just use map.get("name")?
If you want something to be performed dynamically, you should use a function/method.
public class MapTest {
private final Map<String, String> map = new HashMap<String, String>();
public String name() {
return map.get("name");
}
public static void main(String[] args) {
MapTest mapTest = new MapTest();
mapTest.map.put("name", "guru");
System.out.println(mapTest.name());
mapTest.map.put("name", "raj");
System.out.println(mapTest.name());
}
}
prints
guru
raj
I have a nested map:
Map<Integer, Map<Integer, Double>> areaPrices = new HashMap<Integer, Map<Integer, Double>>();
and this map is populated using the code:
while(oResult.next())
{
Integer areaCode = new Integer(oResult.getString("AREA_CODE"));
Map<Integer, Double> zonePrices = areaPrices.get(areaCode);
if(zonePrices==null)
{
zonePrices = new HashMap<Integer, Double>();
areaPrices.put(areaCode, zonePrices);
}
Integer zoneCode = new Integer(oResult.getString("ZONE_CODE"));
Double value = new Double(oResult.getString("ZONE_VALUE"));
zonePrices.put(zoneCode, value);
myBean.setZoneValues(areaPrices);
}
I want to use the value of this Map in another method of the same class. For that I have a bean.
How do I populate it on the bean, so that I can get the ZONE_VALUE in this other method
In my bean I added one new field as:
private Map<Integer, Map<Integer, Double>> zoneValues;
with getter and setter as:
public Map<Integer, Map<Integer, Double>> getZoneValues() {
return zoneValues;
}
public void setZoneValues(Map<Integer, Map<Integer, Double>> areaPrices) {
this.zoneValues = areaPrices;
}
What I am looking for to do in the other method is something like this:
Double value = myBean.get(areaCode).get(zoneCode);
How do I make it happen :(
I would like to suggest a different, hopefully more readable solution:
public class PriceMap {
private Map<Integer, Map<Integer, Double>> priceMap =
new HashMap<Integer, Map<Integer, Double>>();
// You'd use this method in your init
public Double setPrice(Integer areaCode, Integer zoneCode, Double price) {
if (!priceMap.containsKey(zoneCode)) {
priceMap.put(zoneCode, new HashMap<Integer, Double>());
}
Map<Integer, Double> areaMap = priceMap.get(zoneCode);
areaMap.put(areaCode, price);
}
public void getPrice(Integer areaCode, Integer zoneCode) {
if (!priceMap.containsKey(zoneCode)) {
// Eek! Exception or return null?
}
Map<Integer, Double> areaMap = priceMap.get(zoneCode);
return areaMap.get(areaCode);
}
}
I think this is a better, more readable abstraction which, very importantly, makes it easier for you or someone else to read after a few months.
EDIT Added get get
If you're stuck with a get(areaCode).get(zoneCode) (order reversed), but myBean is entirely yours, you could do something like:
public class MyBean {
// I suppose you have this already
private final Map<Integer, Map<Integer, Double>> priceMap =
new HashMap<Integer, Map<Integer, Double>>();
private class LooksLikeAMap implements Map<Integer, Double> {
private Integer areaCode = areaCode;
public LooksLikeAMap(Integer areaCode) {
this.areaCode = areaCode;
}
public Double get(Object zoneCode) {
if (!priceMap.containsKey(zoneCode)) {
// Eek! Exception or return null?
}
Map<Integer, Double> areaMap = priceMap.get(zoneCode);
return areaMap.get(areaCode);
}
// Implement other methods similarly
}
public Map<Integer, Double> get(Integer areaCode) {
return new LooksLikeAMap(areaCode);
}
}
OK, programming in a HTML textarea is not my strong suit, but the idea is clear.
Make some Map like structure backed by the complete data set, and initialize that
Map structure with the required AreaCode.
If the idea is not clear, post a comment fast as it's late here:)
EDIT
I am an idiot. I thought the data was zone first, then area while the get should be area first, then zone. In this case the Map already has the right structure, first area then zone, so this is not necessary. The get-get is by default if you make
public MyBean {
public Map<Integer, Double> get(Integer areaCode) {
return data.get(areaCode);
}
}
To start with, all you need is
myBean.getZoneValues(areaCode).get(zoneCode);
the while loop has an annoyance, you need to call myBean.setZoneValues(areaPrices);
out side the while loop
You can't directly control the second get() call because you have a nested Map, you'll need to return the appropriate nested Map to be able to do what you want. A getter like this should do it:
public Map<Integer, Double> get(Integer areaCode) {
return zoneValues.get(areaCode);
}
So when the client code calls get(areaCode) a map will be returned that they can then call get(zoneCode) on.
I'd suggest that you refactor to eliminate the nested Maps though, because you can't stop client code from changing the returned Map, the code is tough to read and you'll have problems if you want to add any more functionality - imagine that you want to provide a String description of an area code in future.
Something like a Map<Integer, AreaCode> where AreaCode is an object that contains what you currently have as a nested Map might be a good place to start.