• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Ron McLeod
  • Paul Clapham
  • Rob Spoor
  • Liutauras Vilda
Sheriffs:
  • Jeanne Boyarsky
  • Junilu Lacar
  • Tim Cooke
Saloon Keepers:
  • Tim Holloway
  • Piet Souris
  • Stephan van Hulst
  • Tim Moores
  • Carey Brown
Bartenders:
  • Frits Walraven
  • Himai Minh

ArrayList

 
Ranch Hand
Posts: 31
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hello Everyone,
I am preparing for OCA and recently while practicing some questions, I came across one question on ArrayList.

Question: -

Identify two benefits of using ArrayList over array in software development.


A. reduces memory footprint
B. implements the Collection API
C. is multi.thread safe
D. dynamically resizes based on the number of elements in the list
Answer: A,D


I just wanted to ask how ArrayList helps in reducing Memory Footprints.
Please help me with this.
 
Marshal
Posts: 73760
332
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Please always tells us where such information comes from, so we can assess the original.
What did you think the correct answer is?
 
Bartender
Posts: 1059
33
Eclipse IDE Postgres Database C++ Java
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Good call.

An ArrayList will ALWAYS take more memory than a corresponding Array of the same size, trivially if the Array is of reference type anyway, but possibly a LOT more if you have an

ArrayList<Integer>

instead of an int[]

of the same size.

I think they were trying to say that if you dealt with the fact that an Array can not be expanded after creation by over-allocating in advance, say

Integer[] iarray = new Integer[150_000];

That would be wasteful if you wound up only using 3 elements.

 
Master Rancher
Posts: 4002
52
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yeah, I think the given answer A is simply wrong - ArrayList doesn't reduce memory footprint, generally. A better answer exists among the options, that has not been mentioned yet.
 
Saloon Keeper
Posts: 24207
166
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
An ArrayList has an internal pointer to an array. So that blows that whole assertion out of the water right there. On top of that, to facilitate easy growth, the ArrayList will often have a number of reserved slots in its array, so even just the internal array alone would more often than not exceed the size of an equivalent fixed (immutable) array.

So I too think that A not a correct answer.
 
Campbell Ritchie
Marshal
Posts: 73760
332
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think you have found a badly worded question and none of the answers is 100% right. It would be better for B to say it implements the Collection interface, and for D to say that an ArrayList dynamically enlarges whenever necessary. The documentation says nothing about whether it shrinks if it is emptied. But you can be confident that two answers viz. A as discussed above, and C, are 100% wrong.
 
Marshal
Posts: 22409
121
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:The documentation says nothing about whether it shrinks if it is emptied.


This is probably not news to most people, but it doesn't.
 
Campbell Ritchie
Marshal
Posts: 73760
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I thought it didn't shrink, but couldn't find any proof without going through the source code.
 
Jesse Silverman
Bartender
Posts: 1059
33
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It may be *allowed to*, but unless someone makes this call:

public void trimToSize()
Trims the capacity of this ArrayList instance to be the list's current size. An application can use this operation to minimize the storage of an ArrayList instance.

I don't think it actually does.

It is too common for sizes to be going up and down, in which case auto-trimToSize() would be very counterproductive.

We can barely count on the Garbage Collector kicking in when we think it will...
 
Campbell Ritchie
Marshal
Posts: 73760
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There is a problem about trimToSize():-But doesn't use of instanceof other than in an equals() method indicate a bad inheritance hierarchy? The same problem applies to ensureCapacity() too.
 
Jesse Silverman
Bartender
Posts: 1059
33
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
[Heavy EDITS made as I realized List is even closer to ArrayList at compile time than I had thought]

The List interface is usually implemented (in the wild) by either ArrayList<T> or LinkedList<T>.

It is a rather extreme example of an interface that is pre-dominated by one implementation, the ArrayList.

I forget the implementer of it, but they once tweeted "Does anyone use LinkedList in Java?  I implemented it and I almost never use it."

Anyway, neither trimToSize() nor ensureCapacity() have any meaning for LinkedList nor in general any other odd List implementation that isn't "like ArrayList".

People who are always eager to keep their options open by coding to interfaces rather than implementations deplore usage of local variable type inference such as:

var myList = new ArrayList<>();

