List value gets Modify in java - java

I have Three List and in the third list i'm merging two list. if i make changes in third why the change gets reflect to other list and how to overcome?
List<Communication> communicationFromList = fromTicket.getCommunications();
List<Communication> communicationToList = toTicket.getCommunications();
List<Communication> mergedCommunication=new ArrayList<>();
mergedCommunication.addAll(communicationToList);
mergedCommunication.addAll(communicationFromList);
for (int i = index; i < mergedCommunication.size(); i++) {
if (!ObjectUtils.isEmpty(mergedCommunication.get(i))) {
int j =i;
Communication communication = mergedCommunication.get(i);
communication.setCommSeqId(++j);
communication.setMergeInfo("Merged From: " + fromTicketId);
}
}
Due Above changes gets reflect over to other list also.how to overcome

So, in java objects are passed over by reference. In this case when you did addAll on mergedCommunication it added reference of all objects from both lists i.e. communicationToList and communicationFromListto mergedCommunication. Hence, the Communication objects in mergedCommunication are the same objects as in other two lists.
**Suggestion:**If you don't want to modify original objects you can do cloning.

Related

Java List being modified from somewhere despite being of Collections.unmodifiableList()

I have three lists in my program.
ONE A global list of all items. ALL
TWO A list showing all items that are available to the user. VISIBLE
THREE A list of all items currently visible on the screen. SHOWING
The lists were declared as so:
ONE = external source
TWO = ONE
THREE = ONE
I have this issue when responding to the user filtering specific items. The function will iterate over THREE and List.remove(index) items that match the filtered item. The function does not interact with TWO. Despite this TWO is modified to be equivalent to THREE despite the initial declarations not being run again (I checked with the debugger and can't work out when TWO is ever modified).
I tried changing TWO to be a Collections.unmodifiableList() but it was still modified?
I fixed the issue with some serious jankiness.
Object[] temp = new Object[ONE.size()];
temp=ONE.toArray(temp);
TWO = Arrays.asList(temp);
Awful I know... It works because Arrays.asList is immutable but surely the unmodifiable list is also immutable?
Added Code for those requesting.
Initialisation Code:
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
SpaceObject[] atom = new SpaceObject[visibleObjects.size()];
atom=visibleObjects.toArray(atom);
VISIBLE_OBJECTS = Arrays.asList(atom);
SHOWING_OBJECTS = visibleObjects;
addToView(visibleObjects);
}
Responding to removing or adding items.
private void showSpecificItems(String itemType, Boolean remove){
int i = 0;
if (remove) {
while (i < SHOWING_OBJECTS.size()){
if(SHOWING_OBJECTS.get(i).getType().toLowerCase().matches(itemType.toLowerCase())){
SHOWING_OBJECTS.remove(i);
}else{
i++;
}
}
}else{
System.out.println("remove");
while(i < VISIBLE_OBJECTS.size()){
if(VISIBLE_OBJECTS.get(i).getType().toLowerCase().matches(itemType.toLowerCase())){
SHOWING_OBJECTS.add(VISIBLE_OBJECTS.get(i));
}
i++;
}
SHOWING_OBJECTS = sortBrightest(SHOWING_OBJECTS);
}
addToView(SHOWING_OBJECTS);
}
If you have code like this
List<Object> ONE = someMagic();
List<Object> TWO = ONE;
List<Object> THREE = ONE;
Then you have three things all pointing at the same list.
If you want them to be different, you could try something like this:
List<Object> ONE = someMagic();
List<Object> TWO = ONE.clone();
List<Object> THREE = ONE.clone();
This way they make actual different lists.
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
// this line is useless - you allocate an array only to immediately toss it away?!
SpaceObject[] atom = new SpaceObject[visibleObjects.size()];
atom=visibleObjects.toArray(atom);
// this uses atom as its backing array, which comes from visibleObjects
VISIBLE_OBJECTS = Arrays.asList(atom);
// this obviously atom as a backing array
SHOWING_OBJECTS = visibleObjects;
addToView(visibleObjects);
}
So to fix this, you need to remove the dependency on that backing array
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
VISIBLE_OBJECTS = visibleObjects.clone();
SHOWING_OBJECTS = visibleObjects.clone();
addToView(visibleObjects); // probably bad form, but I doubt this will keep a reference, so it's "acceptable"
}
if clone is not available for you, you can do it a little more complicated
protected void onPostExecute(List<SpaceObject> visibleObjects){
hideLoading();
VISIBLE_OBJECTS = new ArrayList(visibleObjects);
SHOWING_OBJECTS = new ArrayList(visibleObjects);
addToView(visibleObjects); // probably bad form, but I doubt this will keep a reference, so it's "acceptable"
}
I tried changing TWO to be a Collections.unmodifiableList() but it was still modified?
The javadocs say this about unmodifiable views as created by that method:
An unmodifiable view collection is a collection that is unmodifiable and that is also a view onto a backing collection. Its mutator methods throw UnsupportedOperationException, as described above, while reading and querying methods are delegated to the backing collection. The effect is to provide read-only access to the backing collection. ...
Note that changes to the backing collection might still be possible, and if they occur, they are visible through the unmodifiable view. Thus, an unmodifiable view collection is not necessarily immutable.
In short, unmodifiableList() does not have the properties that you expect. If you want an immutable snapshot of a mutable list, you need to copy it.

