Java multiple keys direct to the same values - java

Is there a way to points several keys to the same value?
i.e.
HashMap<String, Float> mymap = new HashMap<>();
mymap.put("hello",5f);
mymap.put("bye",5f);
~somehow point bye and hello to the same value~
mymap.put("bye", mymap.get("bye") +5f)
mymap.get("hello") == 10

Java HashMap stores references to Objects. If you store same object with two different keys, the keys will point to the same value.
But that is not your problem. Your problem is that you are using Float values and Float is an immutable data type. You can not change it's value once it has been created. To achieve what you want to do you need to either create a mutable Float or store the float in a container and store that container in the map. One of the most simple containers would be a single element array (though I would only use it in an example code, never in a production code as it is error prone and it is "self undocumentable").
HashMap<String, Float[]> mymap = new HashMap<>();
Float[] val = new Float[] { 5f };
mymap.put("hello", val);
mymap.put("bye", val);
...
mymap.get("bye")[0] = mymap.get("bye")[0] + 5f;
mymap.get("hello")[0] == 10f

You would need a mutable object as Value for that, for example:
static class FLoatHolder {
private float f;
public FLoatHolder(float f) {
this.f = f;
}
public float getF() {
return f;
}
public void setF(float f) {
this.f = f;
}
}
Map<String, FLoatHolder> map = new HashMap<>();
FLoatHolder fh = new FLoatHolder(5f);
map.put("bye", fh);
map.put("hello", fh);
FLoatHolder holder = map.get("bye");
holder.setF(holder.getF() + 0.5f);
map.put("bye", holder);
System.out.println(map.get("hello").getF());

If you just want two keys to point to the same value, that is perfectly fine. Maps don't care what they point to, just that there aren't conflicting keys.
If you want to add the integer values together, then your pseudocode works as you intend.
If you want pointer like behavior where changing the value of key A affects the value of key B, then you'd have to make a wrapper object and use fields.
Something like:
class Pointer<T> {
private T t;
public Pointer(T t) {
set(t);
}
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
...
Map<String, Pointer> map = new HashMap<>();
Pointer<Integer> ptr = new Pointer<>(5);
map.put("A", ptr);
map.put("B", ptr);
System.out.println(map.get("A").get());
System.out.println(map.get("B").get());
ptr.set(25);
System.out.println(map.get("A").get());
System.out.println(map.get("B").get());
If you want something else you may need to elaborate or consider another data structure.

Related

Creating mapping between strings to arbitrary functions in java

For example, I want to support the following functionality:
FunctionActivator ac = new FunctionActivator();
ac.addFunc("times2", (Double x)->x*2));
ac.addFunc("reverse", (String s)-> new StringBuffer(s).reverse().toString());
Integer res = ac.useFunc("times2", 2); // should be 4
The approach I'm taking is something like that:
Interface F<R,P> {
R apply(P input);
}
Class FunctionActivator {
HashSet<String, /*don't know what to put here*/> keyToFunc;
...rest of implementation
}
If I want to keep FunctionActivator class non-generic, what type should I put in the hashset value?
For me, it works like this :
public class FunctionActivator {
//Use a HashMap for Key value mapping, Not a HashSet
private HashMap<String, Function> keyToFunc = new HashMap<>();
public void addFunc(String name, Function f) {
keyToFunc.put(name, f);
}
public Object useFunc(String name, Object parameter) {
return keyToFunc.get(name).apply(parameter);
}
}
And use it like that :
FunctionActivator ac = new FunctionActivator();
Function<Double, Double> doubleDoubleFunction = (Double x) ->x*2;
ac.addFunc("times2",doubleDoubleFunction);
//ac.addFunc("square", (Integer i) -> i*i); //This DOES NOT work, you need to cast to (Function<Integer, Integer>)
ac.addFunc("reverse", (Function<String, String>)(String s)->new StringBuffer(s).reverse().toString());
System.out.println(ac.useFunc("times2",new Double(5.0))); // Prints 10.0
System.out.println(ac.useFunc("reverse","Hello World")); // Prints dlroW olleH
Also, you don't need your F interface, since it exist the Function Interface with the same and more methods

Add number in map java

