I'm looking to improve my Java coding.
Can someone provide a link or an explanation on whether there's a code of practise to initialise ArrayLists and avoid the following problem:
-I have 6 ArrayList in one class, some are subsets of others. Because some are subsets of others I understand they share the same references through the "addAll()" and "add()" methods.
As a result, by attempting to change the elements in subsets I'm also altering the original sets because again - they share the same reference. My code is so messy that a few "get" calls results in 2 of my ArrayLists being reset.
I have researched this forum and google and I can't seem to find the relevant information I want. I only find simple examples of ArrayLists. I have noticed that there are a few ArrayList reference related questions on this forum so I think the answer to this question will benefit others in the future.
Can someone provide a link or an explanation on whether there's a code of practise to initialise ArrayLists and avoid the following problem:
There is no such a code of practice, or best practice or whatever on initializing ArrayLists.
The problem is basically that you need to understand the difference in Java between using a reference to an existing object, and creating a new object. And you need to then choose the appropriate one ... depending on what you are trying to do.
(Asking for "best practice" on this topic is like asking for "best practice" on whether you should use + or - operators ...)
Rather than Googling for "best practice" on this, I suggest you go back to your Java text book / tutorial / lecture notes and read up on:
what Java object references are
what object assignment means, and
what the new operator does.
And make sure that you really understand them. When you understand them you will be able to understand which to use to do what you are trying to do in your program.
addAll adds references to the objects in the ArrayList, not copies from objects. Id you want copies, you must iterate through the 1st ArrayList, and for every Object, call the "clone" function that will create a copy of the object, and add it to the new ArrayList.
Example:
public static void main(String[] args) {
ArrayList<Foo> A = new ArrayList<Foo>();
A.add(new Foo("foo1", 1));
A.add(new Foo("foo2", 2));
ArrayList<Foo> B = new ArrayList<Foo>();
System.out.println("Before: ");
System.out.println("A:");
for(Foo foo:A){
System.out.println(foo);
try {
B.add((Foo)foo.clone());
} catch (CloneNotSupportedException ex) {
//Never Gonna Happen
}
}
System.out.println("B:");
for(Foo foo:B){
System.out.println(foo);
}
A.remove(0);
System.out.println("After:");
System.out.println("A:");
for(Foo foo:A){
System.out.println(foo);
}
System.out.println("B:");
for(Foo foo:B){
System.out.println(foo);
}
}
public static class Foo{
private String Name;
private int Id;
public Foo(String Name, int Id) {
this.Name = Name;
this.Id = Id;
}
#Override
protected Object clone() throws CloneNotSupportedException {
return new Foo(Name,Id);
}
#Override
public String toString() {
return "Name: "+Name+", Id: "+Id;
}
}
This prints out:
Before:
A:
Name: foo1, Id: 1
Name: foo2, Id: 2
B:
Name: foo1, Id: 1
Name: foo2, Id: 2
After:
A:
Name: foo2, Id: 2
B:
Name: foo1, Id: 1
Name: foo2, Id: 2
If you deal with ArrayLists of classes you created, make sure they all have "clone" function overridden. For classes containing classes you also created, use the "clone" functions you created for the inner classes in the parent class "clone". And so on.
Related
I created a class and made 57 objects from it, each one has specific ID number.
Can I create a method which returns an object using an ID as the argument?
For example, assume the name of my class is Things and I made two object from it called apple and dog, they have IDs 1 and 2.
Things.java:
class Things {
private String name;
private int ID;
public Things(String name, int ID) {
this.name = name;
this.ID = ID;
}
}
Main.java:
class Main {
public static void main(String[] args) {
Things apple = new Things("apple", 1);
Things dog = new Things("dog", 2);
}
}
in this example I want to create a method in class "Things" which returns object apple if I use 1 as argument and object dog if I use 2 .
You cannot identify objects by a particular property unless you store it in a special repository
You can create a ThingRepository and can get specific Things by the id.
public class ThingRepository {
private Map<Integer, Things> thingsRepository = new HashMap<>();
public void addThing(int id, Things things) {
thingsRepository.put(id, things);
}
public Things getThingById(int id) {
return thingsRepository.get(id); //Can return null if not present
}
}
The addThing method need not explicitly take the id. If you add a getter to Things, then it can be simplified to
public void addThing(Things things) {
thingsRepository.put(things.getId(), things);
}
Couple of problems you need to address:
Each created Things object has to be added to this somehow (either the caller needs to add or there must be some other wrapper/factory that must do this).
Once a Things is not needed, it must be removed from the above map, else it can lead to memory leak.
Btw, shouldn't Things be named as just a Thing?
There are two aspects here:
you need some sort of data structure that remembers about created objects, and allows you to access them by id, for example a simple Map<Integer, Things>. Each time you create a new Things (should better be called Thing, shouldn't it?!), you go thatMap.put(newId, newThing).
if you want that data to "survive", you would have to somehow persist it (like writing data to a file, database, ...)
If you use Intellij for example press: alt + insert and choose getters/setter.
If not just write your own getters/setter ;).
Like here: https://docs.oracle.com/javaee/6/tutorial/doc/gjbbp.html
But basically if you want to look for Thing with particular Id you need to store somewhere them for example in ArrayList, then iterate through it and if your find element with that Id just return it.
1) Create new ArrayList
2) Iterate through
3) If you find Thing with Id you want, return it.
My question which of the following examples represents the right practice ?
What are the advantages and downsides of these approaches.
Is there another(right) way to achieve this?
Let's say I have class
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String print() {
return this.name;
}
}
And use of class like:
public static void main(String[] args) {
List<Person> people = new ArrayList<Person>();
people.add(new Person("Jane Doe"));
System.out.println(people.get(0).print());
}
And then another way to do this:
public class Persons {
private ArrayList<String> persons;
public Persons() {
persons = new ArrayList<String>();
}
public void putPerson(String name) {
persons.add(name);
}
public String print(int id) {
return this.persons.get(id);
}
}
And use of that:
public static void main(String[] args) {
Persons persons = new Persons();
persons.putPerson("John Doe");
System.out.println(persons.print(0));
}
EDIT:
Assume I have 10 000 of these persons.
Is there any downside to creating 10,000 instances of the class?
I would prefer the first way because there is a rare need to create a wrapper class for keeping a collection (like Persons -> Person). It makes sense if the class Persons gets renamed to a Company/Community, which contains a List of workers/persons and provides specific operations over this list.
public String showAllMembers() { ... }
public void notifyAllMembers() { ... }
Also, the second way breaks the Single responsibility principle. It shouldn't take care about printing a person, the class is responsible for adding/removing them. The Persons can provide to you a specific Person, then you have to call a method print() on a given instance:
Persons persons = ...;
persons.getMember(10).print();
Lets say i have 10 000 of these persons. Is there any downside to create 10 000 instance of class?
In any case, you will have to create 10000+ instances. Consider,
10000 Persons + a List
10000 Persons + a Persons + a List
The first one is more object-oriented than the second, which becomes apparent as soon as you start adding more properties to a person.
For example, consider adding a date of birth to a person. When you have class Person, you modify the class, and everyone who has access to it will be able to get it. You will also be passing the date of birth with the Person object, so any method that takes Person as a parameter will have access to that person's date of birth:
static void displayPerson(Person p) {
// Here, we can print both the name and the date of birth / age
}
public static void main(String[] args) {
...
displayPerson(people.get(0));
}
The second approach would require adding a parallel collection to the Persons class. All users of Persons would get access to date of birth, but unless a method takes the full collection as a parameter, it would have access to only the properties that the caller takes from the collection:
void displayPerson(String name) {
// Here, we have no access to person's date of birth
}
public static void main(String[] args) {
...
displayPerson(persons.print(0));
}
In Java, the first approach is the one to go with. It is more in accordance with the OOP way of doing things.
While there is nothing structurally wrong with the second, it doesn't follow the OOP architecture. You may do this kind of operation in C.
In the first approach, you create a Person class which defines what a person is - its properties, methods, etc. This is the object in Object oriented programming. When you need a Person, you then instantiate one and add it to a list of people.
In the second, you create a array, essentially. You can then create an instance of it and fill in the properties you want. However, I see the following drawbacks for this approach:
The object you're adding to the class doesn't exist anymore. Nowhere have you defined what a person is and what properties it has. These properties only exist in the values you add to the array. This can get very confusing and risky after a while.
No getters and setters. Every operation to retrieve a specific property and update it will result in very complex and redundant iterations on your array. In addition, your class can potentially have a very large number of properties, and every time you want to update a property, you'll have to be very careful to get the right index of that property in the array. It's a recipe for disaster.
Also, a putPerson method which just adds a value to the array? Cringy.
Short answer: don't do the second way, in Java, ever.
In my opinion example A is much easier to understand than B hence I would say code readability and maintainability wise it is better.
Reason is that for example B the persons structure actually involves composition of another structure ArrayList to accomplish generics which in example A, you have achieve it by declaring List<Person>.
Example B is also less flexible than A, because it is always a collection whereas example A you can simply use it as a Person and just plug it into different data structures available in Java. Example if you want to keep track of unique person, you can easily come out with a map that is keyed by <name, Person>, whereas example B would not be as clean because you will be doing something like <name, Persons> where each persons would only contain 1 person. Hence extensiblity wise may not be as great as the other one.
Also to me, A is more of the traditional way of doing object oriented because you are representing a real world object as a class whereas B is really just a wrapper of an object.
Obviously there can be a lot of arguments to this, but I'm happy with others input and critic about this answer.
This question already has answers here:
Copy of arraylist keeps getting modified to the values of the original
(4 answers)
Closed 6 years ago.
I am trying to copy the contents of an ArrayList to another and change the contents of copy. I don't want this to be reflected in the original.
I checked on SO and made changes accordingly, still the same issue. Can someone help? I am sharing the code below:
private ArrayList<CustVoiceListObject> FilterApprovedWorkFromList() {
ArrayList<CustVoiceListObject> arrayListCopy = new ArrayList<>(arrayListCustVoice);
for(int i =0; i<arrayListCopy.get(position).getPackageArray().size();i++)
{
if(!arrayListCopy.get(position).getPackageArray().get(i).getPackageApproved().equals("Y"))
{
arrayListCopy.get(position).getPackageArray().remove(i);
i--;
}
}
return arrayListCopy;
}
While debugging, when it is about to return, I check the original arraylist arrayListCustVoice but this is also modified similar arrayListCopy
What am I missing?
UPDATE [Following the suggestions][This questions hence is not duplicate!]
This is my modified code:
private ArrayList<CustVoiceListObject> FilterApprovedWorkFromList() {
ArrayList<CustVoiceListObject> arrayListCopy = (ArrayList<CustVoiceListObject>) arrayListCustVoice.clone();
for(int i =0; i<arrayListCopy.get(position).getPackageArray().size();i++)
{
if(!arrayListCopy.get(position).getPackageArray().get(i).getPackageApproved().equals("Y"))
{
arrayListCopy.get(position).getPackageArray().remove(i);
i--;
}
}
return arrayListCopy;
}
In fact I have implemented Cloneable to my original class, still I am facing the same problem.
Update 2 [Research Conclusion]
I came across this link
In my case, there are 2 classes. The 2nd is subset of first. Pasting below:
public class CustVoiceListObject implements Cloneable {
private String txtSource, txtCustComment, txtCustOk, txtRepeat;
private int numberOfPackages, complaintSerial;
private ArrayList<CustomerVoicePackageListObject> packageArray;
private Double totalAmount;
//getters & setters & constructors
#Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Class 2:
public class CustomerVoicePackageListObject implements Cloneable {
public String packageCategory;
public String packageName;
public String partUsageFlag;
public String laborUsageFlag;
public String status;
public String isApproved;
//getters & setters & constructors
#Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
The .clone() must link to the clone() of the CLASS, not elsewhere. And if it correctly does, it will provoke for taking measure to address the exception as per my implementing the clone() in each individual class.
So this is what I did, modified that for loop to this:
private CustVoiceListObject FilterApprovedWorkFromList() {
//Observe the change here. It's no more ArrayList, it's Class type
CustVoiceListObject arrayListCopy = null;
try {
arrayListCopy = (CustVoiceListObject) arrayListCustVoice.get(position).clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
for(int i =0; i<arrayListCopy.getPackageArray().size();i++)
{
if(!arrayListCopy.getPackageArray().get(i).getPackageApproved().equals("Y"))
{
arrayListCopy.getPackageArray().remove(i); //this is ArrayList<ClassType>. Nested class objects.
arrayListCopy.setTxtCustOk("OKOK"); //within the class
i--;
}
}
return arrayListCopy;
}
The result was the changes within the packageArray reflected in both (failure) BUT, changes in txtCustOk within the basic class, changed in copy, not in original (success). That means, problem is in cloning with ArrayList
So deep cloning requires satisfaction of following rules:
1.No need to separately copy primitives.
2.All the member classes in original class should support cloning and in clone method of original class in context should call
super.clone() on all member classes.
3.If any member class does not support cloning then in clone method, one must create a new instance of that member class and copy all its
attributes one by one to new member class object. This new member
class object will be set in cloned object.
So my aim is to get rid of ArrayList and shift those elements to this class. This is painful. Looking for an easy alternative.
The problem is that your elements of the original ArrayList are reference values so you just copy the references to those objects but not the objects themselves (which seem to be another kind of arrays).
Have a look at this question which essentially deals with the same kind of problem.
The copy operation copied the original list - but not the elements of the list.
That is, if oldList had the following Person objects:
oldList: John, Jane, Jude, Joe
And you copied oldList to newList:
oldList: John, Jane, Jude, Joe
newList: John, Jane, Jude, Joe
And then deleted John from newList:
oldList: John, Jane, Jude, Joe
newList: Jane, Jude, Joe
you can see that they are two distinct lists. But you're not changing the lists, you're changing the objects inside the list. If Joe's name got changed to Jim, you'd have:
oldList: John, Jane, Jude, Jim
newList: Jane, Jude, Jim
You have only done what is called a "shallow" copy. You want to do a "deep" copy, as shown here.
Edit (to "Update 2" in question)
Your research didn't go far enough, I'm afraid. In your (new) Class 1:
public class CustVoiceListObject implements Cloneable {
// ints, Doubles, Strings
private ArrayList<CustomerVoicePackageListObject> packageArray;
//getters & setters & constructors
#Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
you simply returned super.clone() - letting Java do all the work for you. This basically does the shallow copy that's causing all your problems! You might as well not have made the object ICloneable. What you missed was that ints, Doubles and even Strings already copy successfully, so you don't need to clone them. But ArrayLists don't clone, so you need to help it along:
#Override
protected Object clone() throws CloneNotSupportedException {
// First copy the easy stuff
CustomerVoiceListObject cvlo = (CustomerVoiceListObject)super.clone();
cvlo.packageArray = new ArrayList<CustomerVoicePackageListObject>(packageArray.size()); // Make sure it's the right size
for (CustomerVoicePackageListObject cvplo: packageArray) {
cvlo.packageArray.add(cvplo.clone());
} // for
return cvlo;
} // clone()
As far as I know and researched, arrays in Java are not objects but they're reference-types. My doubt is, when I want to return an array should I return a deep copy (like you would do with an object) with a clone() method for example, or can I return the variable countaining the array like it was a simple-type variable (ie. int or double) with a get method? For clarification porpuses, I will insert an example code to expose this situation:
public class List
{
// Instance Variables ----------
private int[] list1;
private int[] list2;
// Constructors ----------
public List()
{
list1 = new int[0]; list2 = new int[0];
}
public List(List x)
{
list1 = x.getList1();
list2 = x.getList2();
}
// Get methods
public int[] getList1()
{
return list1;
}
public int[] getList2()
{
return list2;
}
// Set methods
public void setList1(int size)
{
list1 = new int[size];
}
public void setList2(int size)
{
list2 = new int[size];
}
// Compare reference between an array and the instance variables
public boolean equals (int[] x)
{
if ( x == list1 || x == list2)
return true;
else
return false;
}
}
And now I have a TestClass the uses class List like this:
List listx = new List();
int[] listy = listx.getList2();
boolean test = listx.equals(listy);
System.out.printf("Result: " + test );
With this said, when I use the method equals to see if the two arrays share the same reference or adress, I get always the result true!! Am I breaking OOP basic principals with this? Will I loose control because listy is pointing to listx instance variable?
Well, I'm really confused with this and I don't know if this is right(being array a non-instantiable class) or If I should send some kind of deepcopy insted of shallow using a Clone method in other to ensure that all basic OOP principals are fulfilled, and with this principals I mean that the class method should be acessed only by the API and that the internal state(instance variables) can only be acessed by the class itself.
You are not breaking OOP principals. However, you are breaking principals of functional programming. Functional programming views leaking of access as losing of control.
Whether or not you want to practice functional programming is up to you, Java doesn't take a stance in that matter.
You may want to consider if it's important not to leak access for this particular class. If you find it important not to leak access then make this class immutable.
You can also guard the instance variables. In this scenario any possible changes to the variables must be handled by the instance class. However, the instance could be modified from separate contexts and result in loss of control. For this reason functional programming only allows immutable classes.
If you want the invoker of the method to be able to modify the original array, you don't need to do a copy. Otherwise, you do.
Check your implementation of equals(). It should be reflexive, symmetric, and transitive, which is not the case on yours.
It depends on your use-case if you want to deep copy or not. If your elements are immutable you normally not need to do a deep copy. If they can change, it depends if you want to see the changes in your receiver of the copy or not. Typically when you want a snapshot of the given data you will have to deep copy it. However keep in mind that Arrays are most of the time not a good argument or return type for APIs anyway.
I am totally confused with ArrayList behavior. Wrote really long post, then realized no one is going to analyse huge code, so just core of the problem. Numbers are for convenience, but in my app these 0 and 24 are dynamic values.
ArrayList<VoipBlock> sortedBlocks = new ArrayList<VoipBlock>();
VoipBlock vb3 =new VoipBlock();
vb3=sortedBlocks.get(0);
vb3.setPacketNumber(24);
Essentially my final aim is to: modify and add back to arrayList as new value. However when I do that the guy at position 0 in ArrayList -> unsortedBlocks.get(0); replicates all the changes done to vb3 which of course is not what I want. I want vb3 acquire same values as VoipBlock inside of ArrayList, but I want it to be detached.
This is yet another case of passing by reference. I hate technical explanations - Java passes everything by value, BUT in some cases it passes references by values - this is same as saying not-oily oil. Please help.
It reminds me my start of learning JavaScript - I hated the language - until I watched proper materials at lynda.com - JavaScript Good Practices? - Diagrams killed me. It is the lazy description that turns us-youth away from brilliant technology, not the technology itself.
Please don't let it bother my stress and don't be in any way offended by me, it is just general complaining, maybe someone will look at it and make life better :-)
Thanks for Your time,
Desperately awaiting for help :-)
To achieve your objective you can use clone method. you have to override this method in VoipBlock class
Lets say VoipBlock is as follows
public class VoipBlock {
private int packetNumber;
private String type;
public int getPacketNumber() {
return packetNumber;
}
public String getType() {
return type;
}
public void setPacketNumber(int value) {
packetNumber = value;
}
public void setType(String value) {
type = value
}
public VoipBlock clone() {
VoipBlock clone = VoipBlock();
clone.setType(this.getType());
clone.setPacketNumber(this.getPacketNumber());
return clone;
}
}
So, using the same code you can do like as follows
ArrayList<VoipBlock> sortedBlocks = new ArrayList<VoipBlock>();
VoipBlock vb3 =new VoipBlock();
sortedBlocks.add(vb3);
vb3=sortedBlocks.get(0).clone();
vb3.setPacketNumber(24);
Note that upon calling clone method in above code segment, vb3 get assigned with a new VoipBlock instance. And already inserted VoipBlock to the array remains unchanged.
if you are looking to have kind of sample instances of VoipBlock instances which you later wanted to use in creating similar instances like them. check on immutability/mutability aspect of the code. check "Effective Java" by Joshua Blouch
The following will always copy the reference of b to a:
AnyClass a = ...;
AnyClass b = ...;
a = b;
What you want is probably to clone the object:
a = b.clone();
If I understand correctly, you're a bit unsure about how references and values work. I think the rule of thumb is that primitive types like int, char, boolean and maybe String are copied but Objects just have their reference passed.
The line vb3=sortedBlocks.get(0); completely replaces whatever vb3 used to be with the first thing in the ArrayList. And yes, it won't be a copy, it will be a reference to the same object in memory. So whatever you do will affect both of them. You need to either manually copy over all the information you need or to use a clone() or copy() function.
So for example, in your code, the line VoipBlock vb3 =new VoipBlock(); is a bit redundant because you're overwriting the new instance straight away.
What you really need here is to either use a copy constructor or declare VoipBlock to be Clonable so you can use the clone() method.
What you are interpreting as passing by reference is not actually passing by reference. Java objects are really pointers. Because of this you are passing the value of the pointer. So when you do:
vb3=sortedBlocks.get(0);
you are really assigning vb3 to point to the same locations in memory as sortedBlocks.get(0). Therefore when you manipulate vb3 properties through their setters, the result is seen in both.
If you want two separate pointers you need to use the new keyword or use the clone() method which does this under the hood.
An example to prove this is:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class Main {
public void doSomething(Person p) {
p = new Person("Bob");
System.out.println(p.getName());
}
public static void main(String[] args) {
Person p = new Person("Billy");
System.out.println(p.getName());
doSomething(p);
System.out.println(p.getName());
}
}
Since Java is pass by value the output will be: Billy, Bob, Billy. If Java were pass by reference it would be Billy, Bob, Bob. If I did not do the new Person in the doSomething() method and instead used the setName() method I would end up with Billy, Bob, Bob also but this is due to the fact I'm now modifying off the same pointer not that I passed by reference as the example above proves that's not the case.