Because now we are committed to ArrayList.  EDIT -- same deal with having a method parameter or an instance variable of ArrayList instead of List...

There's many places in C++ and enough in Java where doing something that looks like that is painting yourself into a corner, and bad.

In the case of List<> vs. ArrayList, I would say if you are thinking about  trimToSize() or ensureCapacity() you have now baked ArrayList into your design, and I'm not so sure that's a terrible thing.

On the scale of "just code to the implementation, dude!" to "Always code to the interface" I am normally 90% of the way towards interface obsessed.

I am actually a little surprised at how much List interface permeates the Java Collections Framework but will note that in the cases of not just immutable but fixed-sized List objects, we aren't really going to be using the index-based methods anyway:
subList(), removeRange(), set()[, add( atIndex ), remove(atIndex), indexOf( value), lastIndexOf( value )

Or would we?  You lose the ability to do all the index-based operations when abstracting to a List, don't you?  EDIT -- not quite--I had forgotten that all except removeRange() are actually legal on any non-fixed-size mutable List, set(), indexOf(), lastIndexOf() and subList() even on a fixed-sized List!

Those are all more frequently something we would want to do than trimToSize() or ensureCapacity()  and therefore more likely reasons to just stay with the implementation (ArrayList) than abstracting to the interface (List)...[except List lets you do most of these still -- oops!]

Note, I still strongly prefer to coding to the interface in general, I just feel like I am missing what it is buying us here, except the rare desire to swap it out for a LinkedList, and other things that are even rarer?

Because I messed up and forgot what we can and can't do with a List.of() which is seen so frequently in Modern Java, either because it is convenient for examples, has the attraction of novelty, or because it is often great to use, I am reminding myself and others what you can and can't do with 'em:

Unmodifiable Lists
The List.of and List.copyOf static factory methods provide a convenient way to create unmodifiable lists. The List instances created by these methods have the following characteristics:

They are unmodifiable. Elements cannot be added, removed, or replaced. Calling any mutator method on the List will always cause UnsupportedOperationException to be thrown. However, if the contained elements are themselves mutable, this may cause the List's contents to appear to change.
They disallow null elements. Attempts to create them with null elements result in NullPointerException.
They are serializable if all elements are serializable.
The order of elements in the list is the same as the order of the provided arguments, or of the elements in the provided array.
The lists and their subList views implement the RandomAccess interface.
They are value-based. Programmers should treat instances that are equal as interchangeable and should not use them for synchronization, or unpredictable behavior may occur. For example, in a future release, synchronization may fail. Callers should make no assumptions about the identity of the returned instances. Factories are free to create new instances or reuse existing ones.
They are serialized as specified on the Serialized Form page.



The more I look at them, the more they remind me of tuple vs. list  in Python.
There, tuple literals use ( el1, el2, el3 ) instead of [ el4, el5, el6 ] and are fundamentally different types.  They are valid legal keys in Maps (called dicts) and elements in sets, lists are NOT.

So I guess the List interface is good to use not just for Immutable Lists (which would be tuples in Python) but fixed-size writable Lists as well.
All you seem to lose is removeRange(),  trimToSize() and ensureCapacity()

It might have been nicer to have the distinction between mutable and immutable lists more clear in Java, like it is in Python, but I jumped the gun thinking there are many use cases for preferring ArrayList implementation to List interface.  List makes it look like you can do dang-near anything you could with a mutable, non-fixed-size ArrayList, even if many of those things might throw UnsupportedOperationException at runtime.  I'd prefer such mistakes not to even compile, but, "You Can't Have Everything".
 
Jesse Silverman
Bartender
Posts: 1059
33
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
A better question is "Why would you ever use an Array rather than ArrayList??"

1. Various API's return them.  Examples: myString.split(), File's .list() or .listFiles() [but please just use nio.2 Files/Path]
2. You have an enormous but known number of primitives, say int/float/double and want to save space and access time...

Neither of these come up that often, the question is kind of backwards nowadays.  It isn't 1998.
 
Rob Spoor
Marshal
Posts: 22409
121
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:1. Various API's return them.  Examples: myString.split(), File's .list() or .listFiles() [but please just use nio.2 Files/Path]


I dare say that at least 95% of methods that return an array should have returned a List instead. The examples you list are ancient (although String.split is more recent than the Collection Framework - 1.4 vs 1.2). Any recent code should return either Collection or List, or possibly Stream if the method internally uses a Stream only to collect to a List at the end.

Going further, I still think it's a mistake that the values() that's automatically added to enums should have returned a List. An enum class internally has a static array of values (thanks to the compiler). When you call values(), to prevent that internal array from being modified, a copy is returned. That's done every single time you call values(). If the method returned List instead, an immutable wrapper could have been returned. Or, since Java 9, the List itself could be immutable.

That does give one limitation - enums cannot be used in the definition of both List and the implementation used to store the enum constants. Unlike List and its implementations, arrays are more low-level constructs and therefore don't have any dependency other than the element type.. Even letting values() return Collections.unmodifiableList(Arrays.asList(INTERNAL_ARRAY)) could lead to class loading issues. That's probably why values() returns an array instead of a List

Now if only someone would add const support for arrays in Java. It's already a reserved word.
 
Jesse Silverman
Bartender
Posts: 1059
33
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:It would be better ... for D to say that an ArrayList dynamically enlarges whenever necessary. The documentation says nothing about whether it shrinks if it is emptied.



I should have read what you wrote more carefully!

Agreed, except that you of course CAN choose to call .trimToSize() (unless using a reference declared as List) which is marginally more flexible than whatever you can do with an already multiply referenced Array.  I still think the question should be turned around to "Why and when would you ever use an Array when we have so many great versions of List now?"  I vaguely remember Java Guys talking about various great optimizations some of those List variants make under the covers because they know they are fixed size or immutable that 99% of Java users just magically benefit from...
 
Jesse Silverman
Bartender
Posts: 1059
33
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rob Spoor wrote:). Any recent code should return either Collection or List, or possibly Stream if the method internally uses a Stream only to collect to a List at the end.