I am creating a function that loops through a string, separates it by comma and then takes the key from the second item in the array and the value from the 1st after splitting the string.
I then want to place these values in a map. This works perfectly, however if i have two strings with the same key it doesn't add the value up it just replaces it.
For example if my string was
123,totti 100,roma, 100,totti
I would want
totti 223
roma 100
Here is my code
private void processCallLogs(String[] splitCalls) {
for (String individualCall : splitCalls) {
int duration = 0;
String[] singleCall = individualCall.split(",");
duration += DurationParser.returnDuration(singleCall[0]);
this.cost += CalculateCost.calculateCostPerCall(singleDuration);
if (totalCallDurations.containsKey(singleCall[1])) {
totalCallDurations.put(singleCall[1], singleDuration);
} else {
totalCallDurations.put(singleCall[1], duration);
}
}
}
You can replace the if with something like this:
if (totalCallDurations.containsKey(singleCall[1])) {
duration += totalCallDurations.get(singleCall[1]);
}
totalCallDurations.put(singleCall[1], duration);
Create a map and update the value if the key is present
public static void main(String[] args) {
myMap = new HashMap<>();
// 123,totti 100,roma, 100,totti
addToMap("totti", 123);
addToMap("roma", 100);
addToMap("totti", 100);
System.out.println(myMap);
}
private static void addToMap(String string, int i) {
int t = i;
if (myMap.get(string) != null) {
t += myMap.get(string);
}
myMap.put(string, t);
}
If you're using Java 8, you can do this easily with the Map.merge() method:
totalCallDurations.merge(singleCall[1], duration, Integer::sum);
If you want to make a map that will add the values together instead of replacing, I would recommend extending the Map type to make your own map. Since Map is very abstract. I would extend HashMap. (I suggest this both for code style and because it will make your code more extendable).
public class AdderMap extends HashMap<String, Integer> { // This extends the HashMap class
public Integer get(String key) { // This overrides the Map::get method
if(super.containsKey(key)) return super.get(key); // If the key-value pairing exists, return the value
else return 0; // If it doesn't exist, return 0
}
public Integer put(String key, Integer value) { // This overrides the Map::put method
Integer old_value = this.get(key); // Get the former value of the key-value pairing (which is 0 if it doesn't exist)
super.put(key, old_value + value); // Add the new value to the former value and replace the key-value pairing (this behaves normally when the former value didn't exist)
return old_value; // As per the documentation, Map::put will return the old value of the key-value pairing
}
}
Now, when you initialize your map, make it an AdderMap. Then, you can just use put(String, Integer) and it will add it together.
The advantage of this solution is that it helps with keeping your code clean and it allows you to use this type of map again in the future without needing separate code in your main code. The disadvantage is that it requires another class, and having too many classes can become cluttered.

How to combine two Maps into one with maintaining the duplicate values

