Remove Object From ArrayList - java

I have one ArrayList and one TreeSet that hold the same object type.
TreeSet<StatusChangeDB> listToChange;
List<StatusChangeDB> originalList;
StatusChangeDB looks like...
public class StatusChangeDB implements Comparable<StatusChangeDB> {
private String sector;
private String superlayer;
private String loclayer;
private String locwire;
private String problem_type;
public StatusChangeDB() {
}
...
...
getters and setters
hashCode()
equals(Object)
compareTo(...)
}
When original list is generated, the Object, StatusChangeDB, has 4 of 5 of its primitives set, i.e. sector, superlayer, loclayer, locwire. The originalList is displayed in a JTable which implements AbstractTableModel.
At some point in the process, the user selects the rows from the displayed table and assigns the "problemType", which creates TreeSet listToChange.
Now I would like to remove the values selected from the JTable once the "problemType" is "set".
To do this, I cannot use a straight
originaList.removeAll(listToChange)
Because the objects differ by "problemType".
I have tried this method in my TableModel
public void removeRow(TreeSet<StatusChangeDB> listToChange) {
//this.wireList.removeAll(statusChangeDBs); Does not work
//because objects are different
for (StatusChangeDB row : listToChange) {
for (StatusChangeDB statusChangeDB : originalList) {
if (statusChangeDB.getSector().equals(row.getSector())
&& statusChangeDB.getSuperlayer().equals(row.getSuperlayer())
&& statusChangeDB.getLoclayer().equals(row.getLoclayer())
&& statusChangeDB.getLocwire().equals(row.getLocwire())) {
System.out.println("####### EQUAL #######");
System.out.println(ro.getSector() + " " + ro.getSuperlayer() + " " + ro.getLoclayer() + " "
+ ro.getLocwire());
this.originalList.remove(statusChangeDB);
}
}
}
This method does the job of finding the values that equal to one another, however the line
this.originalList.remove(statusChangeDB);
produces the error
Exception in thread "AWT-EventQueue-0"
java.lang.UnsupportedOperationException at
java.util.AbstractList.remove(AbstractList.java:161) at
java.util.AbstractList$Itr.remove(AbstractList.java:374) at
java.util.AbstractCollection.remove(AbstractCollection.java:293) at
database.ui.TableModel.removeRow(TableModel.java:95)
I have read the SO forums on this, but I am not able to construct the ArrayList using Arrays.asList because the ArrayList is dymnamically added to.
How can I accomplish this task of removing the Object from the ArrayList?
Please no comments on using DefaultTableModel instead of AbstractTable Model. The "all knowing" leaders of the project have requested the AbstractTableModel.
The code that sets the originalList is as follows:
public void setWireSet(Dataset<StatusChangeDB> wireDF) {
setWireList(wireDF.collectAsList());
updateTable();
}
public void setWireList(List<StatusChangeDB> wireList) {
this.originalList = wireList;
}
Both lists are filled and when dumped to screen have the appropriate values assigned.
The assignment of originalList is if TableModel as
private List<StatusChangeDB> originalList;
private String[] colNames = { "Sector", "SuperLayer", "Layer", "Wire" };
public TableModel() {
this.originalList = new ArrayList<StatusChangeDB>();
}

You are getting this error because you are calling
this.originalList.remove(statusChangeDB);
inside a for-loop which will be using the iterator of that list.
Add the values you want removed to a temporary Arraylist then remove them once the loop completes execution.
Have something like
for (StatusChangeDB row : listToChange) {
ArrayList<StatusChangeDB> tempList = new ArrayList();
for (StatusChangeDB statusChangeDB : originalList) {
if (statusChangeDB.getSector().equals(row.getSector())
&& statusChangeDB.getSuperlayer().equals(row.getSuperlayer())
&& statusChangeDB.getLoclayer().equals(row.getLoclayer())
&& statusChangeDB.getLocwire().equals(row.getLocwire())) {
System.out.println("####### EQUAL #######");
System.out.println(ro.getSector() + " " + ro.getSuperlayer() + " " + ro.getLoclayer() + " "
+ ro.getLocwire());
tempList.add(statusChangeDB);
}
}
this.originalList.removeAll(tempList);
}
Update
Keeping the above implementation
public void setWireList(List<StatusChangeDB> wireList) {
this.originalList = wireList;
}
should be
public void setWireList(List<StatusChangeDB> wireList) {
this.originalList.addAll(wireList);
}
Assuming
public TableModel() {
this.originalList = new ArrayList<StatusChangeDB>();
}
is called before
public void setWireList(List<StatusChangeDB> wireList) {
this.originalList = wireList;
}

Firstly, you are getting UnsupportedOperationException because your List implementation doesn't support removal. To avoid this, just create an ArrayList<>:
List<StatusChangeDB> originalList = new ArrayList<>( Arrays.asList(sourceList) );
Secondly, to remove objects by some set of fields (instead of defining this logic explicitly in your removeRow method), just override equals method in StatusChangeDB which will compare those fields:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StatusChangeDB other = (StatusChangeDB) obj;
return Objects.equals(sector, other.sector) && Objects.equals(superlayer, other.superlayer)
&& Objects.equals(loclayer, other.loclayer) && Objects.equals(locwire, other.locwire);
}

