Passing ArrayList as value only and not reference - java

Simply put, I have a method with an ArrayList parameter. In the method I modify the contents of the ArrayList for purposes relevant only to what is returned by the method. Therefore, I do not want the ArrayList which is being passed as the parameter to be affected at all (i.e. not passed as a reference).
Everything I have tried has failed to achieve the desired effect. What do I need to do so that I can make use of a copy of the ArrayList within the method only, but not have it change the actual variable?

Even if you had a way to pass the array list as a copy and not by reference it would have been only a shallow copy.
I would do something like:
void foo(final ArrayList list) {
ArrayList listCopy = new ArrayList(list);
// Rest of the code
}
And just work on the copied list.

You can create a copy of the ArrayList using ArrayList's copy constructor:
ArrayList copy = new ArrayList(original);
But if the elements of the list are also objects, then you must be aware that modifying a member of the copy will also modify that member in the original.

You could pass Collections#unmodifiableList(yourList) in order to send an unmodifiable copy of your list. By the way, your List<Whatever> is passed by value since Java always pass by value, note that in foo(List<Whatever> list) method you can not modify the list value but you can modify its contents.
public class MyClass {
List<Whatever> list = new ArrayList<Whatever>();
public void bar() {
//filling list...
foo(Collections.unmodifiableList(list));
}
public void foo(List<Whatever> list) {
//do what you want with list except modifying it...
}
}

You could use the .clone method or a CopyOnWriteArrayList to make a copy, thereby not impacting the original.

Try this in you method :
void method(List<Integer> list) {
List copyList = new ArrayList<Integer>();
copyList.addAll(list); // This will create a copy of all the emlements of your original list
}

I'm not sure on why, even after new ArrayList<MyObj>(old) the object was still changing reference in places it wasn't supposed to. So I had to instantiate a new copy of the objects inside.
I made a copy constructor like the one on the ArrayList and did like
newArray = new ArrayList<MyObj>();
for (int i = 0; i < oldArray.size(); i++) {
newArray.add(new MyObj(ondArray.get(i)));
}
Just hope to help someone else if the answer from Avi is not enough in your case, like mine with a code too messy to even understand =P

Just clone it.
public ArrayList cloneArrayList(ArrayList lst){
ArrayList list = new ArrayList();
for (int i=0; i<lst.size(); i++){
list.add(lst.get(i));
}
return list;
}
Add suggested in the comments, you can also use
ArrayList copy = new ArrayList(original);
and also
ArrayList copy = new ArrayList();
copy.addAll(original);

On the lines of the existing answers but using the ArrayList API. You can use subList(fromIndex, toIndex) method. It explicitly creates a view of the list with only desired elements (of course, in sequence). Here, even if you modify the view with add/remove etc operations, it won't change the original list. It saves you from explicitly creating a copy.
Something like this:
public void recursiveMethod(List<Integer> list) {
if(base)
return;
recursiveCall(list);
// following will just create a tail list but will not actually modify the list
recursiveCall(list.subList(1, list.size());
}

Related

Java - How to replace entire 2D ArrayList with another that has different size

I have 2D ArrayList consisting of Objects, i pass it to a function in another class, like this:
public void function(ArrayList<ArrayList<Object>>)..
class.function(array)...
then inside of this function i create another 2D ArrayList (with Object type), but this one has different size and amount of values and values themself are different.
How to "assign" this new ArrayList to the one i passed in to a function, so that the passed one becomes exactly like that new one created in a function? And remains the same outside the function of course.
Btw im doing this inside of a function:
ArrayList<ArrayList<Object>> ArrayNew = new ArrayList<ArrayList<Object>>();
for(int i=0; i < y; i++) {
ArrayList<Object> temp = new ArrayList<Object>();
ArrayNew.add(temp);
}
and then i'm adding new values in some way like this:
ArrayNew.get(some_value_that_changes_and_is_lower_than_y).add(tempObject);
Effect identical to
Array = ArrayNew
In Java, you can't reassign a method parameter and have it also reassign the caller's reference. That is just not possible in Java. You either have to modify the passed-in list or return the new list (which you would assign to the variable you want to replace).
Modify passed-in list:
// function(list);
public void function(List<List<Object>> list) {
int y = list.size();
list.clear();
for (int i = 0; i < y; i++) {
list.add(new ArrayList<>());
}
}
Return the new list:
// list = function(list);
public List<List<Object>> function(List<List<Object>> list) {
List<List<Object>> newList = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
newList.add(new ArrayList<>());
}
return newList;
}
I don't really know what you're trying to do with the list, so I just made something up close to what your example code does.
Couple notes:
You should use standard Java naming conventions, at least when posting to a public forum such as Stack Overflow. Variable names use camelCase (first letter lowercase). Class/interface names use CamelCase (first letter uppercase).
Unless you need something specific to an implementation, typically you want to declare the types of parameters/variables as the interface (List instead of ArrayList, in this case).
You don't need to declare the generic type arguments on both sides of the = operator, at least not since Java 7.
Assuming your current code looks something like this:
public void foo(ArrayList<ArrayList<Object>> array) {
ArrayList<ArrayList<Object>> newArray = new ArrayList<>();
/* fill newArray with data */
array = newArray; // this will have no effect outside this method
}
public void main() {
ArrayList<ArrayList<Object>> mainArray; /* your outer 2D arrayList */
foo(mainArray);
}
To understand why foo does not change the mainArray object, you can think of calling a method as simply copying its code to your method. Like this:
public void main() {
ArrayList<ArrayList<Object>> mainArray; /* your outer 2D arrayList */
// foo(mainArray); gets replaced with
ArrayList<ArrayList<Object>> array = mainArray;
ArrayList<ArrayList<Object>> newArray = new ArrayList<>();
/* fill newArray with data */
array = newArray;
}
Unsurprisingly, the mainArray remains unchanged, and array refers the same instance as newArray.
As Slaw pointed out, you can either edit the instance mainArray refers to, or make mainArray refer to the new instance instead.
But I would suggest first asking yourself: why do want to edit mainArray in the first place?
It sounds like there might be multiple places in your code referencing mainArray, in which case you should consider refactoring your code.
You should also check that you don't have any references to the inner lists of your mainArray, as those will not reflect the changes you make to the outer list.

