Recently I came across an API and it was using some Parameter
void doSomething(final String... olah) {
}
I never have seen something like that.
I have a List<String> now and I want to call that function with my list of string. How can I achieve that?
Welcome to modern Java. That syntax is called varargs in Java.
http://docs.oracle.com/javase/1.5.0/docs/guide/language/varargs.html
You can think of it like
void doSomething(final String[] olaf) {
}
The only difference is that as the name suggests, it is Variable Length Arguments. You can invoke it with 0 to any number or arguments. Thus, doSomething("foo"), doSomething("foo", "bar"), doSomething("foo", "bar", "baz") are all supported.
In order to invoke this method with a List argument though, you'll have to first convert the list into a String[].
Something like this will do:
List<String> myList; // Hope you're acquainted with generics?
doSomething(myList.toArray(new String[myList.size()]));
String... is nothing but String[]. So just loop over list and create an array of String and pass that array or more easy way to use .toArray(new String[collection.size()]) method of Collection class.
String... is the same as String[].
You want to call something like:
String[] listArr = list.toArray( new String[ list.size() ] );
doSomething( listArr );
Use .toArray(new String[0]). The toArray() method will turn your list of strings (java.util.List<String>) into an array of String objects.
The '...' syntax is a mechanism to allow a variable number of parameters. You can pass either something like doSomething("abc", "def", "ghi") or doSomething("abc") or doSomething(new String[] { "abc", "def", "ghi" }). The function will see them all as arrays (respectively as length 3, 1 and 3).
See the following to convert List of String to String Array
List<String> listOfString = new ArrayList<String>();
listOfString.add("sunil");
listOfString.add("sahoo");
String[] strResult=new String[listOfString.size()];
strResult = listOfString.toArray(strResult);
Related
I have a method that takes in a nested ArrayList of Strings as a parameter as given below:
public static String methodName(ArrayList<ArrayList<String>> param1,ArrayList<Integer> param2){
...some logic
}
I want to test this method with an input
input :"param1"= [
["HTML", "C#"],
["C#", "Python"],
["Python", "HTML"]
]
"param2"= [0, 0, 1]
I searched online and found this : How do I declare a 2D String arraylist?
Here is what I tried:
public static void main(String[] args){
ArrayList<ArrayList<String>> param1 = ...what should I put here?
List<List<String>> list = new ArrayList<List<String>>();
List<String> input = new ArrayList<String>();
input.add({"HTML", "C#"});//does not compile ... array initializer not allowed here
input.add({"C#", "Python"});
input.add({"Python", "HTML"});
How would you add the input to the nested ArrayList?
Would appreciate any help...
Since param1 is declared as ArrayList<ArrayList<...>>, you have no choice, but to create new ArrayList(...) explicitly for nested elements:
ArrayList<ArrayList<String>> param1 = new ArrayList<>(Arrays.asList(
new ArrayList<>(Arrays.asList("HTML", "C#")),
new ArrayList<>(Arrays.asList("C#", "Python"))
));
But, as the general rule, try to write functions that accept as permissive parameter types as possible. In this case, if you can replace ArrayList<ArrayList<String>> in methodName with List<List<String>>, that would be much better for the users of your function.
For example, they would be able to create param1 as such:
List<List<String>> param1 = Arrays.asList(
Arrays.asList("HTML", "C#"),
Arrays.asList("C#", "Python")
);
UPD as #DavidConrad pointed out, you can use List.of(...) instead of Arrays.asList(...) since Java 9.
You could do
List<List<String>> list = new ArrayList<>(); // No need for List<String> again, just the one diamond operator is enough here.
list.add(List.of("HTML", "C#"));
...
I want to pass an array and a single object to method which has varargs.
However, the most obvious solution doesn't seem to work:
public static final String[] ARRAY_ARGS = {"first argument", "second argument"};
public static String additionalArgument = "additional argument";
public static void foo(String... args) {
// ...
}
public static void main(String[] args) {
foo(ARRAY_ARGS,additionalArgument); // error! won't compile
}
How can I fix this?
A variable argument is equivalent to an array. Hence, the compiler does not accept an array and a string. One solution is to create an array from the original with the additional string added to it:
List<String> originalList = Arrays.asList(ARRAY_ARGS);
List<String> list = new ArrayList<String>(originalList);
Collections.copy(list, originalList);
list.add(additionalArgument);
foo(list.toArray(new String[list.size()]));
The Collections.copy is needed because adding to the list returned by Arrays.asList throws a java.lang.UnsupportedOperationException as the latter returns a list that extends AbstractList which does not support adding elements.
Another solution is to create a new array and individually add the elements:
String[] arr = new String[3];
arr[0] = ARRAY_ARGS[0];
arr[1] = ARRAY_ARGS[1];
arr[2] = additionalArgument;
foo(arr);
Or you can simply call foo with the individual parameters:
foo(ARRAY_ARGS[0], ARRAY_ARGS[1], additionalArgument);
You can do this, if the first argument to your method is the single string, followed by the varargs:
public static void foo(String a, String... args) {
System.out.println("a = " + a);
System.out.println("args = " + Arrays.toString(args));
}
Then calling foo("a", "b", "c") prints
a = a
args = [b, c]
The argument String ... args is shorthand for String[]. Therefore you get an error when you are calling foo(args,additionalargument) since the method declaration of foo is foo(String[] str) instead of foo(String[],String).
If you must distinguish between the array and the single string, concatenate the array values in a format you can "recognize", pass the "array-string" and the single string as parameters to the main method, and then parse the "array-string" in the relevant (foo) method.
A Java 8 approach to solve this problem is the following oneliner:
foo(Stream.concat(Arrays.stream(ARRAY_ARGS), Stream.of(additionalArgument)).toArray(String[]::new));
This creates a new String[] that can be passed to foo. Furthermore, foo must be made static, otherwise the code will not compile.
The only parameters that you can pass to the foo-method is either a String array or a bunch of individual Strings.
Simple yet elegant solution using Apache Commons Lang library:
foo(ArrayUtils.addAll(ARRAY_ARGS,additionalArgument));
First I have two strings.
String name = "my name is";
String address = "my address is";
Then I want to split the 'name' string value from the space character and add to a list.
List word_list = new ArrayList();
word_list.add(Arrays.asList(name.split(" ")));
Then similarly I want to split the 'address' string value and add to the same 'word_list'.
word_list.add(Arrays.asList(address.split(" ")));
From this finally what I get is,
[[my, name, is], [my, address, is]]
But what I want is,
[my, name, is, my, address, is]
Is there any shorter method other than writing a loop to solve this problem?
You need addAll :
word_list.addAll(Arrays.asList(name.split(" ")));
word_list.addAll(Arrays.asList(address.split(" ")));
add treats the argument as a single element to be added to the list. addAll expects a Collection of elements to be added to the list.
BTW, if you defined your list as List<String> word_list = new ArrayList<String>();, the compiler would have prevented you from calling add with a List as an argument.
There's a function that does what you want:
public String[] mergeArrays(String[] mainArray, String[] addArray) {
String[] finalArray = new String[mainArray.length + addArray.length];
System.arraycopy(mainArray, 0, finalArray, 0, mainArray.length);
System.arraycopy(addArray, 0, finalArray, mainArray.length, addArray.length);
return finalArray;
}
By the way, Apache Commons Lang Lib has a one-line function to do that:
[ArrayUtils.addAll(T\[\], T...)][1]
There are two issues in your code.
Your word_list is not generic. You should declare and initialize it as:
List<String> word_list = new ArrayList<String>();
... or optionally use the diamond syntax from Java 7 on:
List<String> word_list = new ArrayList<>();
This way, you ensure type safety, as in, compile-time checks on what gets in (we want Strings).
Since your List is not generic, it will take any Object. In this case, you are adding a List<String>, not a String
Instead, use word_list.addAll(Arrays.asList(name.split(" ")))
I need this code, but i get this error:
Ljava.lang.Object; cannot be cast to java.lang.String
public Object[] getAllKeys (){
return keys.toArray(new Object[keys.size()]);
}
public String[] getNames (){
return ((String[])super.getAllKeys()); <- Error here. Can't cast, why?
}
The type of the array is Object[] so it cannot know that it contains only Strings. It is quite possible to add a non-String object to that array. As a result the cast is not allowed.
You can return Object[] and then cast each of the objects within that array to string. i.e. (String)arr[0] or you can create a new String[] array and copy all the elements over before returning it.
toArray() returns an array of Objects. If you want to create an array of Strings out of it, you will have to do it yourself. For example,
Object [] objects = super.getAllKeys();
int size = objects.size();
String [] strings = new String[size];
for (int i = 0; i < size; i++)
strings[i] = objects[i].toString();
or something similar... Hope this is useful.
Every String is an Object. Every Object is NOT a String.
You cannot do the cast, because even though Object is a base class of String, their array classes Object[] and String[] classes are unrelated.
You can fix this problem by introducing an additional method that allows taking a typed array:
public Object[] getAllKeys (){
return getAllKeys(new Object[keys.size()]);
}
// Depending on your design, you may want to make this method protected
public <T> T[] getAllKeys(T[] array){
return keys.toArray(array);
}
...
public String[] getNames (){
return super.getAllKeys(new String[keys.size()]);
}
This code takes advantage of the other overload of toArray, which accepts a typed array as an argument.
This cannot be done implicitly since the runtime cannot know that the elements in Object[] are all String types.
If you don't want to code a loop yourself, then one way to coerce is to use
String[] myStringArray = Arrays.asList(keys).toArray(new String[keys.length]);
I think that this will happen without any string copies being taken: asList() binds to the existing array data and toArray uses generics which are removed at runtime anyway due to type erasure. So this will be faster than using toString() etc. Don't forget to deal with any exceptions though.
Try the following snippet
Object[] obj = {"Red","Green","Yellow"};
String[] strArray = (String[]) obj; // Casting from Object[] to String[]
I'd like a convenience method to take a set of parameters and return an array, much like Arrays.asList(T... items) will take a set of parameters and return a List<T> of those items.
It's easy enough to write one, but does one already exist in java?
UPDATE
My bad! I didn't realize the question was so unclear. Your questions have forced me to realize that the question isn't quite the question I thought it was.
I have several calls like the following that place various key/values into a Map:
put( Key.get(A.class), new Key[] { Key.get(X.class), Key.get(Y.class), Key.get(Z.class)});
... where the map is of type Map<Key<? extends Foo>,Key<? extends Foo>[]>
I was looking for a typesafe and succinct way to execute the above statement, and I thought that something like the following would work:
put( Key.get(A.class), toArray( Key.get(X.class), Key.get(Y.class), Key.get(Z.class)));
... where toArray() is defined as something like
private static <T> T[] toArray( T... t ) {
return t;
}
However, it turns out that this solution is not typesafe itself, and thus it's really not much more succinct than just creating a new array manually using new. This was the first cause of my misunderstanding.
I thought that I could get typesafety by using a List instead of an array and then using Arrays.asList() to populate the values of the list, but it turns out that that's not typesafe either. This was the second cause of my misunderstanding. I thought that Arrays.asList() would make this statement more succinct than it actually does, and thus I was looking for something that would do the same for me for arrays.
So I suppose the question is really - Is there a succinct way to get typesafety in the above situation?
Arrays already have such a shortcut syntax:
String[] strArray = {"one", "two", "three"};
In response to your update:
As it seems like you discovered, arrays of parameterized types can never be type-safe. This is one of several limitations due to the fact that arrays and generics are like oil and water.
A varargs method such as Arrays.asList isn't spared from this limitation since varargs works by implicitly creating an array of the comma delimited arguments. In order to have type-safety, you'll need to avoid any solution involving arrays, including varargs.
First, I recommend you change your map's type to hold Lists instead of arrays:
Map<Key<? extends Foo>, List<Key<? extends Foo>>> map = new HashMap<>();
And then build a List before putting it in the Map:
List<Key<? extends Foo>> lst = new ArrayList<>();
lst.add(Key.get(X.class));
lst.add(Key.get(Y.class));
lst.add(Key.get(Z.class));
map.put(Key.get(A.class), lst);
If you want it all in one statement, it's going to be trickier without varargs. Guava's ImmutableList exposes the of factory methods taking up to 12 elements before falling back to varargs. If the Lists in the map aren't going to be modified later, you could store ImmutableList<Key<? extends Foo>> and use:
map.put(
Key.get(A.class),
ImmutableList.of(Key.get(X.class), Key.get(Y.class), Key.get(Z.class))
);
In fact you could still take advantage of those factory methods even if the List needs to be modifiable by copying the returned ImmutableList:
map.put(
Key.get(A.class),
Lists.newArrayList(ImmutableList.of(
Key.get(X.class),
Key.get(Y.class),
Key.get(Z.class)
))
);
But then you're introducing overhead just for the sake of style.
Side note: if you do happen to be using Guava, you might look at using a Multimap instead of a Map of Lists.
What would such a method do that the constructor for the array doesn't already?
String foo = "FOO";
String bar = "BAR";
String[] strings = new String[]{foo, bar};
How about
public static class ToArray {
public static <T> T[] toArray(T... items) {
return items;
}
}
public void example() {
String[] strings = ToArray.toArray("fred", "bob");
}
?
In order to get a Set and return a List you could use an ArrayList:
Set<String> set = new HashSet<String>();
set.add("str1");
set.add("str2");
set.add("str3");
List<String> list = new ArrayList<String>(set);
If you want an array from a list you could do something like:
myList.toArray();
// or even
String[] myStringArray = myList.toArray(new String[]);
Is this what you want? This will return an array because Java treats the varargs construct as an array. I don't know how to genericize it though.
public Object argsToArray(Object... args) {
return args;
}