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
Related
What is the best way to avoid multiple parallel if-else loop. I tried with switch statement as well, but again that doesn't look readable. I have hundreds of such statements:
public static Map getKqvSecureNodeResponse(Sample secureNodeData, Map<String, Object> map) {
if(map.containsKey(Constants.NAME_KQV)) {
map.put(Constants.NAME_KQV, secureNodeData.getNodename());
}
if(map.containsKey(Constants.SPOV)) {
map.put(Constants.SPOV, secureNodeData.getOverride());
}
if(map.containsKey(Constants.SPEP)) {
map.put(Constants.SPEP, secureNodeData.getEnabledProtocol());
}
if(map.containsKey(Constants.SPTO)) {
map.put(Constants.SPTO, secureNodeData.getAuthTimeout());
}
if(map.containsKey(Constants.TLCN)) {
map.put(Constants.TLCN, secureNodeData.getCommonName());
}
if(map.containsKey(Constants.SEDT)) {
map.put(Constants.SEDT, secureNodeData.getEncryptData());
}
if(map.containsKey(Constants.TLCF)) {
map.put(Constants.TLCF, secureNodeData.getKeyCertLabel());
}
if(map.containsKey(Constants.TLCL)) {
map.put(Constants.TLCL, secureNodeData.getCipherSuites());
}
return map;
}
Please note that I have to invoke different getter of secureNodeData for every check.
For each Constants value (e.g. Constants.NAME_KQV), you can provide a Function<Sample, Object> (e.g. sample -> sample.getNodename()).
If you organised it in a structure like Map or enum (here, I used a enum), you could end up with a simple loop:
public static Map<String, Object> getKqvSecureNodeResponse(Sample secureNodeData, Map<String, Object> map) {
for (Constant constant : Constant.values()) {
final String name = constant.getName();
if (map.containsKey(name)) {
map.put(name, constant.getFunction().apply(secureNodeData));
}
}
return map;
}
The enum was defined as:
enum Constant {
NAME_KQV(Constants.NAME_KQV, Sample::getNodename);
// other definitions
final String name;
final Function<Sample, Object> function;
Constant(String name, Function<Sample, Object> function) {
this.name = name;
this.function = function;
}
public String getName() {
return name;
}
public Function<Sample, Object> getFunction() {
return function;
}
}
It seems like this method does a lot. (1) It's unclear why it overrides existing values. (2) The method name is obscure. (3) You are using a raw Map, replace it with Map<String, Object> at least, and figure out how to substitute the Object part. (4)
I feel rethinking the design would help much more than the above approach and these small corrections.
You can try to take advantage of method references:
public static Map getKqvSecureNodeResponse(Sample node, Map<String, Object> map) {
applyParam(Constants.NAME_KQV, map, node::getNodename);
applyParam(Constants.SPOV, map, node::getOverride);
// ...
}
public static void applyParam(String key, Map<String, Object> data, Supplier<Object> getter) {
if (data.containsKey(key)) {
data.put(key, getter.get());
}
}
Alternatively you can use Function references that are instance independent:
private static final Map<String, Function<Sample, Object>> MAPPING;
static {
MAPPING = new LinkedHashMap<>();
MAPPING.put(Constants.NAME_KQV, Sample::getNodename);
MAPPING.put(Constants.SPOV, Sample::getOverride);
}
public static Map getKqvSecureNodeResponse(Sample node, Map<String, Object> map) {
for (String key : MAPPING.keySet()) {
if (map.containsKey(key)) {
map.put(key, MAPPING.get(key).apply(node));
}
}
}
There are many ways how you can approach your specific use case, but method references in general makes developer's life much much easier.
I want to draw a String value from otherclass2. How can I do this?
public class Main {
public static void main(String[] args) {
List<otherclass> list = new ArrayList<otherclass>();
list.add(new otherclass());
}
}
public class otherclass {
private Map<String, otherclass2> maps = new HashMap<String, otherclass2>();
}
public class otherclass2 {
String value = "I want this String";
}
Should I use list.get()? Or is there another approach?
You can not get the value of otherclass2 because your list contain instance of otherclass and the otherclass is have a private maps => so that from the list.get(0) you can not access the maps => can not access maps also mean you can not access otherclass2 value.
Second problem is that you have not init otherclass2 and put it into maps.
To solve you problem (you should create get/setter instead of use public like here):
public class Main {
public static void main(String[] args){
List<otherclass> list = new ArrayList<otherclass>();
list.add(new otherclass());
otherclass other = list.get(0);
String your_value = other.maps.get("first").value;
System.out.println(your_value);
}
}
public class otherclass{
public Map<String, otherclass2> maps = new HashMap<String, otherclass2>();
public otherclass(){
maps.put("first", new otherclass2());
}
}
public class otherclass2{
public String value = "I want this String"
}
In my Java project, I have a need to work with a handful of strings (about 10-30 at a time). I want a data structure to hold them, with properties like so:
Can assign a unique name to each string
The unique names can be used in the code just as if they were variables, with support for IDE auto-complete, no calling getValue() or toString(), etc.
Can iterate over each value in the data structure
In practice, I'd want the code to look something like this:
MagicalDataStructure<String> mds = new MagicalDataStructure(
FirstString = "foo",
SecondString = "bar",
);
/*
This section would output:
foo
bar
*/
for (String value : mds) {
System.out.println(value);
}
/*
This section would output:
The first value is: foo
*/
System.out.println("The first value is: " + FirstString);
Things I've considered:
A class full of static finals. This satisfies #1 and #2, but I can't iterate over them -- at least not without resorting to dark-mojo reflection.
A dictionary. This satisfies #1 and #3, but the keys wouldn't be auto-completable, and there's additional syntax involved in accessing the values.
An enum. This also solves #1 and #3, but accessing the string value takes a little bit of extra code.
Is there a data structure, library, etc that will do what I want?
I would definitely favor a Map for this:
public enum PagePath {
PATH1,
PATH2,
// etc.
}
public static final Map<PagePath, String> ALL_PATHS;
static {
Map<PagePath, String> paths = new EnumMap<>(PagePath.class);
paths.put(PagePath.PATH1, "/html/div[0]/h1");
paths.put(PagePath.PATH2, "/html//form/input[id='firstname']");
// etc.
// Make sure no one breaks things by removing entries
// or by adding enum constants while forgetting to account
// for them in the above Map.
if (!paths.keySet().equals(EnumSet.allOf(PagePath.class))) {
throw new RuntimeException(
"Map does not have entries for all PagePath constants!");
}
ALL_PATHS = Collections.unmodifiableMap(paths);
}
Another possibility, as you’ve mentioned, is using String constants. You can place the initialization of those constants inside the initialization of the “all values” list, to make sure none of them are forgotten:
public static final String PATH1;
public static final String PATH2;
// etc.
public static final Collection<String> ALL_PATHS;
static {
ALL_PATHS = Collections.unmodifiableCollection(Arrays.asList(
PATH1 = "/html/div[0]/h1",
PATH2 = "/html//form/input[id='firstname']",
// etc.
));
}
If someone removes a constant, they’ll be forced to remove its initialization from the Arrays.asList call. If someone adds a constant, and keeps it consistent with the other constants’ declarations, they will be forced to add it to the ALL_PATHS List, since failing to do so would mean it never gets initialized, which compiler will catch.
If your strings are properties your may want to use RessourceBundle or Properties. This can be use to solve problem 1/3.
To solve problem 2, you may create Enum that are Keys to your HashMap so that you need to write hashMap.get(enum) that will auto-complete everything. This solution add words but benefit from auto-completion.
Can you just write a custom method to return the string values using enum?
public enum MagicalDataStructure {
FirstString("foo"),
SecondString("bar");
String value;
MagicalDataStructure(String value) {
this.value = value;
}
public static List<String> getMagicalStrings() {
List<String> strings = new ArrayList<String>();
for (MagicalDataStructure item : MagicalDataStructure.values()) {
strings.add(item.value);
}
return strings;
}
}
And call the function wherever you need to iterate:
public static void main(String[] args) {
for (String magicalString: MagicalDataStructure.getMagicalStrings()) {
System.out.println(magicalString);
}
}
How about this :) The main idea here is the following we use the EnumMap as a base for our CustomEnumMap. My understanding is that you don't need put methods so our first task is to actually throw Unsupported Operation for them. The second step is to define the different enums with the values they are actually representing. The third step is achieved through a static method that converts any Enumeration into our CustomEnumMap. How the map is later used you can see for yourself.
There is one place for improvement though and it is the implementation of the static method. Unfortunately I am just learning java 8 lambdas so I was not able to implement it fast in a good way. But I will work on that and will give you the final implementation of this method later. Or is someone wants to help me out with it is welcome.
public static class CustomEnumMap<K extends Enum<K>,V> extends EnumMap<K, V> {
public CustomEnumMap(EnumMap<K, ? extends V> m) {
super(m);
}
#Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
}
public static enum EnumA {
FIRST("value1"),SECOND("value2"),THREE("value3");
private String value;
private EnumA(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
public static enum EnumB {
FIRST("value1"),SECOND("value2");
private String value;
private EnumB(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
public static <T extends Enum<T>> CustomEnumMap<T, String> toMap(T[] myenum) {
return new CustomEnumMap<T,String>(new EnumMap<T,String>( Arrays.stream(myenum).collect(Collectors.toMap(t->(T)t, t->t.toString()))));
}
public static void main(String args[]) {
CustomEnumMap<EnumA, String> enumA = toMap(EnumA.values());
CustomEnumMap<EnumA, String> enumB = toMap(EnumA.values());
for (String stringA : enumA.values()) {
System.out.print(stringA);
}
System.out.println("");
for (String stringB : enumB.values()) {
System.out.print(stringB);
}
}
I have to use a map which stores keys of type Integer, String and Long only.
One solution: To store type Object and in put method check with instanceof operator. Is there any better solution, maybe with enum
You can use a map and storing Long as String into it
or you can use two different hashmap and duplicate put/get methods. If you have two types, it is probably for two different things, and having two different map should probably be the correct answer
Create a class that has a map as a member and add methods that will store and retrieve int and long as Strings.
class MyMap {
private Map mabObject = Map<String, Object>;
public void add(long key, Object value) {
mapObject.put(Long.toString(key),value);
}
public void add(String key, Object value) {
mapObject.put(key, value);
}
public Object get(long key) {
return mapObject.get(Long.toString(key));
}
public Object get(String key) {
return mapObject.get(key);
}
}
I agree with Paul Boddington's comment, and the need of such trick shows that code smells.
Just for a funny excercise (not for production code) I've made an example that shows what we can do in compile time for limiting types of keys in a map.
For example we can create a wrapper allowing only values of specific classes.
common/map/Wrap.java
package common.map;
import java.util.Arrays;
import java.util.List;
public class Wrap<T> {
private T value;
private Wrap(T value){
this.value = value;
}
public T get() {
return this.value;
}
/*
* it's important to implement this method
* if we intend to use Wrap instances as map's key
*
* and it's needed to see that hash codes are computing differently in different classes,
* and depending on `allowedClasses` contents we can face some unexpected collisions
* so if you care of performance - test your maps usage accurately
*/
public int hashCode() {
return this.value.hashCode();
}
/*
* static
*/
private static List<Class> allowedClasses = Arrays.asList(Long.class, String.class);
public static <T> Wrap<T> create(Class<? extends T> clazz, T value) {
if (!allowedClasses.contains(clazz)) {
throw new IllegalArgumentException("Unexpected class " + clazz);
}
return new Wrap<>(value);
}
public static <T> Wrap<T> create(AllowedClasses allowedClass, T value) {
return create(allowedClass.clazz, value);
}
public enum AllowedClasses {
LONG(Long.class),
STRING(String.class);
private Class clazz;
AllowedClasses(Class clazz) {
this.clazz = clazz;
}
}
}
And let's run it
common/map/Example.java
package common.map;
import common.map.Wrap.AllowedClasses;
import java.util.HashMap;
import java.util.Map;
public class Example {
public static void main(String... args) {
Map<Wrap, Object> map = new HashMap<>();
// next two lines create wrappers for values of types we added to enum AllowedClasses
// but since enums cannot have type parameters, we are not able to check
// if the second parameter type is compatible with a type associated with given enum value
// so I think usage of enum is useless for your purpose
Wrap<?> valLong0 = Wrap.create(AllowedClasses.LONG, "the string in place of Long is OK");
Wrap<?> valString0 = Wrap.create(AllowedClasses.STRING, 12345);
// from the next lines you can see how we can use the Wrap class to keep
// only allowed types to be associated with the map keys
Wrap<Long> valLong = Wrap.create(Long.class, 1L); // legal
Wrap<String> valString = Wrap.create(String.class, "abc"); // legal
Wrap<String> valWrong = Wrap.create(String.class, 123); // doesn't compile
Wrap<Object> valWrong2 = Wrap.create(Object.class, 123); // compiles but throws exception in runtime
Object obj = ThirdParty.getObjectOfUnknownClass();
Wrap<?> valDynamic = Wrap.create(obj.getClass(), obj); // compiles but MAYBE throws exception in runtime
// so we get to this point only if all the wrappers are legal,
// and we can add them as keys to the map
map.put(valLong, new Object());
map.put(valString, new Object());
map.put(valDynamic, new Object());
}
}
HashMap<DataType1,DataType2>hm = new HashMap<DataType1,DataType2>();
or
Map<DataType1,DataType2> m = new HashMap<DataType1,DataType2>();
m.put(key, value);
Instead of DataType1 & DataType2 you can add Integer,String,Long ,etc. and use the put(key,value) method to enter key and values into the HashMap.
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.