Assigning List with preserving reference

Suppose I have two Lists and I want to copy/assign all of one list to another list with preserving reference to the original list. I use this code
List<String> mylist = new List<String>();
List<String> another = getSomeList();
// I have to do
mylist.clear();
mylist.addAll(another);
This works fine, but my question is, is there any better way to do this?
Thanks
I don't think that there is an easier way. You can just implement your own list that has e.g. a setAll() method.
class MyArrayList<E> extends ArrayList<E> {
public void setAll(Collection<E> collection) {
clear();
addAll(collection);
}
}
But this only moves the the clear() and addAll() invokation into another method. Sure from a clients prespective it makes the call easier
MyArrayList<String> mylist = new MyArrayList<String>();
mylist.setAll(another);
but at the price that you use a special list implementation. Maybe you only use this implementation inside of a class and your api does not expose that you use a MyArrayList. Than it might be ok. I would just do it the way you already do.
is there any better way to do this?
No, not for your specifications. You can delete mylist = another;.
And there's no need to call mylist.clear() when you just assigned mylist = new List<String>();
So really all you need is that last line where you addAll(another).
You can loop through first and add all elements into another:
for(String s : mylist){
another.add(s);
}

Hashset objects

I'm writing a piece of code which takes a great deal of objects and adds them to another array. The catch is, I don't want any duplicates. Is there a way I could implement a Hashset to solve this problem?
public static Statistic[] combineStatistics(Statistic[] rptData, Statistic[] dbsData) {
HashSet<Statistic> set = new HashSet<Statistic>();
for (int i=0; i<rptData.length; i++) {
set.add(rptData[i]);
}
/*If there's no data in the database, we don't have anything to add to the new array*/
if (dbsData!=null) {
for (int j=0; j<dbsData.length;j++) {
set.add(dbsData[j]);
}
}
Statistic[] total=set.toArray(new Statistic[0]);
for (int workDummy=0; workDummy<total.length; workDummy++) {
System.out.println(total[workDummy].serialName);
}
return total;
}//end combineStatistics()
Properly implement equals(Object obj) and hashCode() on YourObject if you expect value equality instead of reference equality.
Set<YourObject> set = new HashSet<YourObject>(yourCollection);
or
Set<YourObject> set = new HashSet<YourObject>();
set.add(...);
then
YourObject[] array = set.toArray(new YourObject[0])
I think you should pay attention to:
1 - what to do if there is a duplicate in the original Collection? Use the first added to the array? Use the other(s)?
2 - You definitely need to implement equals and hashcode so that you can tell what are duplicate objects
3 - Are you going to create a fixed size array and then won't add anymore objects? Or are you going to keep adding stuff?
You can use any kind of Set actually, but if you use LinkedHashSet, then you will have a defined iteration order (which looks like an array). HashSet wont't garantee any order and TreeSet will try to order data ascending.
Depends on what you are referring to as a duplicate. If you mean an identical object, then you could use a List and simply see if the List contains the object prior to adding it to the list.
Object obj = new Object();
List<Object> list = new ArrayList<Object>();
if (!list.contains(obj)) {
list.add(obj);
}

Setting a list equal to list using equal sign or copy constructor?

