10

There is a list of Person objects.

List<Person> persons = generatePersons();

An unmodifiableList is created with it.

List<Person> unmodifiableList = Collections.unmodifiableList(persons);

I understand that unmodifiableList doesn't support add/remove/set operations. At the same time it is not immutable since it has a reference to an existing modifiable list persons and whenever changes are made to the persons list, the changes are reflected in unmodifiableList too.

An immutable list is created this way.

List<Person> immutableList = Collections.unmodifiableList(new ArrayList<>(persons));

This creates an immutable list since a conversion constructor is being used. No add/remove/set ops can be performed on immutableList nor any change in the original list persons would reflect in immutableList. Let's make an assumption that Person objects are immutable too.

Now, I want to create these two lists using streams.

The first one, I have created using:

List<Person> unmodifiablePersons = persons.stream() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));

I am lost at creating equivalent of immutableList through streams.

How can I do that?

Edit:

I added a new Person object to the original list persons and printed the size of persons list and unmodifiablePersons. Both give me the same size. So, changes are being reflected to unmodifiablePersonsand hence it is not immutable yet. Am I missing something here?

Edit 2

Silly. Should have gone through the docs. unmodifiablePersons is indeed an immutable list. Also, the new Person object was added before the unmodifiablePersons was created and hence the above observation. Super silly.

Sara
  • 603
  • 8
  • 19
  • 6
    Your example _is_ an immutable List. In fact, it's the example the docs give: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#collectingAndThen-java.util.stream.Collector-java.util.function.Function- – Zircon Oct 26 '18 at 17:16
  • @Zircon I have updated my question with my observation. Kindly look into it. – Sara Oct 26 '18 at 17:25
  • 1
    @Sara you are not - that is indeed a *view* of that `List` – Eugene Oct 26 '18 at 17:26
  • Would recommend going through [this discussion](http://mail.openjdk.java.net/pipermail/core-libs-dev/2017-November/050059.html) on the JDK mailing list while `copy` methods were implemented along with collectors to collect to set, list and map. – Naman Oct 26 '18 at 17:29
  • @Zircon Updated with Edit 2. – Sara Oct 26 '18 at 17:46

2 Answers2

16

Well in your first case someone has access to List<Person> unmodifiableList and can edit it, but when you collect no one has access to that List generated by Collectors.toList - so you are good.

What you are probably missing is that Collectors::toList will create a new List - which should really be obvious; and you wrap it into an unmodifiable one, thus the result of that is truly unmodifiable.

Also in java-10 there is special collector for that:

List<Integer> result = Arrays.asList(1, 2, 3, 4)
        .stream()
        .collect(Collectors.toUnmodifiableList());

This collector uses List::of internally - immutable collections added in java-9, thus for example they don't support nulls.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • I'm disappointed that they didn't remove the mutability ambiguity from `toList()`. There's not much practical difference between an immutable list and a *possibly* immutable list. – shmosel Oct 26 '18 at 17:36
  • @shmosel you mean from documentation of `Collectors::toList`? – Eugene Oct 28 '18 at 03:48
  • @shmosel well removing it might mean that they would have to stick to either mutable or immutable, I guess. there were comments from Stuart Marks that they are thinking that `Collectors::toList` will return an immutable List after all in some future – Eugene Oct 28 '18 at 03:51
  • 1
    Right, but that would be pointless now that there's `toUnmodifiableList()`. – shmosel Oct 28 '18 at 03:53
  • @shmosel right, and now I wonder why not explicitly specify that `toList` *is* modifiable -well, you can't, sine the documentation is *out there* already. reminds me of `Optional::get` and `Optional::orElseThrow` (without argument) – Eugene Oct 28 '18 at 03:57
  • There's no harm in making documentation more specific. – shmosel Oct 28 '18 at 03:58
  • @shmosel [of course](https://bugs.openjdk.java.net/browse/JDK-8211831) [there isn't](https://stackoverflow.com/questions/52692803/hashmap-rehash-resize-capacity) - just like they already did; but in my understanding this would be super low priority – Eugene Oct 28 '18 at 04:00
  • Checking the implementation, this still accumulates to a mutable list, then makes a full copy into an immutable one. I was hoping to avoid this copy, while still getting an `AbstractImmutableList` that incurs no copying cost when later used with `List.copyOf`. – john16384 Sep 07 '19 at 09:01
6

With Java10+, you could have used an inbuilt capability copyOf for creating an unmodifiable list, like this:

List<Person> persons = generatePersons();       
List<Person> unmodifiableList = List.copyOf(persons);

Also, the current list that you're creating using Collector.collectingAndThen is actually immutable as mentioned in the documentation as well.

Naman
  • 27,789
  • 26
  • 218
  • 353