I have two hashmaps "map0 and map1", both of them holds keys of type Point and values of type Double as shown below.
I am sure that the key will never be duplicate but the values could be and I want to add both of "map0 and map1" into one Hashmap so that
I do not lose the duplicate values "I want to keep the duplicate values". After i checked some questions in this website, I used .putAll() method as shown below in the code, but the problem is, when I combined both maps "map0, map1" into one combined map, I lost the duplicate values as shown below in the results as map0 has value of 20 and map1 has value of 20 as well!
Please let me know how to combine both maps into one without losing the duplicate values
Code:
public static void main(String[] args) {
HashMap<Point, Double> map0 = new HashMap<Point, Double>();
HashMap<Point, Double> map1 = new HashMap<Point, Double>();
map0.put(new Point(32,59), (double) 56);
map0.put(new Point(398,3), (double) 20);
map0.put(new Point(3,3), (double) 209);
map1.put(new Point(32,596), (double) 561);
map1.put(new Point(396,311), (double) 20);
map1.put(new Point(35,34), (double) 2099);
System.out.println("map0.size:"+map0.size());
System.out.println("map1.size:"+map1.size());
HashMap<Point, Double> combine = new HashMap<Point, Double>();
combine.putAll(map0);
combine.putAll(map1);
ValueComparator vc = new ValueComparator(combine);
TreeMap<Point, Double> tree= new TreeMap<Point, Double>(vc);
tree.putAll(combine);
System.out.println("tree.size:"+tree.size());
System.out.println("tree_sorted:"+tree);
}
static class ValueComparator implements Comparator<Point> {
private HashMap<Point, Double> map = null;
public ValueComparator(HashMap<Point, Double> map) {
// TODO Auto-generated constructor stub
this.map = map;
}
public int compare(Point arg0, Point arg1) {
// TODO Auto-generated method stub
return Double.compare(this.map.get(arg0), this.map.get(arg1));
/*
if (this.map.get(arg0) >= this.map.get(arg1)) {
return 1;
} else {
return -1;
}
*/
}
}
output:
map0.size:3
map1.size:3
tree.size:5
tree_sorted:{{398.0, 3.0}=20.0, {32.0, 59.0}=56.0, {3.0, 3.0}=209.0, {32.0, 596.0}=561.0, {35.0, 34.0}=2099.0}
You cannot have two objects in TreeMap which are 'equal' from the standpoint of given comparator. It is explicitly stated in javadoc:
This is so because the Map interface is defined in terms of the equals operation, but a sorted map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal.
Your further use of sorted collection is not stated in the question so I presume that sorted List will be enough for you. I also recommend to extend the Pointclass to associate value with it. It will better suits to this use.
Also your comparator was not implemented the right way. It should not have the outside map reference.
So consider following code:
public class Example {
public static class ValuedPoint extends Point {
private double value;
public ValuedPoint(double x, double y, double value) {
super(x, y);
this.value = value;
}
public double getValue() {
return value;
}
public String toString() {
return "{" + x + ", " + y + ", " + value + "}";
}
}
private static class ValueComparator implements Comparator<ValuedPoint> {
public int compare(ValuedPoint arg0, ValuedPoint arg1) {
return Double.compare(arg0.getValue(), arg1.getValue());
}
}
public static void main(String[] args) {
Set<ValuedPoint> set0 = new HashSet<ValuedPoint>();
Set<ValuedPoint> set1 = new HashSet<ValuedPoint>();
set0.add(new ValuedPoint(32, 59, 56));
set0.add(new ValuedPoint(398, 3, 20));
set0.add(new ValuedPoint(3, 3, 209));
set1.add(new ValuedPoint(32, 596, 561));
set1.add(new ValuedPoint(396, 311, 20));
set1.add(new ValuedPoint(35, 34, 2099));
System.out.println("set0.size:" + set0.size());
System.out.println("set1.size:" + set1.size());
Set<ValuedPoint> combined = new HashSet<ValuedPoint>();
combined.addAll(set0);
combined.addAll(set1);
System.out.println("combined size:" + combined.size());
ValueComparator comparator = new ValueComparator();
List<ValuedPoint> sortedList = new ArrayList<ValuedPoint>(combined);
Collections.sort(sortedList, comparator);
System.out.println("list size:" + sortedList.size());
System.out.println("sortedList:" + sortedList);
}
}
The output is:
set0.size:3
set1.size:3
combined size:6
list size:6
sortedList:[{398.0, 3.0, 20.0}, {396.0, 311.0, 20.0}, {32.0, 59.0,56.0}, {3.0, 3.0, 209.0}, {32.0, 596.0, 561.0}, {35.0, 34.0, 2099.0}]
You define the comparison criteria for your keys (Point) is the related value (Double), thus the two points with value 20 are identical to your Comparator.
If you want to keep the duplicates, change the Comparator (i.e. your definition of equality) or use a different data structure, as a map does not allow for duplicate keys.

The implementation of Map implies that each key may contains only 1 value... But i need that 1 key could store <Any Big Number> values

