I have 4 almost same function and would like to refactor them in one. I thought to use reflexy, but I don't understand how to init EnumMap with reflexy.
code of functions like this:
private void loadRealEstateValues() {
realEstateValues = new EnumMap<RealEstateType, String>(RealEstateType.class);
for (RealEstateType r : RealEstateType.values()) {
realEstateValues.put(r, ResUtils.getStringByName(context,
String.format("%s_%s", getParserPrefix(), r.toString().toLowerCase())));
}
}
private void loadPeriodValues() {
periodValues = new EnumMap<PeriodType, String>(PeriodType.class);
for (PeriodType p : PeriodType.values()) {
periodValues.put(p, ResUtils.getStringByName(context,
String.format("%s_%s", getParserPrefix(), p.toString().toLowerCase())));
}
}
ways to put values I found like this, Maybe is a better?
Class<?> c= TestEnum.class;
testEnumMap = new EnumMap<TestEnum, String>(TestEnum.class);
for(Object m : c.getEnumConstants()){
testEnumMap.put((TestEnum) m, "bla bla blah");
}
Use a generic method:
private <T extends Enum<T>> EnumMap<T, String> loadEnumValues(Class<T> enumClass) {
EnumMap<T, String> map = new EnumMap<T, String>(enumClass);
for (T t : enumClass.getEnumConstants()) {
map.put(t, ResUtils.getStringByName(context,
String.format("%s_%s", getParserPrefix(), t.toString().toLowerCase())));
}
return map;
}
Related
I have a function
public static void printTreeMap (TreeMap <Object, ArrayList> map, PrintStream ps)
and a TreeMap:
TreeMap <Integer, ArrayList<MyClass>> tm = new TreeMap<>();
When I'm trying to call printTreeMap like that: printTreeMap(tm, System.out);, I get an exception
The method printTreeMap(TreeMap<Object,ArrayList>, PrintStream) in the type task_v7 is not applicable for the arguments (TreeMap<Integer,ArrayList<MyClass>>, PrintStream) Java(67108979)
How can I fix it?
I would suggest this approach. Note that for general methods of this type there is no a priori knowledge of those types. Therefore, this relies solely on the types involved overriding toString().
public static <T,U> void printTreeMap(TreeMap<T, ArrayList<U>> tm, PrintStream st) {
tm.forEach((k,v)-> {
st.println(k);
for (U e : v) {
st.println(" " + e);
}
});
}
Note that T and U need not be different as both could be the same type.
Here is an example.
TreeMap<Integer, ArrayList<MyClass>> tm = new TreeMap<>();
tm.put(10, new ArrayList<>(
List.of(new MyClass("Alpha"), new MyClass("Beta"))));
tm.put(30, new ArrayList<>(
List.of(new MyClass("Gamma"), new MyClass("Delta"))));
printTreeMap(tm, System.out);
Prints
10
Alpha
Beta
30
Gamma
Delta
The class
class MyClass {
String a;
public MyClass(String a) {
this.a = a;
}
public String toString() {
return a;
}
}
A more versatile approach for any Map of Lists would be the following:
public static <L extends List<?>, M extends Map<?,L>> void printMapOfLists(M tm,
PrintStream st) {
tm.forEach((k, v) -> {
st.println(k);
for (Object e : v) {
st.println(" " + e);
}
});
}
Generics and abstracts are mind boggling difficult to deal with, so bear with me while I try my best to explain the problem in a simple manner.
I have the following classes:
public class Community<T extends Person> {
List<T> people;
public void add(T person) {
people.add(person);
}
public List<T> getPeople() {
return people;
}
}
public abstract class Person {}
public class Aussie extends Person {}
Here is the code to instantiate a community of Aussies:
Aussie aus1 = new Aussie();
Aussie aus2 = new Aussie();
Community<Aussie> aussieCommunity = new Community<>();
aussieCommunity.add(aus1);
aussieCommunity.add(aus2);
Now let's take a step further and say that I have multiple communities that I wish to systematically store inside a list as follow:
List<Community<?>> communities;
I hope you're still with me because here is my problem:
I need to write a code that will take the list of community and display each person's details - assuming each person's details will be accessed differently in their own class. Example: Aussie may say "Oi" as hi, American's may say "Hello" as hi.
for (Community<?> community : communities) {
// I don't know what the type of community this is so, I use wildcard:
List<? extends Person> people = community.getPeople();
for (Type person : people) { // How do I specify the type of person eg Aussie/American etc here?
// Do something
}
}
Any suggestion on how I can specify the type of person in the second for loop?
Ok. Here is an small example of how it can be done:
public abstract class Person {
public final String say(String sentance) {
StringTokenizer tokenizer = new StringTokenizer(sentance);
StringBuilder sb = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
String word = tokenizer.nextToken();
String slang = getSlang(word);
sb.append(slang != null ? slang : word);
sb.append(tokenizer.hasMoreTokens() ? " " : "");
}
return sb.toString();
}
private String getSlang(String word) {
return getSlangMap().get(word);
}
protected abstract Map<String, String> getSlangMap();
}
public class Aussi extends Person {
#Override
protected Map<String, String> getSlangMap() {
Map<String, String> slangMap = new HashMap<>();
slangMap.put("hi", "Oi");
slangMap.put("there", "theeer");
return slangMap;
}
}
public class Swede extends Person {
#Override
protected Map<String, String> getSlangMap() {
Map<String, String> slangMap = new HashMap<>();
slangMap.put("hi", "hejsan");
slangMap.put("there", "där");
return slangMap;
}
}
public class CommunityTest {
#Test
public void testSayHiThere() throws Exception {
Aussi au1 = new Aussi();
Aussi au2 = new Aussi();
Community<Aussi> aussiCommunity = new Community<>();
aussiCommunity.add(au1);
aussiCommunity.add(au2);
Swede sw1 = new Swede();
Swede sw2 = new Swede();
Community<Swede> swedeCommunity = new Community<>();
swedeCommunity.add(sw1);
swedeCommunity.add(sw2);
List<Community<? extends Person>> communities = new ArrayList<>();
communities.add(aussiCommunity);
communities.add(swedeCommunity);
for (Community<? extends Person> community : communities) {
for (Person person : community.getPeople()) {
System.out.println(person.say("hi there"));
}
}
}
}
This is my first question in Stackoverflow.I have come to find a issue with one of the problem suggested and give to me by my colleague to do some research on it.
My question is
i have a class
Class Function{
String func;
String funcname;
boolean log;
}
i have created some objects:
obj1 : ("a" ,"b",true)- //these values come from either DB or UI
obj2 : ("c" ,"x",true)
obj3 : ("a" ,"z",true)
i have a list:
List<function> flist;
now i want to have that list in the map and want to put in inside the map
Map<String, List<function>> funcMap
and then display this following output:
a:[obj1 obj3]
b:[obj2]
if i have the list but how to go about and find the above output as desired
Try this,
add all the objects in the flist.
initialize the map
Map<String, List<Function>> funcMap = new HashMap<String, List<Function>>();
going to add the object to the relevant key based on the func value the object will add to the value list.
for (Function functionValue : flist)
{
List<Function> functionList = funcMap.get(functionValue.getFunc());
if (functionList != null && !functionList.isEmpty())
{
functionList.add(functionValue);
}
else
{
functionList = new ArrayList<Function>();
functionList.add(functionValue);
funcMap.put(functionValue.getFunc(), functionList);
}
}
Atlast print the funcMap
for (Map.Entry< String, List<Function>> entry : funcMap.entrySet())
{
System.out.println("Key : " + entry.getKey() + "Values : "+entry.getValue());
}
Hmm.. I think it's a case of parsing your list in a nested loop kind of way. Here is the pseudo-code:
public void listToMap(List<Function> list)
{
Map<String, List<Function>> map := new Map
for every function in the list.
{
if(is the current function's func value does not exist in the map)
{
func := current functions func value
List matchingFunctions := new list of Functions.
for(every function in the list.)
{
// Every Function with the same key get's added to a list.
if(function has the same func value as func)
{
add to matchingFunctions.
}
}
// That list and key get put into the HashMap.
map.put(func, matchingFunctions).
}
}
}
A Note on your code design
Java convention states that you should wrap your member objects up in getters and setters, and that those members should be private.
what about:
public class FuncTest {
public static void main(String[] args) {
new FuncTest().start();
}
private void start() {
List<Function> flist = new ArrayList<Function>();
flist.add(new Function("a", "b", true));
flist.add(new Function("c", "x", true));
flist.add(new Function("a", "z", true));
Map<String, List<Function>> funcMap = new HashMap<String, List<Function>>();
for (Function func : flist) {
this.add(func.func, func, funcMap);
this.add(func.funcname, func, funcMap);
}
}
private void add(String field, Function func, Map<String, List<Function>> funcMap) {
List<Function> subList = funcMap.get(field);
if (subList == null) {
subList = new ArrayList<Function>();
funcMap.put(field, subList);
}
subList.add(func);
}
}
Note
As already mentioned by Chris you should think about your code design. Use getters and setters ..
public class Stackoverflow {
public static void main(String[] args) {
Function obj1 = new Function("a" ,"b",true);
Function obj2 = new Function("c" ,"x",true);
Function obj3 = new Function("a" ,"z",true);
List<Function> functionsList1 = new ArrayList<Function>();
functionsList1.add(obj1);
functionsList1.add(obj3);
List<Function> functionsList2 = new ArrayList<Function>();
functionsList2.add(obj2);
Map<String, List<Function>> funcMap = new LinkedHashMap<String, List<Function>>();
funcMap.put("a", functionsList1);
funcMap.put("b", functionsList2);
Set<Entry<String,List<Function>>> entrySet = funcMap.entrySet();
for (Entry<String, List<Function>> entry : entrySet) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}
class Function {
String func;
String funcname;
boolean log;
public Function(String func, String funcname, boolean log) {
super();
this.func = func;
this.funcname = funcname;
this.log = log;
}
#Override
public String toString() {
return "Function [func=" + func + ", funcname=" + funcname + ", log="
+ log + "]";
}
}
Write your own map.
Pass the list to map, let map will decide what portion of list to keep as value.
I have added put method here, like the same, have to Override other methods.
class MyHashMap<K,V> extends HashMap<K,V>{
#SuppressWarnings("unchecked")
public V put(K k, V v) {
String key = (String)k;
List<Function> list = (List<Function>) v;
List<Function> list2 = new ArrayList<Function>();
for (Function function : list) {
if(key.equalsIgnoreCase(function.func)){
list2.add(function);
}
}
return (V) list2;
};
#Override
public boolean equals(Object o) {
// Your own code
return true;
}
// other methods goes here..
}
I am trying to write some general code to do the following. Given two kinds of "operations", (a) validation (eg. input: object & context -> output: boolean), and (b) transformation (eg. input: object_A, context -> output: object_B) -objects of any type-.
I want to be able to build chains of "operations", in which an input object and its context can be submitted through (eg. to validate and transform the object). Returning immediately if the object is "invalid" and being able to get the transformed object if it finished "valid".
Idea is that "validations" and "transformations" can be "plugable" functions that other people write and assemble in a chain (eg. they build chains and submit objects through them).
I managed to do the following code, which compiles and seems to work. However, I'm not an expert on generics and would like to hear feedback about possible pitfalls, enhancements, or even maybe some other better/easier approach to the problem. Thanks in advance.
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
interface Operation<T, U, V> {
U execute(T a, V context);
}
abstract class Validation<T, V> implements Operation<T, Boolean, V> {
#Override
public Boolean execute(T a, V context) {
return executeValidation(a, context);
}
public abstract Boolean executeValidation(T a, V context);
}
abstract class Transformation<T, U, V> implements Operation<T, U, V> {
#Override
public U execute(T a, V context) {
return executeTransformation(a, context);
}
public abstract U executeTransformation(T a, V context);
}
class OperationsChain {
List<Operation<Object, Object, Object>> operations = new ArrayList<Operation<Object, Object, Object>>();
Object currentObj;
public <T, V> Boolean run(T a, V context) {
Boolean valid = false;
currentObj = a;
for (Operation<Object, Object, Object> operation : operations) {
if (operation instanceof Validation) {
valid = (Boolean) operation.execute(currentObj, context);
} else if (operation instanceof Transformation) {
currentObj = operation.execute(currentObj, context);
}
if (!valid) {
break;
}
}
return valid;
}
#SuppressWarnings("unchecked")
public <T, U, V> void addOperation(Operation<T, U, V> operation) {
operations.add((Operation<Object, Object, Object>) operation);
}
public Object getCurrentObject() {
return currentObj;
}
}
class ValidationOne extends Validation<MapObject, Map<String, Object>> {
public Boolean executeValidation(MapObject a, Map<String, Object> context) {
if (context.containsKey("validation 1")) {
return (Boolean) context.get("validation 1");
} else {
return false;
}
}
}
class ValidationTwo extends Validation<MapObject, Map<String, Object>> {
public Boolean executeValidation(MapObject a, Map<String, Object> context) {
if (context.containsKey("validation 2")) {
return (Boolean) context.get("validation 2");
} else {
return false;
}
}
}
class TransformationOne extends Transformation<MapObject, MapObject, Map<String, Object>> {
public MapObject executeTransformation(MapObject a, Map<String, Object> context) {
if (context.containsKey("transformation 1")) {
a.addField("data", (String) context.get("transformation 1"));
}
return a;
}
}
class MapObject {
Map<String, String> fields = new HashMap<String, String>();
public void addField(String key, String value) {
fields.put(key, value);
}
public String getField(String key, String value) {
if (fields.containsKey(key)) {
return fields.get(key);
} else {
return null;
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : fields.entrySet()) {
sb.append(entry.getKey());
sb.append(": ");
sb.append(entry.getValue());
sb.append("\n");
}
return sb.toString();
}
}
class OperationsChainDriver {
public static void main(String[] args) {
OperationsChain oc = new OperationsChain();
oc.addOperation(new ValidationOne());
oc.addOperation(new TransformationOne());
oc.addOperation(new ValidationTwo());
oc.addOperation(new TransformationOne());
Map<String, Object> context = new HashMap<String, Object>();
context.put("validation 1", true);
context.put("validation 2", false);
context.put("transformation 1", "aloha");
MapObject mapObject = new MapObject();
mapObject.addField("field 1", "hello");
Boolean result = oc.run(mapObject, context);
if (result == true) {
System.out.println("valid\n"+oc.getCurrentObject().toString());
} else {
System.out.println("invalid\n"+oc.getCurrentObject().toString());
}
}
}
Generics are about type safety - not having to cast, because as you surely know casts are risks proved runtime. You have a very generic design yet get very concrete to and the like and have to cast a lot - this shouldn't happen since it defeats the reason to use generics at all.
As as side note: why not give an operation a method isValid that has always a return type of Boolean, a transformation can fail, too, so yo don't have to make a difference between validation and transformation. Or let it put a value in a context - the operation could know its context and could use it without casts. An operation chain could know its context and could get the results without casts.
Anyway - as long as you code has casts you are still not finished with it.
This kind of task is one that I think a functional language would be ideal for, e.g. Scala (which runs on the JVM and is perfect for interoperating with Java code), or Haskell (which doesn't run on the JVM, but has some other advantages).
OK, I understand if you don't want to learn a new programming language. But one of the key advantages would be that your code should be shorter and easier to read and reason about.
I have a class, the outline of which is basically listed below.
import org.apache.commons.math.stat.Frequency;
public class WebUsageLog {
private Collection<LogLine> logLines;
private Collection<Date> dates;
WebUsageLog() {
this.logLines = new ArrayList<LogLine>();
this.dates = new ArrayList<Date>();
}
SortedMap<Double, String> getFrequencyOfVisitedSites() {
SortedMap<Double, String> frequencyMap = new TreeMap<Double, String>(Collections.reverseOrder()); //we reverse order to sort from the highest percentage to the lowest.
Collection<String> domains = new HashSet<String>();
Frequency freq = new Frequency();
for (LogLine line : this.logLines) {
freq.addValue(line.getVisitedDomain());
domains.add(line.getVisitedDomain());
}
for (String domain : domains) {
frequencyMap.put(freq.getPct(domain), domain);
}
return frequencyMap;
}
}
The intention of this application is to allow our Human Resources folks to be able to view Web Usage Logs we send to them. However, I'm sure that over time, I'd like to be able to offer the option to view not only the frequency of visited sites, but also other members of LogLine (things like the frequency of assigned categories, accessed types [text/html, img/jpeg, etc...] filter verdicts, and so on). Ideally, I'd like to avoid writing individual methods for compilation of data for each of those types, and they could each end up looking nearly identical to the getFrequencyOfVisitedSites() method.
So, my question is twofold: first, can you see anywhere where this method should be improved, from a mechanical standpoint? And secondly, how would you make this method more generic, so that it might be able to handle an arbitrary set of data?
This is basically the same thing as Eugene's solution, I just left all the frequency calculation stuff in the original method and use the strategy only for getting the field to work on.
If you don't like enums you could certainly do this with an interface instead.
public class WebUsageLog {
private Collection<LogLine> logLines;
private Collection<Date> dates;
WebUsageLog() {
this.logLines = new ArrayList<LogLine>();
this.dates = new ArrayList<Date>();
}
SortedMap<Double, String> getFrequency(LineProperty property) {
SortedMap<Double, String> frequencyMap = new TreeMap<Double, String>(Collections.reverseOrder()); //we reverse order to sort from the highest percentage to the lowest.
Collection<String> values = new HashSet<String>();
Frequency freq = new Frequency();
for (LogLine line : this.logLines) {
freq.addValue(property.getValue(line));
values.add(property.getValue(line));
}
for (String value : values) {
frequencyMap.put(freq.getPct(value), value);
}
return frequencyMap;
}
public enum LineProperty {
VISITED_DOMAIN {
#Override
public String getValue(LogLine line) {
return line.getVisitedDomain();
}
},
CATEGORY {
#Override
public String getValue(LogLine line) {
return line.getCategory();
}
},
VERDICT {
#Override
public String getValue(LogLine line) {
return line.getVerdict();
}
};
public abstract String getValue(LogLine line);
}
}
Then given an instance of WebUsageLog you could call it like this:
WebUsageLog usageLog = ...
SortedMap<Double, String> visitedSiteFrequency = usageLog.getFrequency(VISITED_DOMAIN);
SortedMap<Double, String> categoryFrequency = usageLog.getFrequency(CATEGORY);
I'd introduce an abstraction like "data processor" for each computation type, so you can just call individual processors for each line:
...
void process(Collection<Processor> processors) {
for (LogLine line : this.logLines) {
for (Processor processor : processors) {
processor.process();
}
}
for (Processor processor : processors) {
processor.complete();
}
}
...
public interface Processor {
public void process(LogLine line);
public void complete();
}
public class FrequencyProcessor implements Processor {
SortedMap<Double, String> frequencyMap = new TreeMap<Double, String>(Collections.reverseOrder()); //we reverse order to sort from the highest percentage to the lowest.
Collection<String> domains = new HashSet<String>();
Frequency freq = new Frequency();
public void process(LogLine line)
String property = getProperty(line);
freq.addValue(property);
domains.add(property);
}
protected String getProperty(LogLine line) {
return line.getVisitedDomain();
}
public void complete()
for (String domain : domains) {
frequencyMap.put(freq.getPct(domain), domain);
}
}
}
You could also change a LogLine API to be more like a Map, i.e. instead of strongly typed line.getVisitedDomain() could use line.get("VisitedDomain"), then you can write a generic FrequencyProcessor for all properties and just pass a property name in its constructor.