Will this work like a destructor?

I am working on a Processing program for Brownian motion tracking.
I have an ArrayList blobs and ArrayList tomerge. The first one is a list of particles which I track and the second one is a list of particles which I want to merge.
Every particle is a Blob class object. Blob object countains ArrayList of Vectors called lespoints and int id in its data.
Since I need to merge a few particles in one, I need to destroy some Blob objects, but Java doesn't have any destructors and I don't want to use finalise(). Will this work like merge + destruction?
public void delete(Blob a)
{
a = null;
}
void merge(ArrayList<Blob> tomerge)
{
int i = 0;
int j = 0;
while (i <= tomerge.size())
{
Blob k = new Blob();
k = tomerge.get(i);
while (j <= tomerge.get(i).siz()) {
Vector g = k.lespoints.get(j);
lespoints.add(g);
j++;
}
if (i > 0)
{
delete(tomerge.get(i));
}
i++;
}
}
You don't need to manually do anything. Just make sure you don't have any references to the variables you want to go away, and Java will handle the rest.
For example:
String x = "test";
x = null;
At this point, the value "test" can be garbage collected because nothing points to it.
Compare that to this:
String x = "test";
ArrayList<String> list = new ArrayList<>();
list.add(x);
x = null;
At this point, the value "test" cannot be garabage collected, because the ArrayList still points to it.
But if you did this:
list.remove("test");
Then it could be garbage collected.
So basically, all you need to do is remove the element from your ArrayList and Java will take care of the rest. Note that you probably don't want to do this in your current loop, as removing elements while you iterate over a list can cause you to skip over elements.
Instead, you probably want to use an iterator or just loop backwards over your ArrayList.
Shameless self-promotion: here is a tutorial on ArrayLists, including removing elements from them.
There is an exact reason why your code example won't work.
public void delete(Blob a)
{
a = null;
}
Blob b = new Blob();
delete(b);
In this code example, the reference which is set to null is a, not b.
You are not deleting the Blob object, you are setting the reference to null.
When the delete() method is called, there exists 2 references to the Blob.
One reference is b, which is in the calling code.
The other reference is a, which is in the called code.
a is set to null, and then the method exits. But the b reference continues to exist throughout. Therefore the Blob will never be garbage-collected.
To achieve garbage-collection, you must remove all references to an object; then it gets destructed at the JVM's convenience.
The Java Collections API for removing an object during iteration works like this:
Iterator<Blob> itr = list.iterator();
while( itr.hasNext() ) {
Blob b = itr.next();
if( /* test condition */ ) {
itr.remove(); // Safely removes object from List during iteration
}
} // Object `b` goes out of scope, and so this Blob is "lost" to the the code and is going to be destroyed

Merge Queues in Java

I was wondering whats the best way to write a method to merge an ArrayQueue with another Queue without removing any elements from the q thats passed.
eg. queue1 = [1,2,3,4] and queue2 = [5,6,7,8,9,10].
When queue1.mergeQs(queue2) was called it would create queue1 = [1,5,2,6,3,7,4,8,9,10] whilst queue2 would remain [5,6,7,8,9,10]
public void mergeQs(ArrayQmerge q){}
This way seems harder to implement than if you were to pass both Queues and return a new merged Queue. Thanks.
Just to clarify, i'm looking for a method to interleave the elements from the two queues.
One detail that might help you is that private fields are visible between different object of the same class in Java. That means that as long as you only intend to merge queues of your own class, your code has full access to all internal fields, such as the array you use to store your elements.
For the simplest case, where all elements are stored in a linear array with the queue head being at index zero, something like this might be a start:
public void mergeQs(ArrayQmerge q) {
Object[] array = new Object[this.size() + q.size()];
int i;
int o;
// Interleave elements
for (i = 0, o = 0; i < this.size() && i < q.size(); ++i) {
array[o++] = this.array[i];
array[o++] = q.array[i];
}
// Copy the remaining elements
while (i < this.size()) {
array[o++] = this.array[i++];
}
while (i < q.size()) {
array[o++] = q.array[i++];
}
this.array = array;
}
You can create a new Queue locally in the merge method, then assign your class's queue to the local version.
Since you are using your own homebrew ArrayQueue then this is conjecture.
Creating a new queue and returning is as I think you already say is way easier, and more efficient, as inserting elements into an Array backed structure will involve shuffling the rest of the elements down one position for each insert op.
An alternative is to implement public void mergeQs(ArrayQmerge q) by swapping out the underlying array you have backing it. So you get the same easy implementation as returning a new Queue but with the same in place side effect.