...

...Unlike List and its implementations, arrays are more low-level constructs and therefore don't have any dependency other than the element type.. Even letting values() return Collections.unmodifiableList(Arrays.asList(INTERNAL_ARRAY)) could lead to class loading issues. That's probably why values() returns an array instead of a List

Now if only someone would add const support for arrays in Java. It's already a reserved word.



Brilliant.  I've learned ten times more from this thread than I thought I would...there are various places that seams due to the fact that Collections are not part of the language show thru, this is one of them....

I am not sure, but the JVM itself knows about Arrays in ways that it doesn't "know about" Collections, in particular, besides allowing primitive value types, they are "re-ified" with both good and bad effects from that, right?  @SafeVarArgs indeed
 
Campbell Ritchie
Marshal
Posts: 73760
332
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:. . . neither trimToSize() nor ensureCapacity() have any meaning for LinkedList  . . .

Are you quite sure about that? Since its size and capacity are always the same, those two methods can be implemented somply by doing nothing.
 
Mike Simmons
Master Rancher
Posts: 4002
52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:

Jesse Silverman wrote:. . . neither trimToSize() nor ensureCapacity() have any meaning for LinkedList  . . .

Are you quite sure about that? Since its size and capacity are always the same, those two methods can be implemented somply by doing nothing.


Well, they could have been implemented that way, but they have not been.  So in one sense, those methods have no meaning for other List types, because the method has not been declared for other types.  In another, those methods have no meaning because they're nonsensical.  I think putting them in the List API would just confuse people.  And what would trimToSize() or ensureCapacity() do on an immutable list (e.g. from List.of()) or a list returned by Arrays.asList()?  Should they return silently, or throw errors?  For me it's hard to answer those because, once again, the notion of "capacity" is nonsense for those types of lists.
 
Jesse Silverman
Bartender
Posts: 1059
33
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mike Simmons wrote:
Well, they could have been implemented that way, but they have not been.  So in one sense, those methods have no meaning for other List types, because the method has not been declared for other types.  In another, those methods have no meaning because they're nonsensical.  I think putting them in the List API would just confuse people.  And what would trimToSize() or ensureCapacity() do on an immutable list (e.g. from List.of()) or a list returned by Arrays.asList()?  Should they return silently, or throw errors?  For me it's hard to answer those because, once again, the notion of "capacity" is nonsense for those types of lists.