This is a simple question but if I do
List<Object> list = getObjectsFromDatabase();
This would not be the correct way to handle this?
But this would?
List<Object> firstList = getObjectsFromDatabase();
List<Object> list = new ArrayList<Object>(firstList);
Or if I had a class
public class ReportDisplayModel<T> {
public ReportDisplayModel(List<T> data) {
this.data = data;
}
public List<T> data;
}
And I wanted to set the data in this model I would use the constructor?
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>(getData());
Instead of
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.data = getData();
Just need a clarification. Thanks.
It depends entirely on what getData() returns.
usually it is made to return Collections.unmodifiableList(result) so that clients can't modify the result.
if this result is not used anywhere else, and modifications to it doesn't mess with anything, it is fine to use the result as-is
It is rarely needed to use the copy constructor - use it when you are sure that modifying the data will impact some other component.
Regarding
List<Object> list = getObjectsFromDatabase();
vs
List<Object> firstList = getObjectsFromDatabase();
List<Object> list = new ArrayList<Object>(firstList);
either approach is fine. Depends on if you want list to refer to the list returned by getObjectsFromDatabase() or if you want it to refer to a copy of it.
If simply want to, say, print the database objects, the first approach is fine.
If you want to, say, filter out half of the database objects (i.e., remove objects from the list), and you can't say for sure that getObjectsFromDatabase() returns a mutable list, then you'll have to go with the second approach.
Regarding
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>(getData());
vs
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.data = getData();
I'd prefer the first method. Simply because I wouldn't want to worry about null pointer exceptions etc if I accidentally do something like
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.printData();
model.data = getData();
I don't quite get your question, but I'll give it a try.
The main difference is that using the copy constructor creates a new independent copy of the list, i.e.
List<Object> firstList = getObjectsFromDatabase(); // firstList is the list returned by the database
List<Object> list = new ArrayList<Object>(firstList); //list is an independent copy of firstList
Now if you change firstList the list returned by getObjectsFromDatabase() would be changed as well (or would throw an exception if changes are not supported). On the other hand list could freely be changed without the original list being affected.
Avoid using the equal sign, because it breaks encapsulation (bad practice). Go for the copy constructor (best practice).

How do I clone a generic List in Java?

I have an ArrayList<String> that I'd like to return a copy of. ArrayList has a clone method which has the following signature:
public Object clone()
After I call this method, how do I cast the returned Object back to ArrayList<String>?
Why would you want to clone? Creating a new list usually makes more sense.
List<String> strs;
...
List<String> newStrs = new ArrayList<>(strs);
Job done.
ArrayList newArrayList = (ArrayList) oldArrayList.clone();
This is the code I use for that:
ArrayList copy = new ArrayList (original.size());
Collections.copy(copy, original);
Hope is usefull for you
With Java 8 it can be cloned with a stream.
import static java.util.stream.Collectors.toList;
...
List<AnObject> clone = myList.stream().collect(toList());
Be advised that Object.clone() has some major problems, and its use is discouraged in most cases. Please see Item 11, from "Effective Java" by Joshua Bloch for a complete answer. I believe you can safely use Object.clone() on primitive type arrays, but apart from that you need to be judicious about properly using and overriding clone. You are probably better off defining a copy constructor or a static factory method that explicitly clones the object according to your semantics.
I think this should do the trick using the Collections API:
Note: the copy method runs in linear time.
//assume oldList exists and has data in it.
List<String> newList = new ArrayList<String>();
Collections.copy(newList, oldList);
I find using addAll works fine.
ArrayList<String> copy = new ArrayList<String>();
copy.addAll(original);
parentheses are used rather than the generics syntax
List<String> shallowClonedList = new ArrayList<>(listOfStrings);
Keep in mind that this is only a shallow not a deep copy, ie. you get a new list, but the entries are the same. This is no problem for simply strings. Get's more tricky when the list entries are objects themself.
If you want this in order to be able to return the List in a getter it would be better to do:
ImmutableList.copyOf(list);
To clone a generic interface like java.util.List you will just need to cast it. here you are an example:
List list = new ArrayList();
List list2 = ((List) ( (ArrayList) list).clone());
It is a bit tricky, but it works, if you are limited to return a List interface, so anyone after you can implement your list whenever he wants.
I know this answer is close to the final answer, but my answer answers how to do all of that while you are working with List -the generic parent- not ArrayList
Be very careful when cloning ArrayLists. Cloning in java is shallow. This means that it will only clone the Arraylist itself and not its members. So if you have an ArrayList X1 and clone it into X2 any change in X2 will also manifest in X1 and vice-versa. When you clone you will only generate a new ArrayList with pointers to the same elements in the original.
This should also work:
ArrayList<String> orig = new ArrayList<String>();
ArrayList<String> copy = (ArrayList<String>) orig.clone()
ArrayList first = new ArrayList ();
ArrayList copy = (ArrayList) first.clone ();
I am not a java professional, but I have the same problem and I tried to solve by this method. (It suppose that T has a copy constructor).
public static <T extends Object> List<T> clone(List<T> list) {
try {
List<T> c = list.getClass().newInstance();
for(T t: list) {
T copy = (T) t.getClass().getDeclaredConstructor(t.getclass()).newInstance(t);
c.add(copy);
}
return c;
} catch(Exception e) {
throw new RuntimeException("List cloning unsupported",e);
}
}

Categories