Deep copy an ArrayList containing objects with ArrayLists in Java - java

I have a problem to deep copy an ArrayList containing Attribute objects. After I have copied the ArrayList dataSet in a new one called trainingSet, I am trying to clear (of the trainingSet) all the content of the internal ArrayList of the Attribute called data. When I do so all the same content of the the ArrayList dataSet (data of dataSet) gets cleared, too. So in that case I have tried to deep copy all the content of the original list to the new one using the below tuts:
http://javarevisited.blogspot.gr/2014/03/how-to-clone-collection-in-java-deep-copy-vs-shallow.html#axzz4ybComIhC
https://beginnersbook.com/2013/12/how-to-clone-an-arraylist-to-another-arraylist/
How to make a deep copy of Java ArrayList
but I got the same behavior. So can someone please tell me how I can fix this problem and where the wrong thinking is?
Thank you for help.
ID3Algorithm.java
...
ArrayList<Attribute> dataSet = new ArrayList<dataSet>();
ArrayList<Attribute> trainingSet = new ArrayList<Attribute>(dataSet);
for(Attribute att : trainingSet) {
att.GetData().clear(); // At this point all the data in dataSet are cleared,too.
}
...
Attribute.java
public class Attribute
{
private String name;
private ArrayList<String> branchNames = new ArrayList<String>();
private ArrayList<String> data = new ArrayList<String>();
private ArrayList<Branch> branches = new ArrayList<Branch>();
private HashMap<String, Integer> classes = new HashMap<String, Integer>();
private ID3Algorithm id3;
private Leaf leaf = null;
public ArrayList<String> GetData() { return data; }
public Attribute(String attribName, ArrayList<String> attribBranchNames, ArrayList<String> attribData, ID3Algorithm algo) {
name = attribName;
branchNames = attribBranchNames;
data = attribData;
id3 = algo;
}
...
}

When you are assigning a value to trainingSet
ArrayList<Attribute> trainingSet = new ArrayList<Attribute>(dataSet);
You are only passing the references for the existing attributes into a new list. It is not a new list of different attribute objects. The first link you post, describes this process in detail. I would re-read it in depth.(The first example is a shallow copy)
http://javarevisited.blogspot.gr/2014/03/how-to-clone-collection-in-java-deep-copy-vs-shallow.html#axzz4ybComIhC
So when you call
att.GetData().clear();
You are clearing the orginal attribute objects data (which dataset also references)
Try creating new Attribute objects and assigning new data to each(copied from the orginal) then adding those to your trainingSet list.

Related

Calling a method on an object that is in an ArrayList

I've been stuck on this question in an assignment in which I must "List the stations along a given subway line"
There are two Hash Maps:
private Map<String, Station> allStations = new HashMap<String, Station>(); // all stations, indexed by station name
private Map<String, SubwayLine> allSubwayLines = new HashMap<String, SubwayLine>(); // all subway lines, indexed by name of the line
I am trying to call the "getStations()" method, which is a part of the subwayLine class:
public List<Station> getStations(){
return Collections.unmodifiableList(stations);
}
On a subwayLine object which is linked to a button:
public void listStationsOnLine(){
UI.clearText();
List<SubwayLine> subwayLines = new ArrayList(allSubwayLines.values());
for(SubwayLine s : subwayLines){
s.getStations();
}
}
However, this does nothing. Is there anyway in which I can return the stations along the given subwayLine?
you have to save your data in an arraylist again :
public void listStationsOnLine(){
UI.clearText();
List<SubwayLine> subwayLines = new ArrayList(allSubwayLines.values());
List<Collection<Station>> stations = new ArrayList();
for(SubwayLine s : subwayLines){
stations.add(s.getStations());
}
}
The void keyword in your method means that it does not return anything.
You might want to replace it with List and return it at the end of your method, like
public List<Station> listStationsOnLine(){
UI.clearText();
List<SubwayLine> subwayLines = new ArrayList(allSubwayLines.values());
List<Collection<Station>> stations = new ArrayList<>();
for(SubwayLine s : subwayLines){
stations.add(s.getStations());
}
return stations
}
Please note that you loop through all subwaylines and therefore your stations arraylist consists of all stations that are along some subwayline. So not a list of stations per subwayline. Also, I added a diamond operator to the arraylist call. That will automatically make an ArrayList of the type Collection<Station>. However, making a List of Collections seems not right here? You probably just want a List of <Station>.
Moreover, the call s.getStations() seems to yield all stations at subwayLine s? The description of your question seems to ask how to implement thát method.
In your question, you seem to prefer a list of stations for a given subwayline. That should then be input for your method, something like this:
public List<Station> listStationsOnLine(SubwayLine subwayline){
UI.clearText();
List<Station> allStations = getStations();
List<Station> stations = new ArrayList<>();
for(Station s : allStations){
if(s.onLine(subwayline)) {
stations.add(s);
}
}
return stations
}