The take home for me was that the compile-time supported API for List is WAY closer to that of ArrayList than I had realized.

I know I have seen people in this forum questioning whether or not it is a good thing that List allows access to many index-based methods that would seem only to be a good idea to use on a RandomAccess list: https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/RandomAccess.html

As it is, it seems to be a very common practice to have reference variables of declared type List that will throw various RuntimeExceptions  if many different calls that will compile are actually executed.

While I would prefer to have calls that will just blow up my program at run time not compile in the first place, I am sure there are trade-offs here.

The way things are, it seems to require some careful attention to what you try to do to a garden-variety List reference, as some of those things that will compile will blow up if the actual type is fixed-size, and even more of them will blow up if it is completely immutable.  In C++, attempts to call mutating methods with a constant reference won't even compile, Python has mutable and immutable lists treated as distinct data types at the language level itself.  This seems to be a case where coding in Java requires more care and attention.

Or am I just following bad examples where very mutable, fixed-size and immutable objects are all getting assigned to "I dunno, it must be some kind of List" List<> references?
It feels like Java jumped on the Immutability bandwagon early in the case of Number Wrapper types and String, but rather late in case of List types.
 
Mike Simmons
Master Rancher
Posts: 4002
52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Jesse, it's not just you.  Collections are an area where many of the benefits of Java's type safety have been lost... or at least diminished... by various "optional" methods that may or may not throw UnsupportedOperationException when you try to use them.  Yes, it's a bit annoying and disconcerting.  Not so much that they've felt the need to re-do collections... but other libraries have done things a bit differently, e.g. Eclipse Collections, Trove, as well as other JVM languages like Scala and Kotlin.  I believe these all maintain a separation between mutable and immutable interfaces.  (Strictly there's not really such a thing as an immutable interface, but an interface with no defined mutation operations.)
 
Rob Spoor
Marshal
Posts: 22409
121
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I've read discussions about this. The reason Oracle hasn't gone this way is the explosion of interfaces it would lead to. After all, there's not just "read-only" and 'fully-writable", there are different possible in-between states. For instance:
  • Lists returned by Arrays.asList support changing elements, but not adding or removing them
  • Set and Collection views of Maps support removing elements, but not ading them. That is, if the backing Map is modifiable

  • With these two examples alone you would get five interfaces (:
  • BaseList: only query options
  • AddableList extends BaseList: adds only add options
  • RemovableList extends BaseList: adds only remove options
  • SettableList extends BaseList: adds only replace options
  • ModifiableList extends AddableList, RemovableList, SettableList

  • The problem is, a) that's just for List, and b) I probably missed some combinations.
     
    Campbell Ritchie
    Marshal
    Posts: 73760
    332
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Mike Simmons wrote:. . . "optional" methods . . .

    Yes, I had forgotten about those. I have always been dubious of the distinction between optional and compulsory operations. It is probably more precise to say “operation”, as the API does, than “method” here, but I don't think that obviates any confusion.
    What else would you do? Can you have a ReadOnlyList interface which List extends? Rob has shown how much confusion you can get like that. Do we need all those different combinations of restrictions on altering Lists? I am not familiar with those other frameworks; how do they solve this conundrum?
    And I see we have two conflicting opinions about capacities of Lists; I still think that an immutable List has a capacity, even if that capacity never changes. As it is, it is awkward to change the capacity; you end up with horrible code like this:-At least you can set an ArrayList's capacity in advance:-And with linked lists, the capacity always precisely follows the size, so it is a non‑issue.
     
    Jesse Silverman
    Bartender
    Posts: 1059
    33
    Eclipse IDE Postgres Database C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Rob Spoor wrote:I've read discussions about this. The reason Oracle hasn't gone this way is the explosion of interfaces it would lead to. After all, there's not just "read-only" and 'fully-writable", there are different possible in-between states. For instance:
    ...
    lotsa combinations and permutations
    ...
    The problem is, a) that's just for List, and b) I probably missed some combinations.



    I "fully-agree" and haven't just "read-only" with your analysis.

    What still isn't great is that the programmer had better be aware of all these things, to mix in a long-term ongoing thread of discussion between myself and Campbell, I can't count how many popular tutorials just show for instance, that you can get a List from an array using:



    without ever mentioning once that your List will now have fixed-size semantics, and that any changes to either the Array or the List will be reflected in the other.

    For that matter, the ones that are worse than that (still many) don't even bother to let you know that your:



    Is going to be completely immutable.  Yes, the Javadocs make these things pretty clear, but not everyone reads my signature in posts (or even the Javadocs).

    In general, and, boy, is this getting meta now, when there are a bunch of related concepts that have meaningful practical distinctions between them, I find it easier to talk and reason about them when we have a common vocabulary.  We can waste time by giving names to things there is no point in talking or writing about, sure, but when they are differences and not "distinctions without a difference", I like having names for them.  Then you, I, and the compiler can clearly talk about conditions that apply to each.

    In the following extreme (but not very uncommon) cases, I sort of wish the return type could mention Unmodifiable in it, and that is after processing everything you said:
    public static <T> List<T> unmodifiableList​(List<? extends T> list) // better assign it to a reference who's name suggests "don't try to change this"
    public static <K,​V> Map<K,​V> unmodifiableMap​(Map<? extends K,​? extends V> m) // ditto
    public static <K,​V> SortedMap<K,​V> unmodifiableSortedMap​(SortedMap<K,​? extends V> m) // ditto too
    public static <K,​V> NavigableMap<K,​V> unmodifiableNavigableMap​(NavigableMap<K,​? extends V> m) // ditto too too too


    Same thing with the other methods that return immutables, in each case we "best be careful" or hit "UnsupportedOperationException", which is, as a "RuntimeException" implied not intended to be caught and handled...

    In C++ and others that handle their language similarly, you have a language-level enforcement of "constant reference to the exact same type" that turns any of these "RuntimeException" calls into compile failures.

    In Python, (and others that handle things like that) you have language-level support for treating tuple and list as two different types (explicitly convertible to each other of course).

    In Java, you have "Be Careful or Hit RuntimeException".

    I totally understand the reasoning you outline, and that attacking you would be shooting the messenger.

    The bottom line is, if you know only that your reference is to a List<T>, you'd better be pretty careful about what you try to do with it based on where you got it.

    Combined with sloppy/lazy tutorials that are extremely popular, I can see a lot of RuntimeExceptions being debugged in the future by people who might not have made the same mistakes in Python et al or even C++ et al.  Of course, those latter people would find their coding lives sucked in other ways, no language is perfect.

    I think in addition to the "shades of immutability" issue you illustrated, the fact that Java gradually increasingly celebrated Immutability over a long period of time and with full backward-compatibility, is also a reason.
     
    Jesse Silverman
    Bartender
    Posts: 1059
    33
    Eclipse IDE Postgres Database C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I just wrote all that deep and meta-stuff, hit "Submit" and then started laughing and not knowing why.

    I tried to force myself to put it into words and thought:

    "In a language that is supposed to be friendly to beginners, try not to give them Big Red Buttons to push with notes of (Don't Push That, You Will Blow Up!!)"

    The List interface has quite a number of "Big Red Buttons" that are sometimes a great idea to push, depending on what you are pointing to...other times...
     
    Campbell Ritchie
    Marshal
    Posts: 73760
    332
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:. . . not everyone reads my signature in posts (or even the Javadocs). . . .

    At least you can say they only have themselves to blame. If I post a helpful link to a method in the API, or the JLS “(=Java® Language Specification)“, as long as I get the right link (‍) I expect the readers to open that link.
    At least the method name Collections#unmodifiableList() gives us a hint.
    And you are right; reading the documentation is just as important a part of programming as getting the {...} in the right places.
    I don't think Java® was written as a beginners' language. Yes, I know, “Simple” was one of the original buzzwords, but I suspect it might have been forgotten.
     
    Jesse Silverman
    Bartender
    Posts: 1059
    33
    Eclipse IDE Postgres Database C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Another example of two Very Different Things that look Too Similar Until Runtime:

    jshell> List<String> returnedList = new ArrayList<>();
    returnedList ==> []

    jshell> returnedList.add("Cool");
    $2 ==> true

    jshell> returnedList = Collections.emptyList();
    returnedList ==> []

    jshell> returnedList.add("Not Cool");
    |  Exception java.lang.UnsupportedOperationException
    |        at AbstractList.add (AbstractList.java:153)
    |        at AbstractList.add (AbstractList.java:111)
    |        at (#4:1)


    I more fully appreciate Campbell's earlier complaints about needing to use RTTI to figure out what you can safely do or not do with a List<> reference one has.

    I can write safe code that works with strange unknown List types and will never have to catch a RuntimeException, but either need to do complex instanceof or reflection...

    jshell> returnedList.getClass().getName()
    $5 ==> "java.util.Collections$EmptyList"


    List is perfectly good to represent an ImmutableList, but then that makes me not want to use the same type to represent something that *is* writable.

    In C++ it is considered terrible programming to code to the implementation instead of the interface.  But the language trivially supports read-only access/use for pretty much anything.

    I feel like if I am ever intentionally returning a fully modifiable list from an API I should just use ArrayList as the type in the API, and if I am calling it and thinking of modifying it I should assign it to a reference variable of declared type ArrayList.

    I am painting myself into a corner if the API wants to change to a LinkedList (pretty unlikely), but more seriously then the called code can't make use of:
    jshell> ArrayList<String> empty = Collections.emptyList();
    |  Error:
    |  incompatible types: no instance(s) of type variable(s) T exist so that java.util.List<T> conforms to java.util.ArrayList<java.lang.String>
    |  ArrayList<String> empty = Collections.emptyList();
    |                            ^---------------------^



    Maybe this means:

    1. If you want to return an Immutable List, go ahead and use List.  It allows every call on it to succeed until Runtime, but people will be careful when they see you are returning List because they are smart.
    1b. only ever return Collections.emptyList() from this kind of API, enforced by the compiler as seen above.
    2. If you actually want to return a fully modifiable, expandable, shrinkable List, use ArrayList<> as the return type, or very rarely, LinkedList<>...if you have nothing to put into it, you return new ArrayList<>()
    3. If you are ever intending to return anything in-between a fully modifiable, expandable, shrinkable List and an Immutable one, think at least twice because callers may be confused, then return List because those in-betweens have no easy-to-say names?
     
    Campbell Ritchie
    Marshal
    Posts: 73760
    332
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Have you worled out the difference between Collections#emptyList() and Collections#EMPTY_LIST?
     
    Jesse Silverman
    Bartender
    Posts: 1059
    33
    Eclipse IDE Postgres Database C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:

    Jesse Silverman wrote:. . . not everyone reads my signature in posts (or even the Javadocs). . . .

    At least you can say they only have themselves to blame....
    I don't think Java® was written as a beginners' language. Yes, I know, “Simple” was one of the original buzzwords, but I suspect it might have been forgotten.



    My signature came about because I was not bothering to consult the Javadocs because I "already knew what was in there", but they had changed, because Java is moving forward again even if it was relatively static for a good long while...when I actually looked I was shocked about how much stuff was there that I thought I knew wasn't.  It is both to remind me and everyone else.

    Not going to start a long discussion on whether the Java Guardians are trying to make Java friendly for beginners or not.
    If I was, I would mention JShell, being able to launch a single-file .java program by saying "java MyClass.java" and about five other recent developments that seem targeted at making Java friendlier to beginners.  But experts find JShell, var and some of the others very useful too, so it could too easily turn into a long discussion.

    Most relevant are cute, friendly looking things like cute easy friendly ways to initialize various collection types, but that return you immutable collections.
    People teaching beginners need to not ignore the differences, or we have cute friendly stuff that bites.
     
    Jesse Silverman
    Bartender
    Posts: 1059
    33
    Eclipse IDE Postgres Database C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:Have you worked out the difference between Collections#emptyList() and Collections#EMPTY_LIST?



    "What is type safety, Alex?"
     
    Rob Spoor
    Marshal
    Posts: 22409
    121
    Eclipse IDE Spring VI Editor Chrome Java Windows
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    And that's the only difference:

    Since it's empty and no new elements can be added to it, it doesn't matter what the element type is - there won't be any.
     
    Jesse Silverman
    Bartender
    Posts: 1059
    33
    Eclipse IDE Postgres Database C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Regarding my gripes about getting a List<T> back from:
    public static <T> List<T> unmodifiableList​(List<? extends T> list)

    That doesn't identify itself in source as being unmodifiable...

    I suppose it would be much worse to NOT have this option at all.

    While it would be nifty to have someone's erroneous code that tries to modify what you give back to them not even compile, given the choice between:
    1. The possible rare, sporadic, data or timing-dependent spooky-action-at-a-distance debugging that might occur if they actually succeeded in modifying it,
    2. being forced to proactively copy something very large just to protect against something that quite likely may never happen, or
    3.  have fail-fast-at-runtime behavior, and right at the line that is actually in error if someone tries to mess with it, at that!

    I recall that I have spent much of my professional life dealing with a forced choice between bad options 1 and 2.

    3. might not be as super-awesome as being able to express in code "Don't mess with this!" in a way that the compiler would enforce at compile-time, but hey, it isn't just slightly better than options 1 and 2, it is way better.

    I am watching a video right now where someone actually used one of these Collections.unmodifiableSet/List/Map calls, and they do precisely what I would do, name the reference variable unmodifiableSet.

    If that was the type I would always give the reference a mnemonic name that expressed exactly what data the set contained, rather than falling back on some variant of Hungarian Notation.

    If the coders I was working with on the code were fond of long variable names, maybe unmodifiableSetOfStudents might be a self-documenting name I might consider.

    I just felt like saying that I appreciate the option 3 very much, it can prevent many possible debugging nightmares or performance problems, and there are several ways to reflect "being extra careful" in source code, including reflecting the "Don't try to modify this" in the reference variable name.  I appreciate it in the way someone who never spent a ten hour day tracking down how someone messed with something intended to be read-only ten million lines away from where it was born, and another ten million lines away from where it first showed a symptom, likely never could.

    This isn't perfect, and this particular problem is solved a bit neater in some other languages, but boy is it way, way better than the worst environments I have had to function in.

    TLDR: unmodifiable wrappers for the win?
     
    Ranch Hand
    Posts: 92
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Jesse Silverman wrote:It may be *allowed to*, but unless someone makes this call:

    public void trimToSize()
    Trims the capacity of this ArrayList instance to be the list's current size. An application can use this operation to minimize the storage of an ArrayList instance.

    I don't think it actually does.

    It is too common for sizes to be going up and down, in which case auto-trimToSize() would be very counterproductive.

    We can barely count on the Garbage Collector kicking in when we think it will...



    Given the wording, I think a compliant implementation could simply expand in some power series (say, realloc to 2x capacity when full) and shrink (realloc to 1/2 capacity when < half full). trimToSize() is a leaky abstraction not likely to be generally useful. Kinda like the initialCapacity and loadFactor arguments to HashMap. These are provided for performance reasons but to use it effectively, you need to be confident you understand the underlying data structure and your expected data set. Calling trimToSize() too frequently / creating a HashMap with bad values for initialCapacity and loadFactor can be a source of inefficiency. There is an interview by the creator of Java (sorry no link provided) where he states that allowing these user-tweakable parameters into HashMap was a design mistake.
     
    Jesse Silverman
    Bartender
    Posts: 1059
    33
    Eclipse IDE Postgres Database C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I agree with that last post.
    The design challenges are interesting.
    Giving beginners too many knobs and dials and switches to fiddle with makes the API look terrifying to newcomers, and those extra customizations may actually encourage people to engage in premature optimization and spend more time writing WORSE code.

    But there are a small number of developers with very challenging (and hopefully well known) needs, so allowing them an option of finer control, with reasonable defaults obtained using simpler overloads for everyone else is probably the right way to do things.

    I've noticed a number of places where newer API additions seem to limit one's customization options for the reasons you said.

    But even more places where there is a more complex overload for people who think they really know what they are doing (I hope they are right) or have rarely encountered but pressing needs and simpler ones for those who just want something basic, or want easier to follow, less complex code.

    Right now I am looking at Collectors.  Both here and with Comparators they let us grab any of many "Off-the-Shelf" options that I actually like.  A minor pain to learn, but they have names that tell you exactly what they do and result in short code that people who never met you recognize nearly instantly because they are standard.
     
    He's dead Jim. Grab his tricorder. I'll get his wallet and this tiny ad:
    the value of filler advertising in 2021
    https://coderanch.com/t/730886/filler-advertising
    reply
      Bookmark Topic Watch Topic
    • New Topic