List variables in Java - java

Okay, might not be the right title, but here's what I'm thinking. I am looking for a way to loop through a set of variables. For example, I'm thinking in my game there would be a series of things you could unlock, and when you entered "unlocked" or something it would show you what you had unlocked and only what you had unlocked. So it would show only the Booleans that were false.

In Java, there is no easy way to get a variable based on its name. What you can do, however, is store all your variables in a HashMap. This will accomplish what you want.
HashMap<String, Boolean> vars = new HashMap<>();
vars.put("test1", true); // to make a new variable
vars.get("test1"); // to get a variable's value
If you want to store any variable type, change the statement that creates the list of variables.
HashMap<String, Object> vars = new HashMap<>();
To find only the booleans that are false, you can simply iterate over the HashMap, like so:
for(Entry<String, Boolean> entry : vars.entrySet()) {
String key = entry.getKey();
boolean value = entry.getValue();
if(!value) {
System.out.println(key + " was false");
}
}
There is a comprehensive tutorial here, if you want to learn more about maps in Java.

You could have a method that returns a collection of keys that have a false values. Depending on your requirements (if it's a homework assignment or something) there are a couple of ways you could do it. If it is required to use only primitives, you could do something like
public List<String> getUnlockedItems(Map<String, Boolean> map) {
// map validation (not null, has entries, etc)
final List<String> unlockedEntries = new ArrayList<>();
for (Map.Entry<String, Boolean> entry : map.entries()) {
if (entry.getValue() == true) {// if it has been unlocked
unlockedEntries.add(entry.getKey());
}
}
return unlockedEntries;
}
public static void main(String[] args) {
Map<String, Boolean> unlockables = new HashMap<>();
unlockables.put("Fire Punch", false);
unlockables.put("Ice Punch", false);
unlockables.put("Mega Punch", false);
unlockables.put("Thunder Punch", false);
// Player unlocks Fire Punch
unlockables.put("Fire Punch", true);
// Get list of unlockables they have unlocked
List<String> unlockedItems = getUnlockedItems(unlockables); // returns ["Fire Punch"]
}
If you're able to define your own types, it may be better in terms of maintainability and readability to do something like
public class Unlockable {
public Unlockable(String name) {
// name validation
this.name = name;
}
public void unlock() {
unlocked = true;
}
public boolean isUnlocked() {
return unlocked;
}
private String name;
private boolean unlocked;
}
And then something like
public List<Unlockable> getUnlockedItems(List<Unlockable> unlockables) {
List<Unlockable> unlockedItems = new ArrayList<>();
for (Unlockable unlockable : unlockables) {
if (unlockable.isUnlocked()) {
unlockedItems.add(unlockable);
}
}
return unlockedItems;
}
public static void main(String[] args) {
Unlockable firePunch = new Unlockable("Fire Punch");
Unlockable icePunch = new Unlockable("Ice Punch");
Unlockable megaPunch = new Unlockable("Mega Punch");
Unlockable thunderPunch = new Unlockable("Thunder Punch");
List<Unlockable> unlockables = new ArrayList<>();
unlockables.add(firePunch);
unlockables.add(icePunch);
unlockables.add(megaPunch);
unlockables.add(thunderPunch);
// Player unlocks Fire Punch
firePunch.unlock();
// Get list of unlockables they have unlocked
List<Unlockable> unlockedItems = getUnlockedItems(unlockables); // returns ["Fire Punch"]
}

Just use a map:
Map<String, Boolean> m = new HashMap<String, Boolean>();
m.put("test1", false);
m.put("test2", true);
Usage:
System.out.println("is test1 " + m.get("test1"));

Related

Deeply nested hashmaps in Java

I have used this example to
Accessing Deeply nested HashMaps in Java
build the data structure to store node names and properties.
Here is the updated code:
class NestedMap {
private final HashMap<String, NestedMap> child;
private Map<String, Object> value = new HashMap<>();
public NestedMap() {
child = new HashMap<>();
setValue(null);
}
public boolean hasChild(String k) {
return this.child.containsKey(k);
}
public NestedMap getChild(String k) {
return this.child.get(k);
}
public void makeChild(String k) {
this.child.put(k, new NestedMap());
}
public Map<String, Object> getValue() {
return value;
}
public void setValue(Map<String, Object> value) {
this.value = value;
}
}
And my usage example:
class NestedMapIllustration {
public static void main(String[] args) {
NestedMap m = new NestedMap();
m.makeChild("de");
m.getChild("de").makeChild("content");
m.getChild("de").getChild("content").makeChild("00");
m.getChild("de").getChild("content").makeChild("0");
m.getChild("de").getChild("content").makeChild("1");
m.getChild("de").getChild("content").makeChild("01");
m.getChild("de").getChild("content").getChild("01").makeChild("fieldsets");
m.getChild("de").getChild("content").getChild("01").getChild("fieldsets").makeChild("0");
m.getChild("de").getChild("content").getChild("01").getChild("fieldsets").getChild("0").makeChild("fields");
m.getChild("de").getChild("content").getChild("01").getChild("fieldsets").getChild("0").getChild("fields").makeChild("0");
Map<String, Object> properties = new HashMap<>();
properties.put("key", "value");
properties.put("key2", "value");
m.getChild("de").getChild("content").getChild("01").getChild("fieldsets").getChild("0").getChild("fields").setValue(properties);
}
Instead of creating a new object each value I would like to always create a new HashMap where I can store the node properties.
I receive my data structure by visiting nodes in the JCR datastore and extracting their values and properties. This is how my resulting data structure should look in the output yaml file:
How can I do that more efficiently?
You've gone out of your way to let you use any key, but you're using string keys, even though one of the keys is "01" which suggests it's a number instead.
Can I conclude from this that keys are always strings?
In that case, why not define a separator, say, the slash, and use a plain old TreeMap<String, V>? Then you can do:
m.put("de/content/01/fieldsets/0/fields", properties);
If you want everything in the de/content/01 'tree', you can do:
m.subMap("de/content/01/", "de/content/010");
The above will give you a map containing every child of de/content/01. The 0 at the end of the 010 there is 'magic': Zero is the next character, after slash, in the ascii table.
If you want any given key to map to any number of values, you can use:
TreeMap<String, List<V>> map = new TreeMap<>();
to put things in:
map.computeIfAbsent(key, k -> new ArrayList<>()).add(elem);
and to get things out:
for (V value : map.getOrDefault(key, List.of())) {
// works even if key isn't in there (loops 0 times then)
}
Solution to the problem using recursion
public HashMap<String,Object> nestedMap(Node node) {
HashMap<String, Object> map = new LinkedHashMap<>();
PropertyIterator pi;
try {
pi = node.getProperties();
//Get properties for the root node
while(pi.hasNext())
{
Property p = pi.nextProperty();
String name = p.getName();
String val = p.getString();
map.put(name,val);
}//end of while for properties of root node
} catch (RepositoryException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Iterable<Node> children;
try {
children = NodeUtil.getNodes(node);
for (Node child : children) {
if (!child.getPrimaryNodeType().getName().contains("mgnl:page")) {
map.put (child.getName(), nestedMap(child));
}//end of checking if PrimaryNodeType is of type mgnl:page
}
} catch (RepositoryException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return map;
}

How to check String equality while iterating over two lists?

I have two java classes:
public class MyClass1 {
private String userId;
private String userName;
private List<CustomList1> customList1;
// getters and setters
// inner CustomList1 class
}
public class MyClass2 {
private String userId;
private List<CustomList2> customList2;
// getters and setters
// inner CustomList2 class
}
Now, I have have lists of these classes:
List<MyClass1> classOneList;
List<MyClass2> classTwoList;
In both classOneList and classTwoList lists, object should be sorted with userId ascending. userId in both lists should have same values. What I want to check is that:
Has both lists same size? If not, thow error exception about.
Has every next element from both list the same userId? If not, throw another exception.
Step 1. I have done with simply if statement.
By prototype, step 2. should look like this:
for (el1, el2 : classOneList, classTwoList) {
el1.getUserId().isEqualTo(el2.getUserId());
}
Try the below code for your problem.
public class Testing {
public static void main(String[] args) {
Map<String, List<String>> map1 = new LinkedHashMap<String, List<String>>();
List<String> m1l1 = new LinkedList<String>();
m1l1.add("One");
m1l1.add("Two");
m1l1.add("Three");
m1l1.add("Four");
map1.put("1", m1l1);
List<String> m1l2 = new LinkedList<String>();
m1l2.add("One");
m1l2.add("Two");
m1l2.add("Three");
m1l2.add("Four");
map1.put("2", m1l2);
// Add more element into the map1 by creating more list.
Map<String, List<String>> map2 = new LinkedHashMap<String, List<String>>();
List<String> m2l1 = new LinkedList<String>();
m2l1.add("One");
m2l1.add("Two");
m2l1.add("Three");
m2l1.add("Four");
map2.put("1", m2l1);
// Add more element into the map2 by creating more list.
for (Entry<String, List<String>> entry : map1.entrySet()) {
if (map2.containsKey(entry.getKey())) {
if (entry.getValue().size() == map2.get(entry.getKey()).size()) {
} else {
System.out.println("UserId are same but list are different for userid: " + entry.getKey());
}
}
else {
System.out.println("Userid '"+entry.getKey()+"' exists in map1 but is not found in map2");
}
}
}
}
Hope this may help you.
if(classOneList.size() != classTwoList.size()){
throw new ErrorException();
}else{
classOneList = classOneList.stream().sorted(Comparator.comparing(MyClass1::getUserId)).collect(Collectors.toList());
classTwoList = classTwoList.stream().sorted(Comparator.comparing(MyClass2::getUserId)).collect(Collectors.toList());
for (int i = 0; i < classOneList.size(); i++){
if(!classOneList.get(i).getUserId().equals(classTwoList.get(i).getUserId())){
throw new AnotherErrorException();
}
}
}

Finding an object from list inside a map

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

Synchronize write to two collections

I need to put some value to maps if it is not there yet. The key->value (if set) should always be in two collections (that is put should happen in two maps atomically). I have tried to implement this as follows:
private final ConcurrentMap<String, Object> map1 = new ConcurrentHashMap<String, Object>();
private final ConcurrentMap<String, Object> map2 = new ConcurrentHashMap<String, Object>();
public Object putIfAbsent(String key) {
Object retval = map1.get(key);
if (retval == null) {
synchronized (map1) {
retval = map1.get(key);
if (retval == null) {
Object value = new Object(); //or get it somewhere
synchronized (map2) {
map1.put(key, value);
map2.put(key, new Object());
}
retval = value;
}
}
}
return retval;
}
public void doSomething(String key) {
Object obj1 = map1.get(key);
Object obj2 = map2.get(key);
//do smth
}
Will that work fine in all cases? Thanks
A few problems:
Don't use "double-checked locking". A quick Google search will reveal tons of articles which explain the problems with this technique. Just check inside the synchronized block.
You don't need to synchronize on both map1 and map2. Just use one or the other.
Synchronize within doSomething. Make sure you synchronize on the same object which is used for synchronization in putIfAbsent.
You should never use synchronized with ConcurrentHashMap (it's pretty much defeating the purpose). The best way to make atomic additions to CHH is by using the built-in replace method. For example:
do {
oldValue1 = map1.get(key1);
oldValue2 = map2.get(key2);
newValue1 = // some logic to determine a new value for key1/value1
newValue2 = // some more logic to determine a new value for key2/value2
} while (!map1.replace(key1, oldValue1, newValue1) && !map2.replace(key2, oldValue2, newValue2));
I don't know how to specifically adapt this to your example, but this should give you somewhere to start. Basically what happens is you get the key from the map, do some logic, and if the key is still the same as it was before the logic, it will replace the entry and return true, then the loop will break. Otherwise it will just repeat the loop until it can do the update atomically.
Ok, I finally came to this solution:
private Map<String, Object> map1 = new HashMap<String, Object>();
private Map<String, Object> map2 = new HashMap<String, Object>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public void putIfAbsent(String key, Object o) {
rwl.readLock().lock();
try {
if (map1.get(key) == null) {
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
if (map1.get(key) == null) {
map1.put(key, getValue());
map2.put(key, getValue());
}
}finally {
rwl.readLock().lock();
rwl.writeLock().unlock();
}
}
} finally {
readLock.unlock();
}
}
public void readMap(String key) {
rwl.readLock().lock();
try {
Object obj1 = map1.get(key);
Object obj2 = map2.get(key);
} finally {
rwl.readLock().unlock();
}
}
In order to do what you want, I'd use atomic references:
class PairHolder {
public final ConcurrentMap map1;
public final ConcurrentMap map2;
public PairHolder(...) // set values here.
}
private AtomicReference<PairHolder> mapHolder = ... // initialize it somehow
do {
PairHolder holder = mapHolder.get();
ConcurrentMap map1 = holder.map1.clone()
ConcurrentMap map2 = holder.map2.clone()
newMap1.putIfAbsent(...);
newMap2.putIfAbsent(...);
} while (!mapHolder.compareAndSet(holder, new PairHolder(newMap1,newMap2))
this way you alway will be sure, that mapHolder contains the reference to PairHolder, which in turn have two maps updated 100% atomically. At least CAS should guarantee this, however on multi-processor systems it might be false.

My method is too specific. How can I make it more generic?

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.

Categories