For example... Adjacency list realiszation
public class Vertex {
String name;
boolean visited;
public Vertex(String name) {
this.name=name;
visited=false;
}
public int hashCode() {
return name.hashCode();
}
public boolean equals(Object ob) {
return hashCode()==ob.hashCode();
}
public String toString() {
return name;
}
}
The main class
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) {
PrintWriter pw=new PrintWriter(System.out);
Map<Vertex,Vertex> m=new HashMap();
m.put(new Vertex("a"), new Vertex("b"));// a ---> b
m.put(new Vertex("a"), new Vertex("c"));// a ---> c
m.put(new Vertex("a"), new Vertex("d"));// a ---> d
pw.println("All vertex from: ");
for (Vertex vert_from:m.keySet()) {
pw.print(vert_from+" ");
}
pw.println();
pw.println("All vertices to: ");
for (Vertex vert_to:m.values()) {
pw.print(vert_to+" ");
}
pw.close();
}
}
It outputs:
All vertex from:
a
All vertices to:
d
But i need that "All vertices to: b c d"
How can I fix that?
A Map indeed stores a single value per key. You could, however, store a collection in value, say a Set:
Map<Vertex, Set<Vertex>> m = new HashMap<>();
Set<Vertex> set = new HashSet<>();
set.add(new Vertex("b"));
set.add(new Vertex("c"));
set.add(new Vertex("d"));
m.add (new Vertex("a"), set);
Alternatively, you can use one of the common implementations of this concept, such as Apache Commons Collections' MultiValueMap or Guava's HashMultiMap.
What you are asking for is called a "Multi Map".
If you are using Java 8 then this is quite neat, first you need a Map<Vertex, Collection<Vertex>>. I don't know what properties you need from the Collection, that you will have to investigate yourself.
As you have overridden equals and hashCode (incorrectly, but a valiant attempt), I will assume that you want to have the items unique by name. I will also assume that order matters, so LinkedHashSet seems a good choice.
final Map<Vertex, Collection<Vertex>> graph = new HashMap<>();
Now, to add an item to the Map we need to first ensure that the Collection for that key is not null. This is exactly what the new Map.computeIfAbsent comes in.
final Vertex a = new Vertex("a");
graph.computeIfAbsent(a, v -> new LinkedHashSet<>()).add(new Vertex("b"));
graph.computeIfAbsent(a, v -> new LinkedHashSet<>()).add(new Vertex("c"));
graph.computeIfAbsent(a, v -> new LinkedHashSet<>()).add(new Vertex("d"));
So what this does is, when inserting a into the Map, if the Collection for that key is null, computes a new value for it.
Now to get all values for a key:
Collection<Vertex> values = graph.get(a);
You could wrap the Map<Vertex, Collection<Vertex>> in some sort of Graph class to hide the implementation details and to have neater code:
class Graph {
final Map<Vertex, Collection<Vertex>> graph = new HashMap<>();
public void put(final Vertex key, final Vertex value) {
graph.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(value);
}
public Collection<Vertex> get(final Vertex key) {
return Optional.ofNullable(graph.get(key)).orElse(Collections.EMPTY_SET);
}
}
This also deals with returning an empty collection instead of null if a key is not present in the Map. Depending on your use case you might also want to wrap the returned Collection with Collections.unmodifiableCollection to prevent unwanted modifications:
public Collection<Vertex> get(final Vertex key) {
return Optional.ofNullable(graph.get(key))
.map(Collections::unmodifiableCollection)
.orElse(Collections.EMPTY_SET);
}
You could also use a Guava Multimap if you aren't averse to external libraries.
Using a Multimap for your problem, it could be written like that:
public static void main(String[] args) {
PrintWriter pw=new PrintWriter(System.out);
ListMultimap<Vertex,Vertex> m= ArrayListMultimap.create();
Vertex a = new Vertex("a"); // it's better to create each object once
Vertex b = new Vertex("b");
Vertex c = new Vertex("c");
Vertex d = new Vertex("d");
m.put(a,b);// a ---> b
m.put(a,c);// a ---> c
m.put(a,d);// a ---> d
pw.println("All vertex from: ");
for (Vertex vert_from:m.keySet()) { //exactly the same as in your code
pw.print(vert_from+" ");
}
pw.println();
pw.println("All vertices to: ");
for (Vertex vert_to:m.values()) { //exactly the same as in your code
pw.print(vert_to+" ");
}
pw.close();
}
To use Guava, just download the latest jar from here and add it to your libraries.
Explanation:
By definition, each java Map has a single key and a single value.
However, you can use a Collection (like a List), or an Array for value. This way, your Map will be defined like that:
Map<Vertex, List<Vertex>> m = new HashMap<>();
Each time you want to add an element value to the list of vertex key, you can do it that way:
List<Vertex> list = m.get(key);
if (list == null) {
list = new ArrayList<>();
}
list.add(value);
An easier way, is to use Guava's Multimaps. It is the same as a Map, but the value is a Collection. So, an ArrayListMultimap is quite what I described above. The way to use it, though is much simpler:
ListMultimap<Vertex, Vertex> m = ArrayListMultimap.create();
m.put(key, value1);
m.put(key, value2); //adds value2 to the key, which also contains value1
....

Storing the name of an int variable in a String

Is it possible to store the name of an int variable in a string and use that string as a parameter to update the int?
Yes, this is called reflection.
You are interested in the Field class.
Example:
static class A {
public int x = 0;
}
public static void main(String[] args) throws Exception {
A a = new A();
Field f = A.class.getField("x");
f.set(a, 5);
System.out.println(a.x);
}
Note that though it is possible - it is not advised to use reflection except for rare cases, it has some major draw backs (maintainability, safety, performance...) - which makes the alternatives usually better choices.
Using reflection in this case would be overkill. You can obtain the intended behavior by simply using a Map:
Map<String, Integer> variables = new HashMap<String, Integer>();
Then the keys to the map will be the variable names, and the values the actual values:
variables.put("var1", 10);
variables.put("var2", 20);
Later on, you'll retrieve the values like this:
Integer n1 = variables.get("var1"); // n1 == 10
Integer n2 = variables.get("var2"); // n2 == 20
And if you need to update the values:
variables.put("var1", variables.get("var1") + 32);
Integer n3 = variables.get("var1"); // n3 == 42
The context of your question is not clear - a Map<String, Integer> might do what you need:
Map<String, Integer> map = new HashMap<String, Integer> ();
map.put("int1", 1);
map.put("int2", 2);
//now retrieve the ints based on their name
int int1 = map.get("int1");

Categories

Resources