I'm trying to implement an action/reaction system in Java.
For that, I need to have all my methods stock in a container so I can easily call the response I need with the return of the action I want.
Being a C ++ developer and new to Java my first intuition was to create an array of function pointers (or at least reproduce it) so I tried to used anonymous subclasses. But didn't get the result I was looking for.
So I tried with lambdas, here is a sample of what I'm trying to do.
public class Test {
public Map<Integer, Vector<String>> actions = new HashMap<>();
public Map<Integer, Integer> responses = new HashMap<>();
public Test() {
Vector<String> v= new Vector<String>();
actions.put(0, action0());
actions.put(1, action1());
responses.put(0, response0(Vector<String>)); // How can I leave aside this argument which I don't know at this point ?
responses.put(1, response1(Vector<String>));
}
public Vector<String> action0() {...}
public Vector<String> action1() {...}
// This methods takes actions return as argument
public Integer response0 (Vector<String>) {...}
public Integer response1 (Vector<String>) {...}
public void run() {
// When I run, I want to be able to launch any of my responses with any of my actions return
responses.get(0)
}
}
Am I at least trying a good way to solve this problem ?
Thanks a lot
Your code has a few flaws, so I'll assume that getHashtag() and action1() were supposed to be the same, and similar for the other three.
Also, I'll assume that the second responses.put() should have been key 1, not 0. Also, the parameter to your responseX() methods need a name.
Anyway, you need a functional interface, so you can give the responseX() methods as Method References.
In your case, the responseX() methods take a Vector<String> as parameter, and returns an Integer, so the functional interface would be Function<Vector<String>, Integer>.
You can then build a map of those methods, to be executed later.
public Test() {
// Here we can build map of response methods first, if we like, even though Vectors don't exist yet
Map<Integer, Function<Vector<String>, Integer>> responseMethods = new HashMap<>();
responseMethods.put(0, this::response0);
responseMethods.put(1, this::response1);
// Now we build the action map of Vectors
Map<Integer, Vector<String>> actions = new HashMap<>();
actions.put(0, action0());
actions.put(1, action1());
// At this time, we can now execute the referenced methods to get the actual responses
Map<Integer, Integer> responses = new HashMap<>();
for (Integer key : actions.keySet()) {
Vector<String> v = actions.get(key);
Function<Vector<String>, Integer> responseMethod = responseMethods.get(key);
Integer response = responseMethod.apply(v);
responses.put(key, response);
}
}
public Vector<String> action0() {...}
public Vector<String> action1() {...}
public Integer response0(Vector<String> v) {...}
public Integer response1(Vector<String> v) {...}
You can even defer the execution of the action methods if you want:
public Test() {
// Here we can build map of response methods first, if we like, even though Vectors don't exist yet
Map<Integer, Function<Vector<String>, Integer>> responseMethods = new HashMap<>();
responseMethods.put(0, this::response0);
responseMethods.put(1, this::response1);
// Now we build the action map of Vectors
Map<Integer, Supplier<Vector<String>>> actionMethods = new HashMap<>();
actionMethods.put(0, this::action0);
actionMethods.put(1, this::action1);
// At this time, we can now execute the referenced methods to get the actual responses
Map<Integer, Integer> responses = new HashMap<>();
for (Integer key : actionMethods.keySet()) {
Supplier<Vector<String>> actionMethod = actionMethods.get(key);
Function<Vector<String>, Integer> responseMethod = responseMethods.get(key);
Vector<String> v = actionMethod.get();
Integer response = responseMethod.apply(v);
responses.put(key, response);
}
}
Related
I have a simple in-memory cache that looks like (oversimplified) below:
class Cache {
private Map<String, Integer> m = new HashMap<>();
public String get(String key) {
if(!m.containsKey(key))
return null;
return m.get(key);
}
public void set(String key, String value) {
m.put(key, value);
}
public Set<String> getKeys() {
return m.keySet();
}
}
Now I want to write Unit Tests for this class. I was thinking I can do something along the lines of:
[TestMethod]
public void Get_Success() {
Cache c = new Cache();
c.set("Apple", 1);
c.set("Grape", 2);
c.set("Banana", 3);
Assert.Equals(1, c.get("Apple"));
Assert.Equals(2, c.get("Grape"));
Assert.Equals(3, c.get("Banana"));
}
But the problem is, it tests both Set and Get methods. So if the test breaks, I will not know whether the bug is in Set or Get.
What is the recommended way to test the Get and Set methods individually?
One way I thought was, to modify the Cache class to accept a Map<String, Integer> instance like:
class Cache {
private Map<String, Integer> m = new HashMap<>();
public Cache(Map<String, Integer> m) {
this.m = m;
}
...
...
}
Then from the test, I can create an instance of Map and pass it to Cache constructor. Then all the Set operations will happen in the map instance that I passed and I can validate whether Set worked as expected.
But I am not very sure whether it's the right approach.
Similarly, I want the ability to test Get as well without depending on Set.
I'm a developer transitioning from C++ to Java.So I still dont have all the expertise to getting stuff done the Java Way.
I have the following class
class Site
{
String siteName;
Integer siteId;
Integer views;
Integer searches;
}
I maintain 2 maps to search over the objects of this class(using sitename or siteid)
HashMap<String, Site> siteNameToSiteMap;
HashMap<Integer, Site> siteIdToSiteMap;
However going forward, I have to add a one more field called parentBrand to the class Site. This will force me to create another Map to be able to search over it.
HashMap<String, Site> parentBrandToSiteMap;
Such "indexing" variables might increase going ahead and thus also increase the number of maps I maintain.
I remember using Boost Multi-indexed container while solving a similar issue while developing in C++. Is there an equivalent well supported, well documented library in Java that I can use. If no, is there a way I can refactor my code to solve my problem.
I'm surprised that there isn't a version of something like the boost multi-index containers available. (Maybe there is somewhere...) But its not too hard to hook up your own version in Java.
A rough, but working, version might look like this:
The basic site object
I've used a slightly different Site object, just to keep things simple (and because I didn't have access to this post on the bus...)
public class Site {
Integer id;
String name;
String rating;
// .. Constructor and toString removed for brevity
}
A wrapped version
I'm going to introduce some workhorse classes later, but they're a little ugly. This is just to show what the final interface would look like once you've wrapped it up a little:
class SiteRepository {
private final MultiMap<Site> sites = new MultiMap<>();
public final AbstractMap<String, Site> byName = sites.addIndex((site)->site.name);
public final AbstractMap<Integer,Site> byId = sites.addIndex((site)->site.id);
public final AbstractMap<String,List<Site>> byRating = sites.addMultiIndex((Site site)->site.rating);
public void add(Site s) { sites.add(s); }
}
SiteRepository repo = new SiteRepository();
repo.add(...);
Site site = repo.byId.get(1234);
repo.byId.forEach((Integer id, Site s) -> System.err.printf(" %s => %s\n", id, s));
The MultiMap core
Probably should be called MultiIndex since MultiMap means something else...
public static class MultiMap<V> {
public static class MultiMapIndex<K,V> extends AbstractMap<K,V> {
#Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
HashMap<K,V> map = new HashMap<>();
}
public <K> MultiMapIndex<K,V> addIndex(Function<V, K> f) {
MultiMapIndex<K,V> result = new MultiMapIndex<>();
Consumer<V> e = (V v) -> result.map.put(f.apply(v), v);
mappers.add(e);
values.forEach(e);
return result;
}
public <K> MultiMapIndex<K,List<V>> addMultiIndex(Function<V, K> f) {
MultiMapIndex<K,List<V>> result = new MultiMapIndex<>();
Consumer<V> e = (V v) -> {
K key = f.apply(v);
List<V> list = result.map.get(key);
if (list == null) {
list = new ArrayList<>();
result.map.put(key, list);
}
list.add(v);
};
mappers.add(e);
values.forEach(e);
return result;
}
public void add(V v) {
values.add(v);
mappers.forEach( e -> e.accept(v));
}
private List<Consumer<V>> mappers = new ArrayList<>();
private List<V> values = new ArrayList<>();
}
More low level examples
public static void main(String[] args) {
// Create a multi-map
MultiMap<Site> multiMap = new MultiMap<>();
// Add an index by Site.id
MultiMapIndex<Integer, Site> byId = multiMap.addIndex((site)->site.id);
// Add some entries to the map
multiMap.add(new Site(1234,"A Site","A"));
multiMap.add(new Site(4321,"Another Site","B"));
multiMap.add(new Site(7777,"My Site","A"));
// We can add a new index after the entries have been
// added - this time by name.
MultiMapIndex<String, Site> byName = multiMap.addIndex((site)->site.name);
// Get a value by Id.
System.err.printf("Get by id=7777 = %s\n", byId.get(7777));
// Get a value by Name
System.err.printf("Get by name='A Site' = %s\n", byName.get("A Site"));
// We can do usual mappy things with the indexes,
// such as list the keys, or iterate over all entries
System.err.printf("byId.keys() = %s\n", byId.keySet());
byId.forEach((Integer id, Site s) -> System.err.printf(" %s => %s\n", id, s));
// In some cases the map is not unique, so I provide a
// way to get all entries with the same value as a list.
// in this case by their rating value.
MultiMapIndex<String, List<Site>> byRating = multiMap.addMultiIndex((Site site)->site.rating);
System.err.printf("byRating('A') = %s\n", byRating.get("A"));
System.err.printf("byRating('B') = %s\n", byRating.get("B"));
// Adding stuff after creating the indices is fine.
multiMap.add(new Site(3333,"Last Site","B"));
System.err.printf("byRating('A') = %s\n", byRating.get("A"));
System.err.printf("byRating('B') = %s\n", byRating.get("B"));
}
}
I think you can search your objects over List :
List<Site> sites;
for (Site s : sites) {
if (s.getSiteName().equal(siteName)) {
// do something
}
if (s.getSiteId().equal(siteId)) {
// do something
}
}
You should create a Bean (Container) as Java is not requiring code optimisation as it will be optimised by the JIT compiler anyway.
public class SiteMap {
String siteName;
Integer siteId;
String parentBrand;
.... Getters and setters ...
}
List<SiteMap> myList = new ArrayList<>();
If you need to compare or sort then you can implement Comparable interface on the SiteMap class allowing you to sort the details if needed.
you can, if using Java 8 then also use Streams to filter or fetch the one you want. as there is a fetchFirst
SiteMap mysite = myList.stream()
.filter(e -> e.siteName.equals("Amazon.com"))
.findFirst()
.get();
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.
Suppose I want to write a function, which will create a HashMap from some specified type T to String, for example, a HashMap from Integer to String as follows:
HashMap<Integer, String> myHashMay = new HashMap<Integer, String>();
I want to have flexibilty to specify the type T. So I write a function as:
void foo(Class<?> myClass) {
HashMap<myClass, String> myHashMay = new HashMap<myClass, String>();
.......
}
so that if I invoke foo(Integer.class), a HashMap from Integer to String will be created inside this function. Apparently the above foo function does not even compile. Could anybody give me some hints on how to write such a function correctly with the given function signature.
Thanks,
<T> void foo(Class<T> myClass) {
HashMap<T, String> myHashMay = new HashMap<T, String>();
...
}
EDIT:
However, method with such a signature doesn't seem to be very useful, because T is used only for type checking at compile time. I can imagine only the single scenario when it can be used:
<T> void foo(Class<T> myClass) {
HashMap<T, String> myHashMay = new HashMap<T, String>();
...
try {
T key = myClass.newInstance();
myHashMay.put(key, "Value");
} catch (Exception ex) { ... }
...
}
This function creates a map like you are trying to do:
public <KeyType> Map<KeyType,String> createMapWithKeyType(Class<KeyType> keyType)
{
return new HashMap<KeyType, String>();
}
Note: pay attention to the comment by matt b, he makes a good point.
I have the ViewValue class defined as follows:
class ViewValue {
private Long id;
private Integer value;
private String description;
private View view;
private Double defaultFeeRate;
// getters and setters for all properties
}
Somewhere in my code i need to convert a list of ViewValue instances to a list containing values of id fields from corresponding ViewValue.
I do it using foreach loop:
List<Long> toIdsList(List<ViewValue> viewValues) {
List<Long> ids = new ArrayList<Long>();
for (ViewValue viewValue : viewValues) {
ids.add(viewValue.getId());
}
return ids;
}
Is there a better approach to this problem?
We can do it in a single line of code using java 8
List<Long> ids = viewValues.stream().map(ViewValue::getId).collect(Collectors.toList());
For more info : Java 8 - Streams
You could do it in a one-liner using Commons BeanUtils and Collections:
(why write your own code when others have done it for you?)
import org.apache.commons.beanutils.BeanToPropertyValueTransformer;
import org.apache.commons.collections.CollectionUtils;
...
List<Long> ids = (List<Long>) CollectionUtils.collect(viewValues,
new BeanToPropertyValueTransformer("id"));
Use google collections. Example:
Function<ViewValue, Long> transform = new Function<ViewValue, Long>() {
#Override
public Long apply(ViewValue from) {
return from.getId();
}
};
List<ViewValue> list = Lists.newArrayList();
List<Long> idsList = Lists.transform(list, transform);
UPDATE:
On Java 8 you don't need Guava. You can:
import com.example.ViewValue;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
Function<ViewValue, Long> transform = ViewValue::getId;
List<ViewValue> source = new ArrayList<>();
List<Long> result = source.stream().map(transform).collect(Collectors.toList());
Or just:
List<ViewValue> source= new ArrayList<>();
List<Long> result = source.stream().map(ViewValue::getId).collect(Collectors.toList());
NEXT UPDATE (The last one after Javaslang to Vavr name change):
Currently it's worth to mention about the solution with Javaslang library(http://www.javaslang.io/) Vavr library (http://www.vavr.io/). Let's assume that we have our list with genuine objects:
List<ViewValue> source = newArrayList(new ViewValue(1), new ViewValue(2), new ViewValue(2));
We could make transformation with List class from Javaslang library (on the long run the collect is not convenient):
List<Long> result = io.vavr.collection.List.ofAll(source).map(ViewValue::getId).toJavaList();
But you will see the power with only the Javaslang lists:
io.vavr.collection.List<ViewValue> source = javaslang.collection.List.of(new ViewValue(1), new ViewValue(2), new ViewValue(3));
io.vavr.collection.List<Long> res = source.map(ViewValue::getId);
I encourage to take a look available collections and new types on that library (I like especially the Try type). You will find the documentation under the following address: http://www.javaslang.io/javaslang-docs/ http://www.vavr.io/vavr-docs/.
PS. Due to the Oracle and the "Java" word within the name they had to change the library name from javaslang to something else. They had decided to Vavr.
EDIT: This answer is based on the idea that you'll need to do similar things for different entities and different properties elsewhere in your code. If you only need to convert the list of ViewValues to a list of Longs by ID, then stick with your original code. If you want a more reusable solution, however, read on...
I would declare an interface for the projection, e.g.
public interface Function<Arg,Result>
{
public Result apply(Arg arg);
}
Then you can write a single generic conversion method:
public <Source, Result> List<Result> convertAll(List<Source> source,
Function<Source, Result> projection)
{
ArrayList<Result> results = new ArrayList<Result>();
for (Source element : source)
{
results.add(projection.apply(element));
}
return results;
}
Then you can define simple projections like this:
private static final Function<ViewValue, Long> ID_PROJECTION =
new Function<ViewValue, Long>()
{
public Long apply(ViewValue x)
{
return x.getId();
}
};
And apply it just like this:
List<Long> ids = convertAll(values, ID_PROJECTION);
(Obviously using K&R bracing and longer lines makes the projection declaration a bit shorter :)
Frankly all of this would be a lot nicer with lambda expressions, but never mind...
I've implemented a small functional library for this usecase. One of the methods has this signature:
<T> List<T> mapToProperty(List<?> objectList, String property, Class<T> returnType)
Which takes the string and uses reflection to create a call to the property then it returns a List backed by the objectList where get and iterator implemented using this property call.
The mapToProperty functions is implemented in terms of a general map function that takes a Function as a mapper though, just as another post described. Very usefull.
I suggest you read up on basic functionl programming and in particular take a look at Functors (objects implementing a map function)
Edit: Reflection really doesn't have to be expensive. The JVM has improved a lot in this area. Just make sure to compile the invocation once and reuse it.
Edit2: Sample code
public class MapExample {
public static interface Function<A,R>
{
public R apply(A b);
}
public static <A,R> Function<A,R> compilePropertyMapper(Class<A> objectType, String property, Class<R> propertyType)
{
try {
final Method m = objectType.getMethod("get" + property.substring(0,1).toUpperCase() + property.substring(1));
if(!propertyType.isAssignableFrom(m.getReturnType()))
throw new IllegalArgumentException(
"Property "+property+" on class "+objectType.getSimpleName()+" is not a "+propertyType.getSimpleName()
);
return new Function<A,R>()
{
#SuppressWarnings("unchecked")
public R apply(A b)
{
try {
return (R)m.invoke(b);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
};
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static <T1,T2> List<T2> map(final List<T1> list, final Function<T1,T2> mapper)
{
return new AbstractList<T2>()
{
#Override
public T2 get(int index) {
return mapper.apply(list.get(index));
}
#Override
public int size() {
return list.size();
}
};
}
#SuppressWarnings("unchecked")
public static <T1,T2> List<T2> mapToProperty(List<T1> list, String property, Class<T2> propertyType)
{
if(list == null)
return null;
else if(list.isEmpty())
return Collections.emptyList();
return map(list,compilePropertyMapper((Class<T1>)list.get(0).getClass(), property, propertyType));
}
}
You could use a wrapper:
public class IdList impements List<Long>
{
private List<ViewValue> underlying;
pubic IdList(List<ViewValue> underying)
{
this.underlying = underying;
}
public Long get(int index)
{
return underlying.get(index).getId()
}
// other List methods
}
Though that's even more tedious work, it could improve performance.
You could also implement your and my solution generic-ly using reflection, but that would be very bad for performance.
There's no short and easy generic solution in Java, I'm afraid. In Groovy, you would simply use collect(), but I believe that involves reflection as well.
That depends on what you then do with the List<Long>, and the List<ViewValue>
For example you might get sufficient functionality from creating your own List implementation that wraps a List<ViewValue>, implementing iterator() with an iterator implementation that iterates over the ViewValues, returning the id.
You can populate a map from the properties of a list of objects (say id as key and some property as value) as below
Map<String, Integer> mapCount = list.stream().collect(Collectors.toMap(Object::get_id, Object::proprty));