简体   繁体   中英

Java 8 lambda expression changes Collection multiple times

I have the following classes:

Doer, which just executes the run-method of the Runnable (will be a lambda):

public class Doer {
    final Runnable runnable;

    public Doer(final Runnable runnable) {
        this.runnable = runnable;
    }

    public void doSomething() {
        runnable.run();
    }
}

Storage, which is a Observable number store:

import java.util.ArrayList;
import java.util.List;
import java.util.Observable;

public class Storage extends Observable {
    final List<Integer> storage;

    public Storage() {
        this.storage = new ArrayList<>();
    }

    void add(int number) {
        storage.add(number);
        setChanged();
        notifyObservers(number);
    }

    public List<Integer> getStorage() {
        return storage;
    }
}

Filter, preventing the Doer from doing so much ;)

import java.util.Observable;
import java.util.Observer;

public class Filter implements Observer {

    final Doer doer;
    int counter;
    final int nth;

    public Filter(int nth, Storage store, Doer doer) {
        this.doer = doer;
        this.nth = nth;
        store.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        counter++;
        if (counter == nth)
            doer.doSomething();
    }
}

Now consider the following Unit Test:

import org.junit.Test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class FilterTest {

    @Test
    public void testStore() throws Exception {
        List<Collection<Integer>> numbers = new ArrayList<>();
        Storage store = new Storage();
        Filter filter = new Filter(2, store, new Doer(() -> {
            numbers.add(store.getStorage());
            System.out.println(numbers);
        }));

        System.out.println(numbers);

        store.add(1);
        System.out.println(numbers);

        store.add(2);
        System.out.println(numbers);

        store.add(3);

        System.out.println(numbers);

    }
}

It produces the results:

[]
[]
[[1, 2]]
[[1, 2]]
[[1, 2, 3]]

I really do not get the last line, why is the 3 now in numbers, since numbers.add(3) has not been executed in the lambda?

The lambda is called only once. It's adding the List<Integer> that's inside store to numbers .

Note that the lambda is not making a copy of the List<Integer> in store . So, numbers contains a reference to the same List<Integer> as what is in store .

If you add more numbers to the List<Integer> in store , you see those changes also in numbers , because the content of numbers is a reference to the same List<Integer> .

So when you do store.add(3); and then print the content of numbers , you'll also see the 3 in there.

Try making a copy of the collection when you add it to numbers in the lambda, and see the difference:

Filter filter = new Filter(2, store, new Doer(() -> {
    numbers.add(new ArrayList<>(store.getStorage()));
    System.out.println(numbers);
}));

Output:

[]
[]
[[1, 2]]
[[1, 2]]
[[1, 2]]

Without knowing what your code is really supposed to do, I assume your numbers collection is not declared correctly:

List<Collection<Integer>> numbers = new ArrayList<>();

This is a list of collections of integers, so every element of your list is supposed to be a collection by itself. Instead, it should probably be just a list of numbers:

List<Integer> numbers = new ArrayList<>();

So you will also have to change the Runnable too, as you don't want to add the entire storage to your list but just the content of the storage:

() -> {
    numbers.addAll(store.getStorage());
    System.out.println(numbers);
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM