Creating mapping between strings to arbitrary functions in java - 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

Related

Dynamically choose list type to iterate over a common for loop

I have a for loop which applies a Function for each element of a list. The list could be of type String or Long. Depending on a condition, I need to either iterate the String list or the Long list. Is there a way where I can do it without using if - else?
public static <T, S> T weirdFunction(Function<S, T> myFunction, Boolean convertToLong){
List<String> stringList = fetchFromSomewhere();
if(convertToLong){
// fetch list of longs from elsewhere
}
for (<String or Long> id : List<String> or List<Long>) { // choose list type based on some method param
myFunction.apply(id); // myFunction is of type Function
}
}
Having a function that returns another function, to be applied "recursively"1, makes this tricky.
Here is how it can be done:
#FunctionalInterface
interface ChainFunction<T> extends Function<T, ChainFunction<T>> {
// Nothing to add
}
static <T> ChainFunction<T> applyFunctionChain(List<T> list, ChainFunction<T> myFunction) {
for (T id : list) {
myFunction = myFunction.apply(id);
}
return myFunction;
}
The method can be called with String and with Long, and any other type for that matter.
1) "Recursive" is not the right word, but I don't know what it's called.
This presents one approach to call a different function per type without using an if statement. It requires one to build a map of different classes the list contains, each with a particular function. The containing class that houses the methods is SpecialList There is a caveat in this. Since the return value is cast to its actual type it can't be assigned in the loop unless it is returned as an Object. If the return type is not needed, then the return type could be set to void and the FunctionalInterface in the map could be a Consumer
Map<Class<?>, Function<Object, Object>> map = new HashMap<>();
map.put(String.class, SpecialList::stringFnc);
map.put(Long.class, SpecialList::longFnc);
List<Object> list = List.of("String1", 123L, "String2", 1234L,
29292L, "String4");
for (Object ob : list) {
map.get(ob.getClass()).apply(ob);
}
public static String stringFnc(Object str) {
String v = (String)str;
// now do something with v.
System.out.println("String : " + v);
return v;
}
public static Long longFnc(Object lng) {
long v = (Long)lng;
// now do something with v
System.out.println("Long : " + v);
return v;
}
prints
String : String1
Long : 123
String : String2
Long : 1234
Long : 29292
String : String4

Java multiple keys direct to the same values

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.

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.

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
....

In java, is there a way to search for a specific field of a class stored in a linked list?

I wrote a class that is to be stored in a linkedlist, with 3 fields in the class. One of these fields is a String, which I would like to search for in the linked list.
Example
LinkedList
Obj1
String name = "first";
int age = 2;
int size = 4;
Obj2
String name = "second";
int age = 3;
int size = 6;
Obj3
String name = "third";
int age = 5;
int size = 8;
If this is the linkedlist storing these three objects with the given fields, is there a way to search the linked list for the object with the name "second"?
You can search for an item in the list by iteration
// Iterate over each object within the list
for(YourClass obj : yourLinkedList) {
// Check if the object's name matches the criteria, in this case, the name
// of the object has to match "second"
if (obj.name.equals("second")) {
// If we are within this block, it means that we found the object that has
// its name set as "second".
return obj;
}
}
You could also make a method to make things more elegant
public YourClass findByName(String name) {
for(YourClass obj : yourLinkedList) {
if (obj.name.equals(name)) {
return obj;
}
}
return null;
}
And use it the following way
YourClass object = findByName("second");
The easiest way to do this would be to of course, iterate through each element in the collection, checking if it matched your filter condition, and selecting the matches found. However this gets tedious the more times you need to do it, and the more complex your filter condition is. I would recommend utilizing pre-existing libraries to get the task done efficiently. Here is an example using Google-Collections:
final List<SomeObj> listObjs = Arrays.asList(
new SomeObj("first", 2, 4), new SomeObj("second", 3, 6),
new SomeObj("third", 5, 8));
final Iterable<SomeObj> filtered = Iterables.filter(listObjs,
new Predicate<SomeObj>() {
#Override
public boolean apply(final SomeObj obj) {
return "second".equals(obj.getName());
}
});
for (final SomeObj obj : filtered) {
System.out.println(obj);
}
The code shown would select all objects in the list with a name property of "second". Obviously, the predicate doesn't have to be an anonymous inner class - if you needed to reuse it you would just break it out to a standalone class.
Here's another way to implement a Comparator (just in case it helps).
I find it's easier to understand if you implement the Comparator explicitly:
class PersonAgeComparator implements Comparator<Person> {
#Override
public int compare(Person p1, Person person2) {
return p1.getAge().compareTo(p2.getAge());
}
}
You might use the above like this:
Comparator ageComparator = new PersonAgeComparator();
List<Person> personList = // populate list somehow
Person fourYearOld = new Person();
fourYearOld.setAge(4);
for (Person p : personList) {
if (ageComparator.compare(fourYearOld, p) == 0) {
System.out.println(p.getName() + " is 4 years old");
}
}
This doesn't make much sense for this simple example.
It would be ideal if you had several complicated ways to compare people (by height, by adjusted income, by how many states they've lived in, etc...).
Take a look at the java.util.Comprator interface. You can write a method that iterates over a List and uses a comparator to find the one you are after.
Something like (not compiled):
for(final T value : list)
{
if(comparator.compare(value, desired) == 0)
{
// match
}
}
In your comparator you have it perform whatever comparison you want.
Here is a working example:
public class JavaApplication4
{
public static void main(String[] args)
{
final List<Data> list;
final List<Data> a;
final List<Data> b;
list = new ArrayList<Data>();
list.add(new Data("Foo", 1));
list.add(new Data("Bar", 10));
list.add(new Data("Car", 10));
a = find(list,
new Data("Bar", 0),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.name.compareTo(o2.name));
}
});
b = find(list,
new Data(null, 10),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.count - o2.count);
}
});
System.out.println(a.size());
System.out.println(b.size());
}
private static List<Data> find(final List<Data> list,
final Data desired,
final Comparator<Data> comprator)
{
final List<Data> results;
results = new ArrayList(list.size());
for(final Data data : list)
{
if(comprator.compare(desired, data) == 0)
{
results.add(data);
}
}
return (results);
}
private static class Data
{
private final String name;
private final int count;
Data(final String nm,
final int c)
{
name = nm;
count = c;
}
}
}
And here is a generic version of the find method. Using this method you would never have to write the find method again, using a method that embeds the logic for matching in the iteration code means that you would have to re-write the iteration logic for each new set of matching logic.
private static <T> List<T> find(final List<T> list,
final T desired,
final Comparator<T> comprator)
{
final List<T> results;
results = new ArrayList(list.size());
for(final T value : list)
{
if(comprator.compare(desired, value) == 0)
{
results.add(value);
}
}
return (results);
}
You can go through it and get it done or there's another way.
You need to override the equals method in your class (and the hashcode method as well).
After you override the equals to your desire, in this case to compare the names, create a new object with the same name and call the remove(Object o) method of the LinkedList and get the object.
You should note that with this approach you objects equality will be defined by name and that the entry will be removed from the LinkedList

Categories

Resources