There are two types of array list. A regular one, and Arrays helper class's one, which is immodifiable.
During creation of yuor arraylist from array, instead of doing
this.originalList=Arrays.asList(pArray);
which uses the immodifiable fake ArrayList, do
this.originalList=new ArrayList(Arrays.asList(pArray));
make sure to redo imports, because you may have a namespace class for ArrayList

Related

How to assign a method as an attribute to use it later on any compatible object?

I am trying to create a generic menu over an arbitrary type T implementing a Menuable interface:
public interface Procedure {
void invoke();
}
public class Menu<T extends Menuable> implements Menuable {
List<T> items; //The items of the menu
List<String> itemsDisplay; //A list of String specifying how to display each of the above items
Procedure procedure; //A method to be performed on the elements of items
public Menu(List<T> items, List<String> itemsDisplay) {
this.items = items;
this.itemsDisplay = itemsDisplay;
}
//Returns a String display of the menu
//Each item is numbered and displayed as specified by the list itemsDisplay
public String menuDisplay() {
String s = "";
int i;
for (i = 1; i <= this.itemsDisplay.size(); i++) {
s = s + i + "\t" + this.itemsDisplay.get(i-1) + "\n";
}
return s;
}
...
}
The menu will be printed on a terminal, and when the user chooses an item, I would like the menu to be able to perform any instance method on this item. Basically, here is what I would like to do:
public Menu(List<T> items, List<String> itemsDisplay, Procedure procedure) {
this.items = items;
this.itemsDisplay = itemsDisplay;
Here I would like to assign a method to this.procedure for later use,
but without specifying on which object to use it yet.
}
public void waitForAction() throws IOException {
//Display the menu to the user
System.out.println(this.menuDisplay());
BufferedReader inputs = new BufferedReader(new InputStreamReader(System.in));
int choice = 0;
//Read inputs until the user enters a valid choice
while(!(1 <= choice && choice <= this.items.size())) {
try {
choice = Integer.parseInt(inputs.readLine());
} catch(NumberFormatException e) {
}
}
//The item selected is at index (choice - 1) in the list this.items
Here I would like to use my method on this.items.get(choice - 1).
}
I am new to the idea of using methods as variables / arguments and I don't know how to do that. I heard about lambda expressions and method references in Java, but from what I understood, when using them you have to specify the method you want to use and its instance / parameters at the same time, which is awkward. Am I missing something ?

Duplicate Objects being stored in ArrayList with add method