Updating one list also updating referenced list in Java [duplicate]

This question already has answers here:
I have two lists containing the same objects. How do I change one list without changing the other? [duplicate]
(2 answers)
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 2 years ago.
I am facing strange problem (At least awkward for me). I have a list of custom objects. I am adding this list of custom objects 2 other ArrayLists. Here is the problem, when I update one of the list (a property of any custom object), it updates the object of same location in other list. Below is the code
Below is the custom class for example:
public class TestClass {
String name;
}
Here is how I am creating data set:
TestClass testClass1 = new TestClass();
testClass1.name= "first";
TestClass testClass2 = new TestClass();
testClass2.name= "second";
List<TestClass> data = new ArrayList<>();
data.add(testClass1);
data.add(testClass2);
Here is how I am adding data set in other 2 Lists:
List<TestClass> testListFirst = new ArrayList<>();
testListFirst.addAll(data);
List<TestClass> testListSecond = new ArrayList<>();
testListSecond.addAll(data);
Here is the problem when I update an element of one list it gets updated in second list as well:
testListFirst.get(0).name = "third";
If I check testListFirst it is updated with new value, but testListSecond is also updated. My expectation was testListSecond It should not get updated because they both list are different object in memory pointing different objects. If I update one other should not be updated. Please correct me if I am wrong. Any help is highly appreciated.
The problem here is you are creating two separate lists, it's true. But the objects which are inside the lists are the same. So, once you do some change by retrieving an object from any list, it will update the state of the same object.
Sample Solutions:
Method 1:[Recommended]
List<TestClass> clonedist == new ArrayList<>(); //To store the clones
for (TestClass temp : originalList){ //Looping through the original list and cloning the each element
clonedList.add(new TestClass(temp.getName()));//Creating a new TestClass object and adding to the list.Name will be set to the object through it's overloaded constructor.
}
Note:Here new TestClass object will be created through the overloaded constructor of the TestClass. To be brief I didn't include the codes regarding the updated TestClass.But you can still create the new object and update it's state through it's relevant setter methods or directly calling the attributes names(Like in your code snippet if the access modifier of the attribute allows).
Method 2:[There might be some issues with the clone() method ]
More Details regarding the clone() method :Should I use Clone method in java?
List<TestClass> clonedist == new ArrayList<>(); //To store the clones
for (TestClass temp : originalList){ //Looping through the original list and cloning the each element
clonedList.add(temp.clone());//cloning the object and adding to the list
}
Instead of changing fields on an existing object it is best to simply provide a new object. Then you will avoid the problem you are experiencing. I modified your class with a constructor to take a name.
TestClass testClass1 = new TestClass("first");
TestClass testClass2 = new TestClass("second");
List<TestClass> data = new ArrayList<>();
data.add(testClass1);
data.add(testClass2);
System.out.println("data = " + data);
List<TestClass> testListFirst = new ArrayList<>();
testListFirst.addAll(data);
List<TestClass> testListSecond = new ArrayList<>();
testListSecond.addAll(data);
System.out.println("Before adding 'third`");
System.out.println("testListFirst = " + testListFirst);
System.out.println("testListSecond = " + testListSecond);
testListFirst.set(0, new TestClass("third"));
System.out.println("after adding 'third'");
System.out.println("testListFirst = " + testListFirst);
System.out.println("testListSecond = " + testListSecond);
Prints
data = [first, second]
Before adding 'third`
testListFirst = [first, second]
testListSecond = [first, second]
after adding 'third'
testListFirst = [third, second]
testListSecond = [first, second]
Your modified class
class TestClass {
public String name;
public TestClass(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
The lists are different objects, but the elements in the list are the same. You have to make a copy of each element for each list.

Access Values from an ArrayList That is inside another ArrayList

java.util.List records = new java.util.ArrayList();
java.sql.ResultSet rs = selectestatement.executeQuery(query1);
while (rs.next()) {
java.util.List record = new java.util.ArrayList();
record.add(rs.getString("WHLO").trim());
record.add("888509018579");
record.add(rs.getString("ITEM_CODE").trim());
record.add(rs.getString("ARRIVAL_DATE").trim());
record.add(rs.getString("PAIRS_PER_CASE").trim());
record.add(rs.getString("ATS").trim());
records.add(record);
}
In this code, Final arraylist is the "records array". This records arraylist contents few record arrays.
How can i access the 1st element of record arraylist from the records arraylist?
Don't use raw types:
List<List<String>> records = new ArrayList<>();
List<String> record = new ArrayList<>();
...
records.add(record);
This way records.get(i) will return a List<String> instead of an Object, so you can access the elements of the inner List:
String first = records.get(0).get(0);
What you really want is a class containing your row data.
class RecordData {
public String whlo;
public long someNumber = 888509018579;
public String itemCode;
public String arrivalDate;
public String pairsPerCase;
public String ats;
}
and then do
java.util.List<RecordData> records = new java.util.ArrayList<>();
while (rs.next()) {
RecordData record = new RecordData();
record.whlo = rs.getString("WHLO").trim();
record.itemCode = rs.getString("ITEM_CODE").trim();
record.arrivalDate = rs.getString("ARRIVAL_DATE").trim();
record.pairsPerCase = rs.getString("PAIRS_PER_CASE").trim();
record.ats = rs.getString("ATS").trim();
records.add(record);
}
In fact, you want to make the members private and accessible via getters and setters, and use LocalDate for the arrivalDate and int for the pairsPerCase member, but the first point is not using a List to store the retrieved values but wrap it in a business-oriented class.
You can do something like this
((ArrayList)records.get(0)).get(0) to access the first element of the array list that is in the first position of the records array list.
Please note that if you specify what does the records contains (in this case records will contains array lists) then you won't need to cast the element to array list.
List<List<String>> records = new ArrayList<ArrayList>();
{...}
records.get(0).get(0); //You don't need the cast because Java already knows that what it is inside records are Lists

Creating an independent copy of the entity

I'm creating a new list. I write objects to the new list from the list of entities. Then I will clean items from the list of entities appropriately, which also results in the removal of objects from this new list.
final ContributionEntity contributionEntity = this.findContribution(contributionId, DataStatus.WAITING, user, MovieField.PHOTO);
final Set<Long> idsToAddBeforeCleanUp = contributionEntity.getIdsToAdd();
this.cleanUpIdsToAdd(contributionEntity.getIdsToAdd(), contribution.getIdsToAdd(), contributionEntity.getMovie().getPhotos());
private void cleanUpIdsToAdd(final Set<Long> idsToAddFromEntity, final Set<Long> idsToAddFromDto,
final List<? extends MovieInfoEntity> entities) {
for (final Iterator<Long> it = idsToAddFromEntity.iterator(); it.hasNext(); ) {
final Long id = it.next();
if (!idsToAddFromDto.contains(id)) {
it.remove();
this.delete(entities, id);
}
}
}
This code removes the entity from the list of subject photos contributionEntity , but also removes objects from the list idsToAddBeforeCleanUp.
How do I copy a list from an entity and make it independent of this entity? I do not want to delete items from the list idsToAddBeforeCleanUp.
final Set<Long> idsToAddBeforeCleanUp = new HashSet<>();
contributionEntity.getIdsToAdd().foreach(item -> idsToAddBeforeCleanUp.add(item));
You didn't really create a new set, you just made another reference to the set. With the code above you make a new list and should it work.

Hashmap holding a hashset - how to retrieve data

How do I obtain a list of all the objects that have the same value for a specific attribute out of HashMap<String,HashSet<String>> objects the String holds the attributes and the HashSet holds the list of values for the attributes!
Map<String,Set<String>> objects = new HashMap<String,Set<String>>();
// fill it up
String needle = "value";
List<String> results = new LinkedList<String>();
for(Map.Entry<String,Set<String>> entry : objects.entry set())
{
if(entry.getValue().contains(needle))
{
results.add(entry.getKey());
}
}
return results;

Categories

Resources