Get the array from an AtomicLongArray

Using Java 1.6 and the AtomicLongArray, I'd like to "copy" the original AtomicLongArray into a new one. There is a constructor that takes an array (AtomicLongArray(long[])), so I thought I could just get the array from the original one and give it to the constructor.
Sadly, the actual long[] in the AtomicLongArray is private and there seem to be no getters for it. Is there any way to do this, meaning copy the values from one AtomicLongArray to another? I can't create my own class based on this class, as the sun.misc.Unsafe class is not available to me.
This is needed because I'm going to iterate over the values, and I don't want them modified by another thread during iteration. So I thought I could make a copy and use that for the iteration...
Thanks!
Phillip
I suspect you have to create your own long[] and populate it first, or just iterate over the original:
AtomicLongArray copy = new AtomicLongArray(original.length());
for (int i = 0; i < copy.length(); i++)
{
copy.set(i, original.get(i));
}
Note that although each individual operation in AtomicLongArray is atomic, there are no bulk operations - so there's no way of getting a "snapshot" of the whole array at time T. If you want that sort of behaviour, I believe you'll need to use synchronization.
This data structure allows concurrent updates to individual entries in the collection. There is not overall lock, so you can't prevent another thread changing the contents while you are iterating over it.
If you need this, you need a workaround, e.g. copy the array and loop again to check it hasn't changed. If changed, repeat. Or you need a collection which supports a global lock.
long[] copy = new long[original.length()];
boolean changed = true;
// repeat until we get an unchanged copy.
while(true) {
for (int i = 0; i < copy.length(); i++) {
long l = original.get(i);
changed |= copy[i] != l;
copy[i] = l;
}
if (!changed) break;
changed = false;
}
This is not completely safe, but may be enough for what you need.

Better practice to re-instantiate a List or invoke clear()

Using Java (1.6) is it better to call the clear() method on a List or just re-instantiate the reference?
I have an ArrayList that is filled with an unknown number of Objects and periodically "flushed" - where the Objects are processed and the List is cleared. Once flushed the List is filled up again. The flush happens at a random time. The number within the List can potentially be small (10s of Objects) or large (millions of objects).
So is it better to have the "flush" call clear() or new ArrayList() ?
Is it even worth worrying about this sort of issues or should I let the VM worry about it? How could I go about looking at the memory footprint of Java to work this sort of thing out for myself?
Any help greatly appreciated.
The main thing to be concerned about is what other code might have a reference to the list. If the existing list is visible elsewhere, do you want that code to see a cleared list, or keep the existing one?
If nothing else can see the list, I'd probably just clear it - but not for performance reasons; just because the way you've described the operation sounds more like clearing than "create a new list".
The ArrayList<T> docs don't specify what happens to the underlying data structures, but looking at the 1.7 implementation in Eclipse, it looks like you should probably call trimToSize() after clear() - otherwise you could still have a list backed by a large array of null references. (Maybe that isn't an issue for you, of course... maybe that's more efficient than having to copy the array as the size builds up again. You'll know more about this than we do.)
(Of course creating a new list doesn't require the old list to set all the array elements to null... but I doubt that that will be significant in most cases.)
The way you are using it looks very much like how a Queue is used. When you work of the items on the queue they are removed when you treat them.
Using one of the Queue classes might make the code more elegant.
There are also variants which handle concurrent updates in a predictable way.
I think if the Arraylist is to be too frequently flushed,like if it's run continuously in loop or something then better use clear if the flushing is not too frequent then you may create a new instance.Also since you say that elements may vary from 10 object to millions you can probably go for an in-between size for each new Arraylist your creating so that the arraylist can avoid resizing a lot of time.
There is no advantage for list.clear() than new XXList.
Here is my investigation to compare performance.
import java.util.ArrayList;
import java.util.List;
public class ClearList {
public static void testClear(int m, int n) {
List<Integer> list = new ArrayList<>();
long start = System.currentTimeMillis();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
list.add(Integer.parseInt("" + j + i));
}
list.clear();
}
System.out.println(System.currentTimeMillis() - start);
}
public static void testNewInit(int m, int n) {
List<Integer> list = new ArrayList<>();
long start = System.currentTimeMillis();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
list.add(Integer.parseInt("" + j + i));
}
list = new ArrayList<>();
}
System.out.println(System.currentTimeMillis() - start);
}
public static void main(String[] args) {
System.out.println("clear ArrayList:");
testClear(991000, 100);
System.out.println("new ArrayList:");
testNewInit(991000, 100);
}
}
/*--*
* Out:
*
* clear ArrayList:
* 8391
* new ArrayList:
* 6871
*/

Categories