I have two classes, one named Bird the other Runtime. The bird class creates birds - name, latin name. The Runtime class has 4 methods, only one is important for this question, that is the 'add' method. The add method when called upon needs to take input from the user that is name and latin name, these are saved into a string variable 'name' and 'latin name' and I call the Bird class constructor and pass in these string variables into its parameter and finally it is added to an ArrayList. However I get duplicate values, if I were to write the same bird twice.
I have tried to convert the ArrayList into a set and convert it back again into an ArrayList, i did this within the add method, this did not work. I suspect it is down to my poor understanding of how objects are stored in an ArrayList. I also created a getName method within the Bird class, so I can use list.get(i).getName, and if the name is equal to the one typed by the user, it prompts the user accordingly, if not it is added to my ArrayList. This also did not work. I also tried a for loop that would go through the ArrayList and an if statement would determine if the name typed by the user exists within the ArrayList, this also did not work, the attempt was early on so I can't remember exactly the error message, but the add method is called from within a while loop, and I think the error message was concurrent modification, I'm not entirely sure so please ignore that, my point is showing the various solutions I tried.
Below is The Bird class
public class Bird{
int obeservation = 0;
String name;
String latinName;
public Bird(String name, String latinName){
this.name = name;
this.latinName = latinName;
}
public void addBird(String name, String latinName){
this.name = name;
this.latinName = latinName;
}
public String getName(){
return this.name;
}
public String statistics(){
return this.name + " (" + this.latinName + ") : " +
this.obeservation + " observation";
}
}
Below is the Runtime class
public class Runtime {
ArrayList<Bird> birds = new ArrayList<Bird>();
Scanner scan = new Scanner(System.in);
public void scan() {
System.out.println("?");
String answer = scan.nextLine().trim();
while (!answer.equalsIgnoreCase("EXIT")) {
System.out.println("?");
answer = scan.nextLine().trim().toUpperCase();
if (answer.equalsIgnoreCase("ADD")) {
add();
} else if (answer.equalsIgnoreCase("OBSERVATION")) {
observation();
} else if (answer.equalsIgnoreCase("STATISTICS")) {
System.out.println("jhjh");//just to see if this is
working
statistics();
}
}
}
below is the add method, also what I've commented is the attempts,
currently the add method does not have an if statements to decide duplicates.
public void add() {
System.out.print("Name: ");
String name1 = scan.nextLine().trim().toUpperCase();
System.out.print("Latin Name: ");
String latinName1 = scan.nextLine().trim().toUpperCase();
birds.add(new Bird(name1, latinName1));
/*
Bird newBird = new Bird(name1, latinName1);
for (int i = 0; i < birds.size(); i++) {
if (birds.get(i).getName().equals(name)) {
System.out.println("Bird already exist");
return;
} else {
birds.add(newBird);
}
}
/*
* hBirds.addAll(birds); birds = new ArrayList<Bird>();
birds.addAll(hBirds);
*
* // Bird newBird = new Bird(name, latinName);
* /* if(birds.contains(name)){
* System.out.println("That name already exist");
* return;
* }else{
* birds.add(newBird(name, latinName));
*
* }
*/
}
The statistics method prints out the ArrayList, a foreach loop that goes through the ArrayList prints it out. The expected result if I input seagull twice should be one seagull value not two. How do i reject the duplicate?
You can have two approaches here:
First: Traverse through ArrayList, if you can't find the same bird, add it to ArrayList. It is a worse approach.
Second: Store birds inside HashSet. In this case, you need to override .hashCode() and .equals(Object obj) methods. It is a better approach.
Before talking about how to generate .hashCode() and .equals(Object obj) methods, I want to mention about .hashCode() method and HashSet<T>.
HashSet<T>s provide a unique set of the elements inside. To achieve this, .hashCode() method of a class is used. If you override .hashCode() method in any class, you can get the benefit of using HashSet<T>s. If you don't override this method, Java automatically returns the memory address of the object. That's why your HashSet<Bird> was including duplicate elements.
.hashCode() and .equals() methods can be generated by lots of IDEs. I copied and pasted your Bird class to Eclipse. By using Alt+Shift+S -> h for Eclipse or Alt+Insert -> equals() and hashCode() for IntelliJ, automatically generated the methods below:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((latinName == null) ? 0 : latinName.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + obeservation;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Bird other = (Bird) obj;
if (latinName == null) {
if (other.latinName != null)
return false;
} else if (!latinName.equals(other.latinName))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (obeservation != other.obeservation)
return false;
return true;
}
If you add these methods(I encourage you to generate in your IDE) to Bird class, you can use HashSet<Bird>. To avoid duplicates, simply add all of your Bird objects into defined HashSet<Bird>. You don't need any other data structure or equality check to control if any two Bird type objects are equal.
You will just need to change your object collection from ArrayList<Bird> birds = new ArrayList<Bird>(); to Set<Bird> birds = new HashSet<>();.
Move the add out of the loop:
for (int i = 0; i < birds.size(); i++) {
if (birds.get(i).getName().equals(name1)) {
System.out.println("Bird already exist");
return;
}
}
birds.add(new Bird(name1, latinName1));

Add elements to a HashMap containing an ArrayList

This simple method is supposed to add elements to a HashMap. The elements are read from a txt file, I have tested that and everything regarding that area is working just fine. The issue comes when I print the values that are in the Map.Therefore, I think there might be some issue when adding them.
The content of the txt file is
SW: (scores, 90); (scores, 91); (scores, 92);
Where the Permissions is "SW" and the Attributes are "(scores, 90)... (scores, 92)". These are the elements used in the following methods.
public Map<Permissions, ArrayList<Attributes>> paMap;
public void addMap(Permissions per, Attributes att) {
if(paMap.containsKey(per)) {
paMap.get(per).add(att);
}
else{
ArrayList<Attributes> attList = new ArrayList<Attributes>();
attList.add(att);
paMap.put(per, attList);
}
}
//Attributes contain variables (Scores) and values(90-93), that is just for printing
public void printMap() {
ArrayList<Attributes> list;
for(Permissions p: paMap.keySet()) {
list = paMap.get(p);
System.out.print(p.name + ": ");
for(Attributes l: list) {
System.out.print("<" + l.variable +", " + l.value + ">; ");
}
System.out.println();
}
When I print the Map I expect to get exactly the same thing that is in the txt file, but instead I get
SW: (scores, 92); (scores, 92); (scores, 92)
Even if I remove the paMap.get(per).add(att) line from the addMap method I end up getting SW: (scores, 92);
Also, I have tested if the for loop is being called properly and it is.
Therefore, I am confused on how the attributes on the ArrayList are getting updated and what would be the right way of doing it. Do you see any error in the way I am adding or printing the values?
Thank you for your help
I dont know how looks like read method but seems like You are adding all the time the same attribute, so maybe in read function You are not create new attribute but using the same object all the time and just call sets on this, so in the array lsit at the end is the same object added three times
btw:
also method addmap can be replaced by:
paMap.computeIfAbsent(per,x->new ArrayList<Attributes>()).add(att);
If You are using java8 and above ;)
Couple of suggestions:
use interfaces in the Map declaration:
public Map<Permissions, List<Attributes>> paMap;
in order to be used as key in the Map ensure Permissions is
immutable and has equals and hashCode properly defined
So, assuming Permissions has only 1 String field, it should look something like this:
public final class Permissions {
private final String permission;
public Permissions(String permission) {
this.permission = permission;
}
public String getPermission() {
return this.permission;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Permissions that= (Permissions ) o;
return Objects.equals(this.permission, that.permission);
}
#Override
public int hashCode() {
return Objects.hash(this.permission);
}
}
Try another implementation of printMap method. For example, with Java
8:
private void printMap() {
paMap.entrySet().stream().map(this::format).forEach(System.out::println);
}
private String format(Map.Entry<Permissions, List<Attributes>> entry) {
String key = formatPermissions(entry.getKey());
String value = formatAttributes(entry.getValue());
return String.format("%s: %s", key, value);
}
private String formatPermissions(Permissions permissions) {
return permissions.name;
}
private String formatAttributes(List<Attributes> attributes) {
return attributes.stream()
.map(attr -> String.format("(%s, %s)", attr.variable, attr.value))
.collect(Collectors.joining("; "));
}

Implemented Iterable hands wrong size back

I'm new to this site, so please feel free to correct me if there's anything wrong about my question or the style of the question.
I need to implement the Iterable Interface in my ShareCollection class, so that I can iterate over all the shares in this class. When I'm testing my class with the sample data it always hands back '0' as size, even though there are (in my example) two shares in my collection.
Here's the code of the class + one sample method which hands back an error:
public class ShareCollection implements Iterable<Share>{
private HashSet<Share> shares;
public ShareCollection() {
this.shares = new HashSet<Share>();
}
public ShareCollection(Collection<Share> shares) {
for (Share s : shares) {
HashSet<Share> checkSet = new HashSet<Share>(shares);
checkSet.remove(s);
if (checkSet.contains(s)) {
throw new IllegalArgumentException("There can't be two shares with the same name!");
}
}
this.shares = new HashSet<Share>(shares);
}
public boolean add(Share share) {
if (share == null) {
throw new NullPointerException("share isnt allowed to be null!");
}
return shares.add(share);
}
#Override
public Iterator<Share> iterator() {
return new HashSet<Share>(shares).iterator();
}
}
Here's the main method with the sample data I'm using:
public static void main(String[] args) {
Share s1 = new Share("s1", new ArrayList<>());
Share s2 = new Share("s2", new ArrayList<>());
ShareCollection sc = new ShareCollection()
sc.add(s1);
sc.add(s2);
int counter = 0;
for (Share s : sc) {
counter++;
}
System.out.print("Counter: " + counter + "\n");
System.out.print("Size: " + sc.size());
}
Here's the output for the main-method:
Counter: 2
Size: 0
Here's the error for the 'add'-method:
java.lang.AssertionError: ShareCollection#size should give 1 for a collection with 1 elements.
Expected: <1>
but: was <0>
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at jpp.marketanalysis.tests.data.TestShareCollection.hasElements(TestShareCollection.java:158)
at jpp.marketanalysis.tests.data.TestShareCollection.testAdd(TestShareCollection.java:55)
Thank you in advance for your answers!
Update:
Exchanged the ArrayList with a HashSet (see #SeanPatrickFloyd's first answer)
Possible error: Does your Share class override the .equals() method?
Because ArrayList.contains() delegates to .equals()
Also, I see at least two problems with your code:
An ArrayList is very bad at a .contains() lookup (O(n)). You should use a HashSet instead (in that case you'd need to override both .equals() and .hashCode() in your Share class), it gives you O(1) and handles the .add() method properly for you as well
The Iterator you are returning is the ArrayList's original iterator, which makes your code vulnerable in several ways, including ConcurrentModificationException if you add something while iterating, but also mutation, if someone calls .remove() on the iterator. I'd suggest you make a defensive copy of the collection and use that iterator.
Here's your code rewritten accordingly:
public class ShareCollection implements Iterable<Share>{
private final Set<Share> shares;
public ShareCollection() {
this.shares = new HashSet<>();
}
public ShareCollection(Collection<Share> shares) {
this.shares = new HashSet<>(shares);
}
public boolean add(Share share) {
if (share == null) {
throw new NullPointerException("share isnt allowed to be null!");
}
return shares.add(share);
}
#Override
public Iterator<Share> iterator() {
return new HashSet<>(shares).iterator();
}
}

Can we get the exact location where the condition fails, in an If case having multiple conditions?

I am new to Java,
Here is my code,
if( a.name == b.name
&& a.displayname == b.displayname
&& a.linkname == b.linkname
......... )
return true;
else
return false;
I will call this method and have to check that all properties of objects 'a' and 'b'.
Each object will have more than 20 properties. So, it is will be tidy if i use if case for each property.
An exception is throwed if the return is false and I have to report which property fails.
Is there any easy method to find where the condition fails within the if case.
Pls help. Ask if you are not clear about the question.
The question is, would you like to continue checking if one of the conditions fails?
You could do something like comparator where you have interface:
public interface IComparator {
boolean compare(YourObject o1, YourObject o2);
String getComparatorName();
}
Next you create set of implementations of that interface:
NameComparator implements IComparator {
private name="Name Comparator";
#Override
public boolean compare(YourObject o1, YourObjecto2) {
return o1.getName().equals(o2.getName());
}
#Override
public String getComparatorName() {
return name;
}
}
Next you store set of these comparators in arrayList and you iterate through them and record which one fails by adding them to some other collection.. Hope that helps!
For instance you create array:
IComparator[] comparators = new IComparator[]{ new NameComparator, new DisplayNameComparator};
List<IComparator> failedComparationOperations = new ArrayList<IComparator>();
for(IComparator currentComparator : comparators) {
if(!currentComparator.compare(o1, o2)) {
failedComparationOperations.add(currentComparator);
}
}
for(IComparator currentComparator: failedComparationOperations)
{
System.out.println("Failed Comparation at: "+currentComparator.getComparatorName());
}
You may use reflection: browse what fields are defined, and check each of them using method equals. Print error message if they're not equal, give summary at the end.
boolean equals = true;
Field[] fields = a.getClass().getDeclaredFields();
for (Field f: fields){
f.setAccessible(true);
try {
if (!f.get(a).equals(f.get(b))){
System.out.println(f.getName() + ": " + f.get(a) + "!="+ f.get(b));
equals = false;
};
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("equals?: " + equals);
If you need to know which of the conditions has failed you should check each of the conditions independently.
It might be a little overkill if you are dealing with this single requirement, but what about the Strategy Design Pattern?
http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism
It should be an interesting option if you have other business rules that you can combine with this check.
If a and b are instances of the same class, let's assume A, and the fields are visible, then you can use reflections:
for (Field f : A.class.getFields()) {
try {
if (!f.get(a).equals(f.get(b))) {
throw new RuntimeException("Field " + f.getName() + " is different.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
Without reflection you can't get maximum conciseness, but the followincg can help you to some extent. Make this kind of class:
class NamedEquals {
final String name;
final Object left, right;
NamedCondition(String name, Object left, Object right) { ...assign them... }
boolean areEqual() { return left.equals(right); }
}
Then make a List<NamedEquals>:
List<NamedEquals> conds = Arrays.asList(
new NamedEquals("name", left.name, right.name),
new NamedEquals("displayname", left. displayname, right.displayname),
...
);
And you can find if some of them fail:
for (NamedEquals eq : conds)
if (!eq.areEqual()) throw new ValidationException(eq.name);
Using a factory method can shorten the construction code:
static NamedEquals eq(String name, Object left, Object right) {
return new NamedEquals(name, left, right);
}
With that you can have
List<NamedEquals> conds = Arrays.asList(
eq("name", left.name, right.name),
eq("displayname", left. displayname, right.displayname),
...
);
How about?
// Adapted from your example:
if(!equalTo(a.name, b.name))
fail("name");
if(!equalTo(a.displayname, b.displayname))
fail("displayname");
... etc ...
...
// Allow for null values.
public boolean equalTo(Object a, Object b) {
return a != null ? a.equals(b) : b == null;
}
public void fail(String which) throws SomeException {
throw new SomeException("Failed on '"+which+"'!");
}
Another possible might be to turn each object into a Map<String,?>, perhaps by adding a Map<String,?> toMap() method to the value object, and implementing this by constructing a new map and dumping the value's fields into it. Then you can get the maps and do equals() on them.

Categories

Resources