How to create dynamic variable names in Java? [duplicate] - java

This question already has answers here:
Assigning variables with dynamic names in Java
(7 answers)
Closed 8 years ago.
I have a hashmap of type Map<String, List<Integer>> empage where String is the name of the department and List is the list of age of employees who work in that department.
Now for each department I want to divide the age of employees into 5 age categories like (0-20,20-40...and so on).How can I create these 5 list variables for each department dynamically? I mean I cannot hardcode variable name like Finance_grp1 and so on for each department name? So basically I want something like:
for(each departname in empage.keyset())
{
create Arraylist departmentname_grp1
create Arraylist departmentname_grp2
create Arraylist departmentname_grp3
.
.
and so on till 5 groups
}
For Example the structure that I want is something like this:
Department Finance
grp1 for age 0-20
grp2 for age 20-40
and so on till grp5
Department HR
grp1 for age 0-20
grp2 for age 20-40
and so on till grp5
This way for all the department names, I want to group employees age into groups
Also after creating these 5 groups and processing the employee age into categories, I want to variable of type ChartSeries for each department name which then I will add to create a bar chart.So, I want something like:
for(department_name in empage)
{
ChartSeries department_name = new ChartSeries();
}
Can anyone help me in resolving this issue?
UPDATE: I know that in Java we cannot append dynamic string while creating variables. What I want is the possible solution to this issue and above problem

The basic answer to your question is you cannot dynamically assign variable names in Java. Here's another SO post that has more potential ways around this: Assigning Dynamic Variable Names

