I am just curious about the Lists class implementation of google guava, we have two methods to create List from Array,
Lists.asList(E first, E[] rest)
Lists.asList(E first, E second, E[] rest)
Why do these methods has first and rest separately? cant it be like Arrays.asList implementation of java ?
The only thing I am able to see is the first and second is nullable and rest is not nullable
Can anyone help to understand this ?
Ok, so the job of the Lists.asList() is not exactly to directly convert an array to a list.
Suppose we have an array, and we want to insert an element to it, we can’t do it as the array size is fixed. One solution to this problem is to allocate an new array of +1 the size of the original array and copy all elements from the original array to the new array. This works but it is highly inefficient.
Guava provides an efficent solution to this problem – Guava’s Lists.asList() method returns an unmodifiable list backed by the original array which also contains the specified element.
source: https://www.techiedelight.com/guava-lists-class-java/
So basically, you can use it to just convert an array by giving the first and/or second parameters (depending upon what method you are using) as null, and giving the "rest" parameter as your array.
This will return your array as list, perhaps with null as the first index (and second as well, depending on what you are using)
But if you want, you can use the same methods to get a list with some specific data appended to your array (at first and second index values)
Hope this was helpful!
The main reason these methods exist is to help you when you write a method with a signature like
void foo(Bar first, Bar... rest);
which is something you'd do when you want to allow the user to call the method as if it were a method with just a varargs parameter, but you want to require that it be called with a minimum of one or two arguments (whereas varargs alone would allow them to call it with zero). In the implementation of such a method, you might want to treat those arguments as a single list. Lists.asList does that without doing any array copying:
void foo(Bar first, Bar... rest) {
List<Bar> bars = Lists.asList(first, rest);
// ...
}
Related
I faced a problem yesterday, when I was writing my homework. I finished the homework, but I still don't really understand why my code works. I had to write a sort function that takes an varargs of any comparable generic object as an argument and return the argument. The problem was that I had to return an array of sorted objects. So I had to learn more about varargs lists and arrays.
The function was defined like this.
public <T extends Comparable<T>> T[] stableSort(T ... items)
and inside the function I made a list, which I would sort and do all the work on.
List<T> list = new ArrayList<T>(Arrays.asList(items));
and at the end of the function I was returning list toArray so that it matched the output type T[].
list.toArray(items.clone());
My question is since I already made the list from the varargs, why do I have to do items.clone() inside the toArray function. That seemed like doing two same things to me. I thought arrays.asList() would clone the values of array to list and I don't get why am I doing it again at the end of the code in toArray(). I know that this was the correct way to write it, because I finished the homework yesterday and found out this way from forums of the class, but I still don't understand why.
EDIT
The task required me to create a new array with sorted files and return it instead. Due to Type Erasure, it is not possible to instantiate an array of a generic type without a reference to a class that fits the generic. However, the varargs array has type T, so I should have cloned an array of a type which fits the generic constraints. Which I didn't know how to do in time. So I decided to use list to make my time easier till the deadline.
My question is since I already made the list from the varargs, why do I have to do items.clone()
You are right. Unfortunately, the compiler will be unable to determine the type of the array if you simply use the toArray() method. You should get a compilation error saying Cannot convert from Object[] to T[]. The call to item.clone() is required to assist the compiler in type-inference. An alternate approach would be to say return (T[])list.toArray
That said, I would not recommend either of the approaches. It doesn't really make sense to convert an array to a list and convert it back to an array in the first place. I don't see any significant take-aways that you would even understand from this code.
It seems to me there are a few questions here, that may have come together to create some confusion as to why what needs to be done.
I thought arrays.asList() would clone the values of array to list and I don't get why am I doing it again at the end of the code in toArray().
This is probably just the way it is typed, but it should be made clear that you don't clone the objects in the array, but only make a new List with the references to the objects in the array. The objects themselves will be the same ones in the array as in the List. I believe that is probably what you meant, but terminology can be tricky here.
I thought arrays.asList() would clone the values of array to list...
Not really. Using Arrays.asList(T[] items) will provide a view onto the array items that implements the java.util.List interface. This is a fixed-size list. You can't add to it. Changes to it, such as replacing an element or sorting in-place, will pass through to the underlying array. So if you do this
List<T> l = Arrays.asList(T[] items);
l.set(0, null);
... you've just set the element at index 0 of the actual array items to null.
The part of your code where you do this
List<T> list = new ArrayList<T>(Arrays.asList(items));
could be written as this:
List<T> temp = Arrays.asList(items);
List<T> list = new ArrayList<T>(temp);
The first line is the "view", the second line will effectively create a new java.util.ArrayList and fill it with the values of the view in the order they are returned in by their iterator (which is just the order in the array). So any changes to list that you make now don't change array items, but keep in mind that it's still just a list of references. items and list are referencing the same objects, just with their own order.
My question is since I already made the list from the varargs, why do I have to do items.clone() inside the toArray function.
There could be two reasons here. The first is as CKing said in his/her answer. Because of type erasure and the way arrays are implemented in Java (there are separate array types depending on whether it's an array of primitives or references) the JVM would not know what type of array to create if you just called toArray() on the list, which is why that method has a return type of Object[]. So in order to get an array of a specific type, you must provide an array to the method that can be used at run-time to determine the type from. This is a piece of the Java API where the fact that generics work via type-erasure, aren't retained at run-time and the particular way in which arrays work all come together to surprise the developer. A bit of abstraction is leaking there.
But there might be a second reason. If you go check the toArray(T[] a) method in the Java API, you'll notice this part:
If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.
Suppose some code by another dev is using your stableSort method like this:
T[] items;
// items is created and filled...
T[] sortedItems = stableSort(items);
If you didn't do the clone, what would happen in your code would be this:
List<T> list = new ArrayList<T>(Arrays.asList(items));
// List is now a new ArrayList with the same elements as items
// Do some things with list, such as sorting
T[] result = list.toArray(items);
// Seeing how the list would fit in items, since it has the same number of elements,
// result IS in fact items
So now the caller of your code gets sortedItems back, but that array is the same array as the one he passed in, namely items. You see, varargs are nothing more than syntactic sugar for a method with an array argument, and are implemented as such. Perhaps the caller didn't expect the array he passed in as an argument to be changed, and might still need the array with the original order. Doing a clone first will avoid that and makes the effect of the method less surprising. Good documentation on your methods is crucial in situations like this.
It's possible that code testing your assignment's implementation wants a different array back, and it's an actual acquirement that your method adheres to that contract.
EDIT:
Actually, your code could be much simpler. You'll achieve the same with:
T[] copy = items.clone();
Arrays.sort(copy);
return copy;
But your assignment might have been to actually implement a sorting algorithm yourself, so this point may be moot.
You need to use this:
List<T> list = new ArrayList<T>(Arrays.asList(items));
when you want to do an inline declaration.
For example:
List<String> list = new ArrayList<String>(Arrays.asList("aaa", "bbb", "ccc"));
By the way, you didn't have to use return list.toArray(items.clone()); You could have used, for example, return list.toArray(Arrays.copyOf(items, 0));, where you are passing to list.toArray() an empty array that contains none of the arguments from items.
The whole point of passing an argument to the version of list.toArray() that takes an argument, is to provide an array object whose actual runtime class is the actual runtime class of the array object it wants to return. This could have been achieved with items.clone(), or with items itself (though that would cause list.toArray() to write the resulting elements into the original array pointed to by items which you may not want to happen), or with, as I showed above, an empty array that has the same runtime class.
By the way, the need to pass the argument to list.toArray() is not a generics type issue at all. Even if you had written this with pre-generics Java, you would have had to do the same thing. This is because the version of List::toArray() that took no arguments always returns an array object whose actual runtime class is Object[], as the List doesn't know at runtime what its component type is. To have it return an array object whose actual runtime class is something different, you had to give it an example array object of the right runtime class to follow. That's why pre-generics Java also had the version of List::toArray() that took one argument; even though in pre-generics, both methods were declared to return Object[], they are different as the actual runtime class returned is different.
i'm currently trying to add items to an generic "array" of arraylists but for some reason i keep getting a null pointer exception. The Structure is initialised and both my array index reference and the reference to the object i'm passing in are both visible within the body of code right before the exception occurs. I'm almost sure its down to the way i either declared the data structure or my way im trying to add it in. Any advice would be appreciated. Thanks in advance
ArrayList<Site>[] group = (ArrayList<Site>[])new ArrayList[entranceSites.size()];
group[i].add(sIndex(path));
sIndex is a function I'm using to convert integers to graph sites and the object is not null when I'm passing it in so i'm sure its not the problem. I is initialised and also visible to the program.
new ArrayList[entranceSites.size()];
does not actually initialize the array elements with any constructor. The array will be filled with enteranceSites.size() null elements.
You will need to iterate through the array and actually construct ArrayList objects.
Here's how you can set each element of the array to a new ArrayList using Java 8:
Arrays.setAll(group, n -> new ArrayList<Site>());
(The second argument is a function of n, the array index, but n isn't actually used. You still need to include it.)
You have allocated an array of ArrayLists, but you have not allocated any actual ArrayLists inside that array. The array initially contains all null references. So your invocation to add is on a null reference and thus causes the exception. If you say:
group[i] = new ArrayList<Site>();
Before you call add it will work.
Note that it is generally a bad idea to mix primitive arrays and Java collections, and if you are new to Java, then you should probably stick to collections since they are going to be easier to work with.
You should also be aware that the cast you are making (ArrayList<Site>[]) is unchecked and will almost certainly generate a warning assuming you have warnings enabled, which you should be enabling warnings as a beginner. This is another reason why it is not a good idea to mix generics with primitive arrays.
By the looks of your code fragment, my guess is that you failed to initialize the ArrayList<Site> element being added to the array; thus, failing when calling the List.add() method. The array itself is properly initialized, but you are trying to add a Site to an ArrayList that has not been initialized properly.
For this to work, you must create your ArrayList<Site> object. Once your lists are properly instantiated, you can add them to the array. You can add Site objects when creating the list or after you add them to the array. It does not matter when because the space in memory will be already allocated. Suppose a company has sites in many states, for argument sake, New York. All of the sites in that geographical location will be added to the NY list of sites:
ArrayList<Site> nySites = new ArrayList<Site>();
Site site1 = new Site();
group[0] = nySites;
group[0].add(site1); // Now you can call the add() method
Given an IP Address Range ( a.b.c.d - a.b.c.e) i would like a method to return the ip address's in an array list between the range.
Option 1 :
public static int getIPAddressesFromRange(String rangeStr, List list ) ;
return value is count and the input list would be populated with list of IP's the range has
Option 2:
public static List getIPAddressesFromRange(String rangeStr)
return value is the list of ip addresses'
My Option is 2, but that is intuition, not able to support my argument though.
Edit: Is there any design principle the option 1 is violation ?
I'd say
public static List<String> getIPAddressesFromRange(String rangeStr)
if you decide to represent IP addresses as strings.
Arguments against #1:
The caller needs to construct the list in advance
It is not straightforward what the return value is unless you document it
The method mutates one of its arguments, which is not in general forbidden, but it is best to avoid surprising the user of your API (especially if they are prone not to read the documentation)
Passing in a null value accidentally for the list parameter will result in a NullPointerException.
You can always get the length of the list from the list itself if you really care about it.
Prefer the option 2 to the option 1.
The list contains its count anyway, so there is no need to return two values (the count and the list).
Also, since you know the type of the list, you can use generics: List<String>.
Finally, you might also consider taking two arguments: the beginning and the end of the range.
Why do you want to return count in first method? You can fetch the number of IP's from List itself.
Second method should be the preferred one
Your second option is best because the first option has two problems:
It's redundant. If a List is returned, you can use its size() method to get that count, so you gain nothing by returning the count.
The list must be validated and in some cases the method outright cannot perform its work. If the caller passes null, there is danger of a NullPointerException being thrown if the code was not written carefully. Also in that case, reassigning the parameter to point to a new list will not be observed by the caller, so your only remotely sane option is to throw a clear exception. With the second option, you have full control of the list until it is returned to the caller.
Option two is probably better, since it is clear for any reader what is the method returning.
Method 1 might cause future coders to spend time thinking what is this parameter (unless it is properly documented), while method 2 is realy straight forward.
Option two also makes it more neat if you later need to iterate on the retrieved list, no need for temporary variables:
for (Object o : getIPAddressesFromRange(String rangeStr)) { ... }
You should also prefer using the generic type List<> and not the raw type.
Stuff in, stuff out. That's what your Option 2 does.
Option 1 mutates its input argument and returns redundant value (count, which can be got from the list).
Another thing is, perhaps a range of IP addresses would be described better by some other type than a String.
IMO method signature suggests it will return a list of ip addresses from range, not how many addresses are in this range, hence I'm also for option 2.
I think the 2nd one is better :
The count is the size of the list
You don't have to give a list to the function
Less null pointer exception risk
Your intuition is mine also, it is better to let getIPAddressesFromRange use its preferred implementation of List and avoid someone to give you an already populated list.
My opinion is that the second method signature is generally the best one as the first one will exposes your list object to concurrent modification. Thus, at the end of your method, it may hold less, more, other objects than expected.
It depends on whether you want to fill pre-created lists, or create new ones.
For example: You could do multiple calls to your function using the same List object to save some memory.
Or: To compare multiple lists, you may want to return a new List for each call.
I would go with Option 2.
If fooService.getFoos() returns List<Foo>.
then you can write this:
List<Foo> fooList = fooService.getFoos();
or this:
List<Foo> fooList = new ArrayList(fooService.getFoos());
Is there any significant difference in the resulting fooList between these two approaches?
Yes - you are creating a completely new List, containing the elements of the original one. You are duplicating the collection in memory, and iterating it from start to end. You are also not using instance provided by the service, and you can't modify the original. And finally, you've omitted the generics declaration in the 2nd snippet.
So use the first option.
Update: you indicated you are not allowed to modify the original list. This is actually a problem of fooService, not of its clients. If the service is also in your control, return Collections.unmodifiableList(originalList) - thus clients won't be able to perform modification operations (on attempt an exception will be thrown)
The second isn't really a good idea because you omit the generic part.
But the main problem is the unnecessary code which will be called. You can look at the ArrayList code source, and you'll see all the operations used in the constructor. If you only need a List, and fooService.getFoos() returns a valid List, you should stick with it.
The result of those two statement will be more or less the same unless:
later you check if your list is an instance of ArrayList and cast it, but let's face it, you would have declared ArrayList<Foo> if it was the case.
the list returned by fooService.getFoos() shouldn't be modified (for any reason) but you still want modify elements in the List on your side (without affecting the original list).
Resources :
grepcode - ArrayList
I'd stick with the first one just because it reads lots easier and makes much more sense than the second one.
In the second statement it returns only of List type. If you are sure of method returning of same type den you can use firs type.
If I have a class that needs to return an array of strings of variable dimension (and that dimension could only be determined upon running some method of the class), how do I declare the dynamic array in my class' constructor?
If the question wasn't clear enough,
in php we could simply declare an array of strings as $my_string_array = array();
and add elements to it by $my_string_array[] = "New value";
What is the above code equivalent then in java?
You will want to look into the java.util package, specifically the ArrayList class. It has methods such as .add() .remove() .indexof() .contains() .toArray(), and more.
Plain java arrays (ie String[] strings) cannot be resized dynamically; when you're out of room but you still want to add elements to your array, you need to create a bigger one and copy the existing array into its first n positions.
Fortunately, there are java.util.List implementations that do this work for you. Both java.util.ArrayList and java.util.Vector are implemented using arrays.
But then, do you really care if the strings happen to be stored internally in an array, or do you just need a collection that will let you keep adding items without worrying about running out of room? If the latter, then you can pick any of the several general purpose List implementations out there. Most of the time the choices are:
ArrayList - basic array based implementation, not synchronized
Vector - synchronized, array based implementation
LinkedList - Doubly linked list implementation, faster for inserting items in the middle of a list
Do you expect your list to have duplicate items? If duplicate items should never exist for your use case, then you should prefer a java.util.Set. Sets are guaranteed to not contain duplicate items. A good general-purpose set implementation is java.util.HashSet.
Answer to follow-up question
To access strings using an index similar to $my_string_array["property"], you need to put them in a Map<String, String>, also in the java.util package. A good general-purpose map implementation is HashMap.
Once you've created your map,
Use map.put("key", "string") to add strings
Use map.get("key") to access a string by its key.
Note that java.util.Map cannot contain duplicate keys. If you call put consecutively with the same key, only the value set in the latest call will remain, the earlier ones will be lost. But I'd guess this is also the behavior for PHP associative arrays, so it shouldn't be a surprise.
Create a List instead.
List<String> l = new LinkedList<String>();
l.add("foo");
l.add("bar");
No dynamic array in java, length of array is fixed.
Similar structure is ArrayList, a real array is implemented underlying it.
See the name ArrayList :)