• Post Reply Bookmark Topic Watch Topic
  • New Topic

Arrays.asList and Generics  RSS feed

 
Stephan Strauss
Greenhorn
Posts: 14
Java Linux Scala
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Dear mates, I have the following problem I actually do not totally understand ...
Having the following code snip



The code compiles without any warning (e.g. unchecked) or error, but when running this
code I get in the case of aslist = true a Runtime error


Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at ch.phonon.examples.covariance.Main.copy(Main.java:79)
at ch.phonon.examples.covariance.Main.main(Main.java:108)


what I cannot understand, because the code should be typesafe, but I still run into a wall.
Where is my mistake?

Thanks a lot and have a nice weekend,
Stephan
 
Jesper de Jong
Java Cowboy
Sheriff
Posts: 16059
88
Android IntelliJ IDE Java Scala Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That happens because Arrays.asList() returns an implementation of interface List to which you cannot add any elements - the add() method of the returned List throws UnsupportedOperationException.

If you want to be able to add numbers, then copy whatever Arrays.asList() returns into a regular ArrayList:

 
Stephan Strauss
Greenhorn
Posts: 14
Java Linux Scala
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jesper de Jong wrote:That happens because Arrays.asList() returns an implementation of interface List to which you cannot add any elements - the add() method of the returned List throws UnsupportedOperationException.

If you want to be able to add numbers, then copy whatever Arrays.asList() returns into a regular ArrayList:



Thanks a lot for your fast and good explanation. Now, I checked the source of Arrays and find this private static class
implementing another ArrayList (:-) -- this I have missed when I first checked the code. Now I understand.
But why is the Arrays class designed like that -- would It have been not better to directly return a standard
java.util.ArrayList ? Probably to address also other containers implementing List, right ?

Cheers, Ste
 
Jesper de Jong
Java Cowboy
Sheriff
Posts: 16059
88
Android IntelliJ IDE Java Scala Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Because Arrays.asList() returns a special List that is backed by the original array - which means that if you change an element in the original array, you'll see the change in the list returned by Arrays.asList() for that array, and vice versa.

That wouldn't be possible if Arrays.asList() returned a regular java.util.ArrayList.

It also explains why add() isn't implemented - because you cannot make an array larger after creating it, so there's no way to add an extra element to the array that's behind the List.
 
Stephan Strauss
Greenhorn
Posts: 14
Java Linux Scala
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jesper de Jong wrote:Because Arrays.asList() returns a special List that is backed by the original array - which means that if you change an element in the original array, you'll see the change in the list returned by Arrays.asList() for that array, and vice versa.

That wouldn't be possible if Arrays.asList() returned a regular java.util.ArrayList.

It also explains why add() isn't implemented - because you cannot make an array larger after creating it, so there's no way to add an extra element to the array that's behind the List.



Ah, yes ... makes perfectly sense ... avoid aliasing between the array elements and the list elements, and add ... yep ... the array is a static container ... good hint.
Maybe there are still some questions open ... but first I will go on checking myself.

Jesper, thanks a lot for your time and have a nice weekend,
Stephan
 
Campbell Ritchie
Marshal
Posts: 56536
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It says in the API documentation that it returns a fixed‑size List, and that changes to the underlying array are written through. It might have been better to have said that the add method cannot be used. What happens if you use the set method on the returned List? Do you get the same Exception? Or remove?
 
Stephan Strauss
Greenhorn
Posts: 14
Java Linux Scala
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Ritchie: set works in the bounds of the original array

@all:

I discussed a the implementation a little bit with some friends over lunch and we agree with Ritchie, that the documentation
could be at least more precise concerning the consequences of this design / on the other hand I could have had a hint already
by the method name that is asList and not toList.

Anyway some additional questions I would like to ask and try to answer:

a)

What is the advantage of providing actually a view onto the array as List type VS directly providing a new ArrayList for example.
I can see the benefit in cases someone would like to avoid the "deep" copy of the stored objects or avoid problems,
that could be caused, if the clone contract is broken ...

BUT there is another problem (I think) that Jesper pointed me to with his comment concerning the alias in the actual implemention ...

You CANNOT remove[*] array elements (you just can set them to null), but you can remove list elements, right ? That means, that any
array element set to null, will NOT lead to a (n-1)-reduced number of the ordered list elements but to one null entry in the list.

[*] ok, at least when not using Apache ArrayUtils.remove() that probably does a defensive copy and gives back the new array

b)

Little bit off the topic but also relevant in case of asList:

How are primitive arrays handeled ... is each element boxed, probably not ... a list with one entry that is a array of int ?

e.g.

I will check this in the evening ... and just write my observation.
 
Campbell Ritchie
Marshal
Posts: 56536
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
There are several things you can do
  • 1: new XYZList<>(myArray). That gives you a List containing the exact contents of the array at the time of creation; both can be changed independently because they are completely separate from each other.
  • 2: Arrays.asList(myArray). That is rather like a read‑only version of the array but in a List. We have seen however that the set() method can be used: did it alter the original array?
  • 3: Collections.unmodifiableList(Arrays.asList(myArray)). That is a proper read‑only version of the array: the List returned contains the contents of the array but (I think) can be altered only by reassigning elements in the original array.
  • 4: Collections.unmodifiableList(new XYZList<>(myArray)). That creates a new List and a read‑only copy of it. You can only amend that by changing the new XYZList(...). But you cannot get a reference to that XYZList, so the result of the whole expression is an immutable List.
  • By immutable I mean that the elements of the List will remain the same for ever. They may however be mutable and may therefore change their state.
    Try all four of those possibilities and see what happens.
     
    Campbell Ritchie
    Marshal
    Posts: 56536
    172
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Stephan Strauss wrote: . . .
    How are primitive arrays handeled ... is each element boxed, probably not ... a list with one entry that is a array of int ?
    . . .
    Not sure. It says T... array in the API documentation and there is no overloading of that method, so I think you would regard that as a 1‑element Object[] and get a List<int[]>. I do not think there is any boxing involved.
     
    Stephan Strauss
    Greenhorn
    Posts: 14
    Java Linux Scala
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Campbell Ritchie wrote: ... did it alter the original array?


    Yes, Ritchie, it does (aka aliasing) ... as we assumed ... because the new list element references point all to the
    original array objects.

    Thanks for your answers and effort, today ... It is/was my first posting here, and it is true, that this forum is obviously very good
    moderated by you guys - and people are really friendly here as well. Great!

    I will go over the other points in the evening ...

    cheers,
    Stephan
     
    Campbell Ritchie
    Marshal
    Posts: 56536
    172
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I think it is aliasing; I think if you go through the code you will find the private ArrayList class uses the same array passed as its backing array, so you have two references to the same object.

    And thank you for the nice things you said about the Ranch
     
    It is sorta covered in the JavaRanch Style Guide.
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!