I need a solution to change the value of an element in a multidimensional ArrayList in Java. This is my ArrayList:
public class Users {
ArrayList<ValidateUser> personer = new ArrayList<ValidateUser>();
public Users() {
personer.add(new ValidateUser("admin", "asdf123", 0.8, "admin"));
personer.add(new ValidateUser("jesper", "ukamm19", 2.5, "user"));
personer.add(new ValidateUser("lars", "lol123", 1.5, "user"));
}
I want to change the double value (0.8) at the user "admin", for example.
This would be done from another a class.
How to do so?
Thanks in advance! :)
As I've stated in my comment, just iterate through the list to find the object. If you're going to do this a lot, consider using a map.
for (ValidateUser user : personer)
if (user.getName().equals("admin"))
user.setNumber(someNumber);
First, note that this is not a multidimensional array, is just a list that holds elements of ValidateUser class object references. Second, you need to access to the element before updating it. You have several ways to accomplish this:
Implement the equals and hashCode methods in your ValidateUser class, then just retrieve the object from your List:
ValidateUser adminUser = personer.get(new ValidateUser("admin", "", 0.8, ""));
adminUser.set...
Note: this looks ridiculous but will work (assuming your equals method only checks by the field that holds this "admin" value.
Navigate through the array and seek for the desired element manually, then update it:
for (ValidateUser user : personer) {
if ("admin".equals(user.getXxx()) {
user.set...
break; //don't forget this!
}
}
Use a different data structure like a Map<String, ValidateUser> to store your data and faster retrieval:
Map<String, ValidateUser> personerMap = new LinkedHashMap<String, ValidateUser>();
personerMap.add("admin", new ValidateUser("admin", ...);
//fill the map with other values...
//if you still want a Collection<ValidateUser> personer variable
Collection<ValidateUser> personer = personerMap.values();
//now check for the desired element
ValidateUser admin = personerMap.get("admin");
if (admin != null) {
admin.set...
}
By comments, your ValidateUser is an immutable object, so you cannot update its fields using setters (because there aren't). So, the best approach here is to use a ListIterator<ValidateUser> instead (not to confuse with Iterator) and replace the element by your modified object. Here's an example:
//the new immutable ValidateUser that will replace the older one...
//set the parameters as needed
ValidateUser newAdmin = new ValidateUser("admin", ...);
ListIterator<ValidateUser> listIterator = personer.listIterator();
while (listIterator.hasNext()) {
ValidateUser validateUser = listIterator.next();
if ("admin".equals(validateUser.getXxx()) {
listIterator.set(newAdmin);
break;
}
}
Related
According to a business requirement, I will need not check if the value of an attribute if it equal to a list of given string values.
I am wondering what is the better way to do it, in case if someday there is a new value that needs to be added. Should these values be stored in a table?
List<String> values = new ArrayList<>();
values.add("value1");
values.add("value2");
values.add("value3");
if(values.contains(brand){
// if the brand contains the given values
// implement a specific logic
}
Thank you
You could do it like this. If there was a Brand class that returned an immutable list of attributes.
Brand brand = new Brand(...);
List<String> values = new ArrayList<>();
values.add("value1");
values.add("value2");
values.add("value3");
if(brand.getAtrributes().containsAll(values)) {
// do something.
}
But imo, it would be better to use an EnumSet and define the attributes as enums.
enum Attr {VALUE1, VALUE2, VALUE3,VALUE4, VALUE5};
EnumSet<Attr> attr = EnumSet.of(Attr.VALUE1, Attr.VALUE2, Attr.VALUE3, Attr.VALUE4, Attr.VALUE5);
if(attr.contains(Attr.VALUE1)) {
// do something.
}
There is still a containsAll method as well as other potentially helpful methods.
create a record that takes an set of enums
create an instance of that with values 2-4.
And use the range feature to verify.
record Brand(EnumSet<Attr> getAttributes){}
Brand brand = new Brand(EnumSet.of(Attr.VALUE2,Attr.VALUE3, Attr.VALUE4));
EnumSet<Attr> required = EnumSet.range(Attr.VALUE2,Attr.VALUE4);
if (brand.getAttributes().containsAll(required)) {
System.out.println("Good to go");
}
Prints
Good to go.
I am wondering if there is already an implemented feature in streams (or Collectors) which first groups a stream by an attribute and then returns the first element in the list sorted by another attribute. E.g. the following code tries to group a stream of objects using the first attribute and then wants to collect that object which has the highest value of the second attribute.
class MyClass{
String att1;
String att2;
}
Now, I want to achieve something like this with Stream myClassStream -
Map<String,MyClass> myMap = myClassStream().collect(Collectors.groupingBy(MyClass::getAtt1)); //Now I want to do Sorting after grouping to collect only the element which has the highest value of attr2.
My code using simple for loop is:
Map<String, MyClass> postAnalyticsMap = new HashMap<>();
for (MyClass post : myClassList) {
if (post.get(post.getAtt1()) == null) {
post.put(post.getAtt1(), post);
} else {
MyClass existingClass = postAnalyticsMap.get(post.getAtt1());
if (existingPostAnalytics.getAtt2() < post.getAtt2()) {
postAnalyticsMap.put(post.getAtt1(), post);
}
}
}
Any help will be much appreciated.
Use the toMap with merge function to find the max element on second attribute
Map<String, MyClass> map = myClassList.stream()
.collect(Collectors.toMap(MyClass::getAtt1, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(MyClass::getAtt2))));
You can do something like this:
myClassStream()
.collect(Collectors.groupingBy(MyClass::getAtt1,
Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(MyClass::getAtt2)),
myClass -> myClass.map(MyClass::getAtt2).orElse(""))));
When I wrote this piece of code due to the pnValue.clear(); the output I was getting was null values for the keys. So I read somewhere that adding values of one map to the other is a mere reference to the original map and one has to use the clone() method to ensure the two maps are separate. Now the issue I am facing after cloning my map is that if I have multiple values for a particular key then they are being over written. E.g. The output I am expecting from processing a goldSentence is:
{PERSON = [James Fisher],ORGANIZATION=[American League, Chicago Bulls]}
but what I get is:
{PERSON = [James Fisher],ORGANIZATION=[Chicago Bulls]}
I wonder where I am going wrong considering I am declaring my values as a Vector<String>
for(WSDSentence goldSentence : goldSentences)
{
for (WSDElement word : goldSentence.getWsdElements()){
if (word.getPN()!=null){
if (word.getPN().equals("group")){
String newPNTag = word.getPN().replace("group", "organization");
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (Vector<String>) pnValue.clone();
annotationMap.put(newPNTag.toUpperCase(),newPNValue);
}
else{
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (Vector<String>) pnValue.clone();
annotationMap.put(word.getPN().toUpperCase(),newPNValue);
}
}
sentenceAnnotationMap = (LinkedHashMap<String, Vector<String>>) annotationMap.clone();
pnValue.clear();
}
EDITED CODE
Replaced Vector with List and removed cloning. However this still doesn't solve my problem. This takes me back to square one where my output is : {PERSON=[], ORGANIZATION=[]}
for(WSDSentence goldSentence : goldSentences)
{
for (WSDElement word : goldSentence.getWsdElements()){
if (word.getPN()!=null){
if (word.getPN().equals("group")){
String newPNTag = word.getPN().replace("group", "organization");
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (List<String>) pnValue;
annotationMap.put(newPNTag.toUpperCase(),newPNValue);
}
else{
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = pnValue;
annotationMap.put(word.getPN().toUpperCase(),newPNValue);
}
}
sentenceAnnotationMap = annotationMap;
}
pnValue.clear();
You're trying a bunch of stuff without really thinking through the logic behind it. There's no need to clear or clone anything, you just need to manage separate lists for separate keys. Here's the basic process for each new value:
If the map contains our key, get the list and add our value
Otherwise, create a new list, add our value, and add the list to the map
You've left out most of your variable declarations, so I won't try to show you the exact solution, but here's the general formula:
List<String> list = map.get(key); // try to get the list
if (list == null) { // list doesn't exist?
list = new ArrayList<>(); // create an empty list
map.put(key, list); // insert it into the map
}
list.add(value); // update the list
EDIT
I've tried this HashMap with multiple values under the same key, and my hashMap now looks like this HashMap<String, List<Place>> placeMap = new HashMap<>();
Also tried to put Object instead of Place(place is my superclass). But when I now create my subclasses and wants to add them to the HashMap I get:
The method put(String, List) in the type HashMap<String,List<Place>> is not applicable for the arguments (String, NamedPlace)
and
The method put(String, List) in the type HashMap<String,List<Place>> is not applicable for the arguments (String, DescPlace)
here is my adding which created the error:
NamedPlace p = new NamedPlace(x,y,answer,col,cat);
placeMap.put(answer, p);
DescPlace dp = new DescPlace(x,y,answer, desc, col, cat);
mp.add(dp);
placeMap.put(answer, dp);
NamedPlace and DescPlace are both subclasses to Place, and I want them both in the same HashMap..
OP
I'm working on a little project here. The thing is that I need to use a HashMap instead of a ArrayList on this part of the project because HashMap is alot faster for searching. I've created a HashMap like this:
HashMap<String, Object> placeMap = new HashMap<>();
The String is the name of the Object, but the thing is that more than one object can have the same name. So I search for a object in my searchfield and I want to store all those objects that has that name into an ArrayList so I can change info in just them.
The object have alot of different values, like name, position, some booleans etc.
Do I need to create a HashCode method into my object class which shall create a unique hashcode?
When using a standard Map<String, List<YourClassHere>> instance, it is important to remember that the map's values for each entry will be a List<YourClassHere>, and will not handle it in any special way. So in your case, if you have
private Map<String, List<Place>> placeMap = new HashMap<>();
Then to store values you will need to do as follows:
NamedPlace p = new NamedPlace(x,y,answer,col,cat);
List<Place> list = placeMap.get (answer);
list.add(p);
However, this piece of code has some underlying problems.
It doesn't take into account that answer might not be present in placeMap.
It assumes that there's always a List<Place> instance for each key you query.
So the best way to fix those potential problems is to do as follows (Java 7 and later):
NamedPlace p = new NamedPlace(x,y,answer,col,cat);
if (placeMap.containsKey (answer) && placeMap.get (answer) != null) {
placeMap.get (answer).add(p);
} else {
List<Place> list = new ArrayList<Place> (); // ..or whatever List implementation you need
list.add (p);
placeMap.put (answer, list);
}
If you want to scna through the list of places, the code would look like this:
if (placeMap.containsKey (key) && placeMap.get (answer) != null) {
for (Place p: placeMap.get (key)) {
// Do stuff
}
}
Edit: My list is sorted as it is coming from a DB
I have an ArrayList that has objects of class People. People has two properties: ssn and terminationReason. So my list looks like this
ArrayList:
ssn TerminatinoReason
123456789 Reason1
123456789 Reason2
123456789 Reason3
568956899 Reason2
000000001 Reason3
000000001 Reason2
I want to change this list up so that there are no duplicates and termination reasons are seperated by commas.
so above list would become
New ArrayList:
ssn TerminatinoReason
123456789 Reason1, Reason2, Reason3
568956899 Reason2
000000001 Reason3, Reason2
I have something going where I am looping through the original list and matching ssn's but it does not seem to work.
Can someone help?
Code I was using was:
String ssn = "";
Iterator it = results.iterator();
ArrayList newList = new ArrayList();
People ob;
while (it.hasNext())
{
ob = (People) it.next();
if (ssn.equalsIgnoreCase(""))
{
newList.add(ob);
ssn = ob.getSSN();
}
else if (ssn.equalsIgnoreCase(ob.getSSN()))
{
//should I get last object from new list and append this termination reason?
ob.getTerminationReason()
}
}
To me, this seems like a good case to use a Multimap, which would allow storing multiple values for a single key.
The Google Collections has a Multimap implementation.
This may mean that the Person object's ssn and terminationReason fields may have to be taken out to be a key and value, respectively. (And those fields will be assumed to be String.)
Basically, it can be used as follows:
Multimap<String, String> m = HashMultimap.create();
// In reality, the following would probably be iterating over the
// Person objects returned from the database, and calling the
// getSSN and getTerminationReasons methods.
m.put("0000001", "Reason1");
m.put("0000001", "Reason2");
m.put("0000001", "Reason3");
m.put("0000002", "Reason1");
m.put("0000002", "Reason2");
m.put("0000002", "Reason3");
for (String ssn : m.keySet())
{
// For each SSN, the termination reasons can be retrieved.
Collection<String> termReasonsList = m.get(ssn);
// Do something with the list of reasons.
}
If necessary, a comma-separated list of a Collection can be produced:
StringBuilder sb = new StringBuilder();
for (String reason : termReasonsList)
{
sb.append(reason);
sb.append(", ");
}
sb.delete(sb.length() - 2, sb.length());
String commaSepList = sb.toString();
This could once again be set to the terminationReason field.
An alternative, as Jonik mentioned in the comments, is to use the StringUtils.join method from Apache Commons Lang could be used to create a comma-separated list.
It should also be noted that the Multimap doesn't specify whether an implementation should or should not allow duplicate key/value pairs, so one should look at which type of Multimap to use.
In this example, the HashMultimap is a good choice, as it does not allow duplicate key/value pairs. This would automatically eliminate any duplicate reasons given for one specific person.
What you might need is a Hash. HashMap maybe usable.
Override equals() and hashCode() inside your People Class.
Make hashCode return the people (person) SSN. This way you will have all People objects with the same SSN in the same "bucket".
Keep in mind that the Map interface implementation classes use key/value pairs for holding your objects so you will have something like myHashMap.add("ssn",peopleobject);
List<People> newlst = new ArrayList<People>();
People last = null;
for (People p : listFromDB) {
if (last == null || !last.ssn.equals(p.ssn)) {
last = new People();
last.ssn = p.ssn;
last.terminationReason = "";
newlst.add(last);
}
if (last.terminationReason.length() > 0) {
last.terminationReason += ", ";
}
last.terminationReason += p.terminationReason;
}
And you get the aggregated list in newlst.
Update: If you are using MySQL, you can use the GROUP_CONCAT function to extract data in your required format. I don't know whether other DB engines have similar function or not.
Update 2: Removed the unnecessary sorting.
Two possible problems:
This won't work if your list isn't sorted
You aren't doing anything with ob.getTerminationReason(). I think you mean to add it to the previous object.
EDIT: Now that i see you´ve edited your question.
As your list is sorted, (by ssn I presume)
Integer currentSSN = null;
List<People> peoplelist = getSortedList();//gets sorted list from DB.
/*Uses foreach construct instead of iterators*/
for (People person:peopleList){
if (currentSSN != null && people.getSSN().equals(currentSSN)){
//same person
system.out.print(person.getReason()+" ");//writes termination reason
}
else{//person has changed. New row.
currentSSN = person.getSSN();
system.out.println(" ");//new row.
system.out.print(person.getSSN()+ " ");//writes row header.
}
}
If you don´t want to display the contents of your list, you could use it to create a MAP and then use it as shown below.
If your list is not sorted
Maybe you should try a different approach, using a Map. Here, ssn would be the key of the map, and values could be a list of People
Map<Integer,List<People>> mymap = getMap();//loads a Map from input data.
for(Integer ssn:mymap.keyset()){
dorow(ssn,mymap.get(ssn));
}
public void dorow(Integer ssn, List<People> reasons){
system.out.print(ssn+" ");
for (People people:reasons){
system.out.print(people.getTerminationReason()+" ");
}
system.out.println("-----");//row separator.
Last but not least, you should override your hashCode() and equals() method on People class.
for example
public void int hashcode(){
return 3*this.reason.hascode();
}