if the dept-age calculations are very important for your business logic, you could consider to create a new type like DeptAgeStat.
It has:
String name;
List<Integer> allAges;
List<Integer> getGroup1(){//return new List, or a ListView of allAges};
List<Integer> getGroup2(){//same as above};
...
List<Integer> getAgesWithWhateverPartitionCondidtions(here could have condition para see below text){...};
this will ease your future calculation. If it is necessary, e.g. you could have different criteria to filter/group ages in future, you can even design a PartitionCriteria type as parameter to those methods. again, it depends on the requirement.
Hope it helps.

I would propose you to change the List inside your Map, so you will have:
Map<String, Map<Integer,List<Integer>>>
where the outer Map has the departments's String as Keys, and the inner Map has the Integer representation per age group (eg 0,1,2,3,4).
*As #sage88 noted, you could use String instead of Integer as keys for the age groups.

Alright I think I see better what you're trying to do, but ultimately the answer to your question is still to use an appropriate collection. Try something like this:
Map<Department, Map<Integer, List<Employee>>> departmentEmployeeAgeMap;
where Integer is the age bracket they fall into 0-20 being 0, 20-40 being 1, and so on. This assumes you have a department class, if you don't you could use String to represent the department names as well.
This way when you want to store an employee they are accessible by a department key and then an Integer age range key.
So if you need to add employees to the groups you would go like this:
Map<Department, Map<Integer, List<Employee>>> departmentEmployeeAgeMap = new Map<Department, Map<Integer, ArrayList<Employee>>>();
Map<Integer, List<Employee>> currentDepartmentAgeMap;
for(department : departments) {
departmentEmployeeAgeMap.put(department, new Map<Integer, List<Employee>>());
currentDepartmentAgeMap = departmentEmployeeAgeMap.get(department);
for(int i=0; i<5; i++) {
currentDepartmentAgeMap.put(i, new ArrayList<Employee>());
}
for(employee : department) {
currentDepartmentAgeMap.get(employee.getAge()/20).add(employee);
}
}
And then accessing this datastructure to pull the employees back out is easy:
departmentEmployeeAgeMap.get(department).get(1);
Will retrieve a list of all the employees who work in a given department between the ages of 20-39.
If you really want to be able to create dynamic variable names you should consider another language other than java. This is not how Java was intended to function and it does not do this well.

Related

JAVA : Best performance-wise method to find an object stored in hashMap

I have a bunch of objects stored in hashMap<Long,Person> i need to find the person object with a specific attribute without knowing its ID.
for example the person class:
public person{
long id;
String firstName;
String lastName;
String userName;
String password;
String address;
..
(around 7-10 attributes in total)
}
lets say i want to find the object with username = "mike". Is there any method to find it without actually iterating on the whole hash map like this :
for (Map.Entry<Long,Person> entry : map.entrySet()) {
if(entry.getValue().getUserName().equalsIgnoreCase("mike"));
the answers i found here was pretty old.
If you want speed and are always looking for one specific attribute, your best bet is to create another 'cache' hash-map keyed with that attribute.
The memory taken up will be insignificant for less than a million entries and the hash-map lookup will be much much faster than any other solution.
Alternatively you could put all search attributes into a single map (ie. names, and ids). Prefix the keys with something unique if you're concerned with collisions. Something like:
String ID_PREFIX = "^!^ID^!^";
String USERNAME_PREFIX = "^!^USERNAME^!^";
String FIRSTNAME_PREFIX = "^!^FIRSTNAME^!^";
Map<String,Person> personMap = new HashMap<String,Person>();
//add a person
void addPersonToMap(Person person)
{
personMap.put(ID_PREFIX+person.id, person);
personMap.put(USERNAME_PREFIX+person.username, person);
personMap.put(FIRSTNAME_PREFIX+person.firstname, person);
}
//search person
Person findPersonByID(long id)
{
return personMap.get(ID_PREFIX+id);
}
Person findPersonByUsername(String username)
{
return personMap.get(USERNAME_PREFIX+username);
}
//or a more generic version:
//Person foundPerson = findPersonByAttribute(FIRSTNAME_PREFIX, "mike");
Person findPersonByAttribute(String attr, String attr_value)
{
return personMap.get(attr+attr_value);
}
The above assumes that each attribute is unique amongst all the Persons. This might be true for ID and username, but the question specifies firstname=mike which is unlikely to be unique.
In that case you want to abstract with a list, so it would be more like this:
Map<String,List<Person>> personMap = new HashMap<String,List<Person>>();
//add a person
void addPersonToMap(Person person)
{
insertPersonIntoMap(ID_PREFIX+person.id, person);
insertPersonIntoMap(USERNAME_PREFIX+person.username, person);
insertPersonIntoMap(FIRSTNAME_PREFIX+person.firstname, person);
}
//note that List contains no duplicates, so can be called multiple times for the same person.
void insertPersonIntoMap(String key, Person person)
{
List<Person> personsList = personMap.get(key);
if(personsList==null)
personsList = new ArrayList<Person>();
personsList.add(person);
personMap.put(key,personsList);
}
//we know id is unique, so we can just get the only person in the list
Person findPersonByID(long id)
{
List<Person> personList = personMap.get(ID_PREFIX+id);
if(personList!=null)
return personList.get(0);
return null;
}
//get list of persons with firstname
List<Person> findPersonsByFirstName(String firstname)
{
return personMap.get(FIRSTNAME_PREFIX+firstname);
}
At that point you're really getting into a grab-bag design but still very efficient if you're not expecting millions of entries.
The best performance-wise method I can think of is to have another HashMap, with the key being the attribute you want to search for, and the value being a list of objects.
For your example this would be HashMap<String, List<Person>>, with the key being the username. The downside is that you have to maintain two maps.
Note: I've used a List<Person> as the value because we cannot guarantee that username is unique among all users. The same applies for any other field.
For example, to add a Person to this new map you could do:
Map<String, List<Person>> peopleByUsername = new HashMap<>();
// ...
Person p = ...;
peopleByUsername.computeIfAbsent(
p.getUsername(),
k -> new ArrayList<>())
.add(p);
Then, to return all people whose username is i.e. joesmith:
List<Person> matching = peopleByUsername.get("joesmith");
Getting one or a few entries from a volatile map
If the map you're operating on can change often and you only want to get a few entries then iterating over the map's entries is ok since you'd need space and time to build other structures or sort the data as well.
Getting many entries from a volatile map
If you need to get many entries from that map you might get better performance by either sorting the entries first (e.g. build a list and sort that) and then using binary search. Alternatively you could build an intermediate map that uses the attribute(s) you need to search for as its key.
Note, however, that both approaches at least need time so this only yields better performance when you're looking for many entries.
Getting entries multiple times from a "persistent" map
If your map and its valuies doesn't change (or not that often) you could maintain a map attribute -> person. This would mean some effort for the initial setup and updating the additional map (unless your data doesn't change) as well as some memory overhead but speeds up lookups tremendously later on. This is a worthwhile approach when you'd do very little "writes" compared to how often you do lookups and if you can spare the memory overhead (depends on how big those maps would be and how much memory you have to spare).
Consider one hashmap per alternate key.
This will have "high" setup cost,
but will result in quick retrieval by alternate key.
Setup the hashmap using the Long key value.
Run through the hashmap Person objects and create a second hashmap (HashMap<String, Person>) for which username is the key.
Perhaps, fill both hashmaps at the same time.
In your case,
you will end up with something like HashMap<Long, Person> idKeyedMap and HashMap<String, Person> usernameKeyedMap.
You can also put all the key values in the same map,
if you define the map as Map<Object, Person>.
Then,
when you add the
(id, person) pair,
you need to also add the (username, person) pair.
Caveat, this is not a great technique.
What is the best way to solve the problem?
There are many ways to tackle this as you can see in the answers and comments.
How is the Map is being used (and perhaps how it is created). If the Map is built from a select statement with the long id value from a column from a table we might think we should use HashMap<Long, Person>.
Another way to look at the problem is to consider usernames should also be unique (i.e. no two persons should ever share the same username). So instead create the map as a HashMap<String, Person>. With username as the key and the Person object as the value.
Using the latter:
Map<String, Person> users = new HashMap<>();
users = retrieveUsersFromDatabase(); // perform db select and build map
String username = "mike";
users.get(username).
This will be the fastest way to retrieve the object you want to find in a Map containing Person objects as its values.
You can simply convert Hashmap to List using:
List list = new ArrayList(map.values());
Now, you can iterate through the list object easily. This way you can search Hashmap values on any property of Person class not just limiting to firstname.
Only downside is you will end up creating a list object. But using stream api you can further improve code to convert Hashmap to list and iterate in single operation saving space and improved performance with parallel streams.
Sorting and finding of value object can be done by designing and using an appropriate Comparator class.
Comparator Class : Designing a Comparator with respect to a specific attribute can be done as follows:
class UserComparator implements Comparator<Person>{
#Override
public int compare(Person p1, Person p2) {
return p1.userName.compareTo(p2.userName);
}
}
Usage : Comparator designed above can be used as follows:
HashMap<Long, Person> personMap = new HashMap<Long, Person>();
.
.
.
ArrayList<Person> pAL = new ArrayList<Person>(personMap.values()); //create list of values
Collections.sort(pAL,new UserComparator()); // sort the list using comparator
Person p = new Person(); // create a dummy object
p.userName="mike"; // Only set the username
int i= Collections.binarySearch(pAL,p,new UserComparator()); // search the list using comparator
if(i>=0){
Person p1 = pAL.get(Collections.binarySearch(pAL,p,new UserComparator())); //Obtain object if username is present
}else{
System.out.println("Insertion point: "+ i); // Returns a negative value if username is not present
}

Get the same element values on multiple arrays

Ive been searching SO about this question and most only have the problem with two arrays comparing by have a nested loop. My problem is quite the same but on a bigger scale. Suppose I have a 100 or thousand user on my app, and each user has the list of item it wants.
Something like this
User1 = {apple,orange,guava,melon,durian}
User2 = {apple, melon,banana,lemon,mango}
User3 = {orange,carrots,guava,melon,tomato}
User4 = {mango,carrots,tomato,apple,durian}
.
.
Nuser = ...
I wanted to see how many apples or oranges was listed from all the users array. So I am basically comparing but on a bigger scale. The data isn't static as well, A user can input an unknown fruit from the developers knowledge but on the users knowledge they can put it there so there can be multiple users that can put this unknown fruit and yet the system can still figure out how many is this unknown item was listed. Keep in mind this is a dynamic one. User can reach for example a 100 users depending how popular an app would be. I can't afford to do nested loop here.
PS this is not the exact problem but it is the simplest scenario I can think of to explain my problem.
PS: just to clarify, I dont intend to use 3rd party lib as well like guava. I am having a problem on proguard with it.
Edit
Just read that Original poster cannot use Java 8, which is a pity, because this would realy make it very easy!
Java 7 solution
final Map<String, Integer> occurencesByFruit = new HashMap<>();
for (User user : users) {
String[] fruits = user.getFruits();
for (String fruit : fruits) {
final Integer currentCount = occurencesByFruit.get(fruit);
if (currentCount == null) {
occurencesByFruit.put(fruit, 1);
} else {
occurencesByFruit.put(fruit, currentCount + 1);
}
}
}
Java 8 solution
I'd stream the users, flatMap() to the actual fruit elements, and then use Collectors.groupingBy() with a downstream collector Collectors.counting().
This will give you a Map where the keys are the fruits, and the values are the occurrences of each fruit throughout all your users.
List<User> users = Arrays.asList(/* ... */);
final Map<String, Long> occurencesByFruit = users.stream()
.map(User::getFruits)
.flatMap(Arrays::stream)
.collect(Collectors.groupingBy(f -> f, Collectors.counting()));
Seems it is a good possibility to use HashMap<Item, Integer> fruits. You could iterate over all Users (you would need to store all Users in some kind of list, such as ArrayList<User> users) and check the list of items chosen by each User (I suppose User should have a field ArrayList<Item> items in its body to store items). You could achieve it with something like that:
for (User user : users) { // for each User from users list
for (Item item : user.items) { // check each item chosen by this user
if (fruits.containsKey(item) { // if the fruit is already present in the items HashMap increment the amount of items
int previousNumberOfItems = fruits.get(item);
fruits.put(item, ++previousNumberOfItems);
else { // otherwise put the first occurrency of this item
fruits.put(item, 1);
}
}
}
I would either create an ArrayList containing a HashMap with strings and ints or use two ArrayLists (one of type String and one of type Integer). Then you can iterate over every entry in each of the user arrays (this is only a simple nested loop). For every entry in the current user array you check if there is already the same entry in the ArrayList you created additionally. If so, you increment the respective int. If not, you add a string and an int. In the end, you have the number of occurrences of all the fruit strings in the added ArrayLists, which is, if I understood you correctly, just what you wanted.

DataStructure to use to store elements and sorted them in the order of their usage frequency/timestamp

I have one problem statement: Suppose there is Employee class and a class has to be designed which provides get and put methods to access and add employees. Employee class is a third party class so we can't make changes in it. There is a memory limit like max 50k employees can be stored. Now if while adding employee if memory limit is reached then remove one employee by following any of the below approaches and add the new employee:
on the basis of access frequency
on the basis of timestamp. one with the old timestamp should be removed
I can think of taking two hashmaps: - one for storing employee objects and one with the employees with key and timestamp or frequency as value.
But this is not the optimal solution. Please provide the valuable suggestions on which data structure can be used to solve this problem.
Just use a normal collection for storing your Employee. When adding a new employee and you detect that the memory is full, you just determine the employee with the smallest sort order criteria and remove it from the database.
Since you did not provide the Employee class I declare here an example:
class Employee {
int frequency;
Instant lastAccess;
}
Your database (can also be LinkedList):
private List<Employee> myDatabase = new ArrayList<>();
The comporator for evaluating the employee with the smallest criteria (first frequency is compared, if equals the lastAccess is compared:
public int compare( Employee emp1, Employee emp2 )
{
int result = emp1.frequency - emp2.frequency;
if ( result == 0 )
{
result = emp1.lastAccess.compareTo( emp2.lastAccess );
}
return result;
}
And here the 'main' method which inserts a new employee into your database (the implementation of isMemoryFull is left to you):
private void insertEmp( Employee emp)
{
if ( isMemoryFull() )
{
Employee minEmp = myDatabase.stream().min( this::compare ).get();
myDatabase.remove( minEmp);
}
myDatabase.add( emp );
}
Note: This works with Java8 lambdas and streams. For java < 8 you have to implement the compare method formally as Comparator and use Collections.sort instead the stream().min() method.

Manipulating data in java List<Object> in a structured way

I have the following class :
class Students{
int age;
int dept;
}
Lets say i have a List<Students> and I want to manipulate the list by doing simple calculations like : calculate the mean, calculate the middle value (e.g. (age+debt)/2), find the closest value to the mean and so on. How can I do this in a structured way?. I want to be in a position where I can use different combinations on the list. e.g. calculate mean of age // calculate mean of the middle value from age/debt, find the closest value of the age etc.
How should i approach this?. Would appreciate it if someone could point me in the right direction.
Apache Math has a nice Descriptive Statistics package that does this sought of thing.
http://commons.apache.org/proper/commons-math/userguide/stat.html#a1.2_Descriptive_statistics
If you're using Java 8 this works well with Lambdas:
DescriptiveStatistics stats = new DescriptiveStatistics();
students.forEach(s -> stats.add(s.age));
double mean = stats.getMean();
And to filter etc:
//Only students with an age > 18
students.stream.filter(s -> s.age > 18).forEach(s -> stats.add(s.age));
If you're not using Java 8 then simply foreach it.
You can create a separate class (StudentCalculator) that will require a List of Students (perhaps pass the List in the constructor) and have the instance methods perform calculations on the List.
Or you can create a utility (e.g. StudentCalculatorUtility) where you would define a series of methods that would accept a List of Students as a parameter, that would perform all the calculations you would need on the students(middle value,closest to mean, etc.)
There is a concept where you step through a list and perform an operation on each item in turn which may or may not change the item.
In this case, you want a method that takes an item from the list does some stuff and returns a running total.
int sumItems(Student stu, int sum){
return (stu.age + stu.debt)/2;
}
To use this method, either use either a forEach or an iterator.
Iterator itr = Students.iterator(); // assuming List<Student> Students = new List<Student>()
int sum = 0;
while(itr.hasnext()){
sum = sumItems(itr.next(), sum)
}
Now do something with your sum.

Move a value from one key to another in HashMap

I've got an ArrayList that holds student object as follows:
List<Students> stdList = new ArrayList<Students>();
stdList.add(new Students(1,"std1","address1"));
stdList.add(new Students(2,"std2","address2"));
stdList.add(new Students(3,"std3","address3"));
stdList.add(new Students(4,"std4","address4"));
stdList.add(new Students(5,"std5","address5"));
stdList.add(new Students(6,"std6","address6"));
stdList.add(new Students(7,"std7","address7"));
stdList.add(new Students(8,"std8","address8"));
Now, I need to divide the stdList to two groups containing equal no of students say 4 in this case, and add them to hashMap which I achieved by:
int j=0;
HashMap<Integer,List<Students>> hm = new HashMap<>();
for (int i = 0; i < stdList.size(); i = i + 4)
{
j++;
hm.put(j,stdList.subList(i, i + 4));
}
The hashmap now contains key value pair as:
{1=[1 std1 address1, 2 std2 address2, 3 std3 address3, 4 std4 address4], 2=[5 std5 address5, 6 std6 address6, 7 std7 address7, 8 std8 address8]}
Now I need to move one value say "3 std3 address3" from "key 1" to "key 2" like:
{1=[1 std1 address1, 2 std2 address2, 4 std4 address4], 2=[5 std5 address5, 6 std6 address6, 7 std7 address7, 8 std8 address8,3 std3 address3]}
How can I achieve this?
Assume "someKey" is the key you're gonna remove, then
key1.put(someKey, key2.remove(someKey));
List<Student> ls = hm.get(1);
Student st = ls.get(3);
ls.remove(st); hm.get(2).add(st);
you don't need to search the list if you could access it by the index.
You can do like this;
Student stud3=myMap.get(1).remove(myMap.get(1).get(2));
List<Student> secondList=myMap.get(2);
secondList.add(stud3);
myMap.put(2,secondList);
where myMap is map formed by you.
The solution would be to get the list of students from the HashMap and remove the Student object you want to move. Then get the other list from the HashMap and simply just add the object.
I didn't run the below code, but it would be something like this
//Get the list for Key 1
List<Students> list = hm.get(Integer.valueOf(1));
//Remove the 3rd value, that would be your "3 std3 address3"
Students std = list.remove(2);
//Now get the list of Key 2
list = hm.get(Integer.valueOf(2));
//Add the value to that list
list.add(std);
I think you know how to search an element in list/map, and how to remove/add them. You have shown it in your codes. Your requirement is just another combination of those method calls, they won't be problem for you.
I guess you cannot go further because you got an exception:
ConcurrentModificationException
Because I see that you have used subList() method. It will return a view of backed list. You can change the elements in that list, but any modification of structure will throw that exception.
If this is the problem you are facing, simple solution would be creating a new list when you invoked subList, like new ArrayList(stdList.subList(i, i + 4)) then you can do structural modifications.
if this is not your problem, pls leave a comment, I will remove the answer.
P.S you may want to change your data-structure a bit, I don't know your exact requirement, but current structure is not so convenient..... you can check out the guava multi-map...

Categories