Move a value from one key to another in HashMap - java

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

Related

Iterate through list and add to list without incurring ConcurrentModificationException - Java

Apologies, this has been done to death but I am really struggling to implement a solution to this problem and I am quite new to Java.
I need to be able to call a method which basically allows a Person object to be added to a list.
The main problem I have encountered whilst trying to implement a solution is a 'ConcurrentModificationException' which is understandable given I have been trying to update the list whilst in the middle of a for each loop.
So, I have come up with the following solution which prevents the 'ConcurrentModificationException' but my solution doesn't work properly and seems overcomplicated - see following code snippet of method:
public void addPerson(Person aPerson) {
// tempPersons list for temporarily storing Person objects
tempPersons = new ArrayList<>();
// if persons list is initially empty, add aPerson object to it
if (persons.isEmpty()) {
persons.add(aPerson);
}
// if persons list is not initially empty
if (!persons.isEmpty()) {
// loop through persons list
for (Person anotherPerson : persons) {
// if persons list anotherPerson first name is the same as the aPerson first name, print message
if (anotherPerson.getFirstName().equals(aPerson.getFirstName())) {
System.out.println("The Person " + aPerson.getFirstName() +
" is already entered in the list");
}
// otherwise add the aPerson object to the tempPersons list
else {
tempPersons.add(aPerson);
}
}
// once out of loop, add the tempPersons list to the persons list
persons.addAll(tempPersons);
// create new tempPersons2 list based on a hashset of the persons list so there are no duplicates
List<Person> tempPersons2 = new ArrayList<>(
new HashSet<>(persons));
// assign tempPersons2 list without duplicates to persons list
persons = tempPersons2;
}
}
So, if for example I call the above addPerson method 4 separate times with a mixture of unique and duplicate objects (aPerson param), the method correctly identifies that there is already an object with the same first name in there but the persons list always seems to end up with a duplicate object (first name) in there e.g. if I have the following objects:
Person1
FirstName = Bob
Person2
FirstName = Jan
Person3
FirsName = Bob
Person4
FirstName = Ann
Then I make the following method call 4 separate times:
addPerson(Person1);
addPerson(Person2);
addPerson(Person3);
addPerson(Person4);
When I call a print method, I get the following output:
The Person Bob is already entered in the list
Jan
Ann
Bob
Bob
I would expect the following:
The Person Bob is already present
Jan
Ann
Bob
Apologies for all the waffle and what is probably a really simple solution to most of you but I have been stuck on this for a couple of days.
Similar article can be found here but I am still struggling.
Adding elements to a collection during iteration
Without changing your code too much:
public void addPerson(Person person) {
// The guard is more or less a premature optimization and should be removed.
if (persons.isEmpty()) {
persons.add(person);
return;
}
for (Person anotherPerson : persons) {
if (anotherPerson.getFirstName().equals(person.getFirstName())) {
System.out.println("The Person " + person.getFirstName() +
" is already entered in the list");
return;
}
}
persons.add(person);
}
This would exit the method when a match is found, if there are no matches the person is simply added after the loop. Note the return in the first check.
Optimizations of this code could be using a Map or Set to speed up the contains check; also just using anyMatch on persons would lead to a more elegant solution.
The duplicates are caused by your for loop and it's else condition. If Bob and Jan is in your collection and you add a second Bob then Jan won't be equal to Bob and your else path is executed adding a duplicate to your final persons List.
You can use Set instead of list which will satisfy your need and implement the comparator or comparable interface for person comparison

Java console - Remove element from arraylist, still shows up when printing

I have an arraylist which I can add to using constructors. That works perfectly fine, and they show up when printing, but when I remove them, it still shows up in the list when printing, even though when I try to remove it for a second time, it says "NullPointerException", or goes to my failsafe else if. The problem therefore have to be in the printing part, or am I thinking completely incorrectly? Is it not updating the arraylist after removing an element?
Thank you.
Instance stuff - Creating Arraylist/constructor(employeeVar = int)
static Employees[] employee = new Employees[employeeVar];
static List<Employees> EmployeesAL = new ArrayList<>(Arrays.asList(employee));
Printing Arraylist
else{
System.out.println("id: " + EmployeesAL.get(i).getEmployeeID() + "Name: " + EmployeesAL.get(i).getEmployeeName());
}
Removing element
else if (employee[idToRemove].getEmployeeID() == idToRemove && customer[idToRemove].getEmployeeName() != null){
EmployeesAL.remove(idToRemove);
employee[idToRemove] = null;
}
}
The way you set it up, EmployeesAL and employee are completely unbound. A change in one is not reflected in the other.
So, when you have: [Jane, Joe, Jack] in your employee, that means at first, EmployeesAL is also [Jane, Joe, Jack]. But if you then remove an employee, the effect is that the entry in the array is nulled out, but the entry in the arraylist is removed. So, removing Joe results in employee being [Jane, null, Jack] and EmployeesAL being [Jane, Jack]. Now Jack's position in the array is index 2 (employee[2]) and in the arraylist it's 1: EmployeesAL.get(1). Your code checks employee[idToRemove] and then asks EmployeeAL to remove that index, which, obviously, is going to fail.
Why do you have 2 data structures with the same data? Stop doing that. Pick one: Either have Employee[] employees, or have List<Employee>. You probably want only the latter:
List<Employee> employees = new ArrayList<>();
employees.addAll(Arrays.asList(new Employee("Jane"), new Employee("Joe"), etc);

Retrieve String from values in HashMap at a specific occurrence of special character

So I'm trying retrieve specific substrings in values in a Hashmap constructed like this..
HashMap<ID, "Home > Recipe > Main Dish > Chicken > Chicken Breasts">
Which is passed from a different method that returns a HashMap
In above example, I need to retrieve Chicken.
Thus far, I have..
public static ArrayList<String> generalize() {
HashMap<String, String> items = new HashMap<>();
ArrayList<String> cats = new ArrayList<>();
items = RecSys.readInItemProfile("PATH", 0, 1);
for(String w : items.values()) {
cats.add(w);
}
for(String w : cats) {
int e = w.indexOf('>', 1 + w.indexOf('>', 1 + w.indexOf('>')));
String k = w.substring(e+1);
System.out.print(k);
e = 0;
}
System.out.println("k" + cats);
return cats;
}
Where I try to nullify String e for each iteration (I know it's redundant but it was just to test).
In my dataset, the first k-v pair is
3880=Home  >  Recipes  >  Main Dish  >  Pasta,
My output is
Pasta
Which is ok. If there are more than 3x ">", it'll return all following categories. Optimally it wouldn't do that, but it's ok if it does. However, further down the line, it (seemingly) randomly returns
Home > Recipe
Along with the rest of the data...
This happens at the 6th loop, I believe.
Any help is greatly appreciated..
Edit:
To clarify, I have a .csv file containing 3 columns, whereas 2 are used in this function (ID and Category). These are passed to this function by a read method in another class.
What I need to do is extract a generalized description of each category, which in all cases is the third instance of category specification (that is, always between the third and fourth ">" in every k-v pair).
My idea was to simply put all values in an arraylist, and for every value extract a string from between the third and fourth ">".
I recommend using the following map:
Map<Integer, List> map = new HashMap<>();
String[] vals = new String[] { "HomeRecipe", "Main Dish", "Chicken",
"Chicken Breasts" };
map.put(1, Arrays.asList(vals));
Then, if you need to find a given value in your original string using an ID, you can simply call ArrayList#get() at a certain position. If you don't care at all about order, then a map of integers to sets might make more sense here.
If you can. change your data structure to a HashMap<Integer, List<String>> or HashMap<Integer, String[]>. It's better to store the categories (by cats you mean categories right?) in a collection instead of a string.
Then you can easily get the third item.
If this is not possible. You need to do some debugging. Start by printing every input and output pair and find out which input caused the unexpected output. Your indexOf method seems to work at first glance.
Alternatively, try this regex method:
String k = cats.replaceAll("(?:[^>]+\\s*>\\s*){3}([^>]+).*", "$1");
System.out.println(k);
The regex basically looks for a xxx > yyy > zzz > aaa ... pattern and replaces that pattern with aaa (whatever that is in the original string).

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.

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

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.

Categories