Equivalent to Arrays.asList() but for arrays? - java

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;
}

Related

Method to flatten generic array

I've been trying to write a method that will flatten a generic array if it is nested.
private static <T> List<T> flatten(T[] in) {
List<T> result = new ArrayList<>();
for (T e : in) {
if (e.getClass().isArray()) {
result.addAll(Arrays.asList(e)); ## Issue is here.
} else {
result.add(e);
}
}
return result;
}
This code does not cause any errors but also does not work. When e is not an array, things work as expected... a list is populated with the elements of in and returned.
However when e.getClass().isArray() == true, the elements of e are not added. Rather the original array is added so I end up with a list of arrays.
My use case here is that I have a method that is being passed generics T[] someArray
public <T> void doSomeStuff(T[] someArray) {
Set<T> unique = Sets.newHashSet(someArray)
... do some stuff with the unique values ...
}
The input someArray may either be nested or not (i.e. T itself may be an array, resulting in T[][]). I want to determine the unique elements contained in the input, whether or not it is nested. Passing the input someArray to a set only works if it's not nested, hence I'm trying to flatten.
So my question is, how can I do this and why is my method above not working? Thanks in advance for the edcuation.
Your code can't work. The generics just don't line up.
Let's say you have an array that is a combination of strings and arrays of strings. That cannot possibly be a T[] unless T is object, which isn't what you want (as that would mean you get a List<Object>. After all, If T is String, then your input array, which is defined as T[] in, is a String[] in, which cannot contain string arrays. After all, a String[] is not a subtype of String, for obvious reasons.
It is impossible to describe in terms of generics the concept of 'an array of Strings, or an array of arrays of Strings, or an array of arrays of arrays of Strings, and so forth'. So, generics have no place here. If you want that, all you can 'type' is 'an array whose component type is unknown and hybrid anyway', which is Object[] in java (this is co/contra-variance wise broken, but this is just part of the java spec: Variance on arrays is incorrect, known problem and not fixable).
This gets you a secondary issue: Generics are erased, and in that model you don't have an actual type to work with. In fact, because it is impossible to use generics to tell the compiler to do some type checking on the input array, there is nothing the compiler can do for you, so any type checking you want (and you clearly want that, you don't want to return a List of who knows what this is), will have to be done at runtime.
Unfortunately, it is impossible to do that, too - you can't check if at runtime if some object is, say, a Map<String, Integer>.
So, what you want is impossible.
It becomes possible if you're okay with this method being only able to do the job for reified types. That is, types that don't contain any <> themselves. So, if you want to take 'an array that contains a combination of "Map of string to integer" and "arrays of Maps of string to integer"', this method will not be able to do that and it is in fact completely impossible to do such a thing in java. But if you're okay with, say, "An array containing a combination of strings and arrays of strings" and want to turn that into a flattened-out list of strings, okay, that's possible.
It's complicated, though:
public <T> List<T> flattenArray(Class<T> type, Object[] in) {
if (type.isArray()) throw new IllegalArgumentException();
var out = new ArrayList<T>();
flattenArray0(type, in, out);
return out;
}
private <T> void flattenArray0(Class<T> type, Object[] in, List<T> out) {
for (Object a : in) {
if (a == null) {
out.add(null);
} else if (a.getClass().isArray()) {
flattenArray0(type, (Object[]) a, out);
} else {
out.add(type.cast(a));
}
}
}
In action:
Object[] test = new Object[3];
test[0] = "Hello";
test[1] = new String[] {"Foo", "Bar"};
Object[] threeDeep = new Object[2];
test[2] = threeDeep;
threeDeep[0] = "Goodbye";
threeDeep[1] = new String[] {"Baz"};
List<String> result = flattenArray(String.class, test);
System.out.println(result);
should print: ["Hello", "Foo", "Bar", "Goodbye", "Baz"].

Casting Java ArrayList<Custom Class> to ArrayList<Object>

I am attempting to convert an ArrayList of class SomeClass to an ArrayList of class Object. This new ArrayList of Object will then be passed to a function. I currently have done the following:
// convert ArrayList<SomeClass> to generic ArrayList<Object>
Object[] objectArray = someClassList.toArray();
ArrayList<Object> objects = new ArrayList<Object>();
for (int i = 0; i < objectArray.length; i++) {
objects.add(objectArray[i]);
}
someFunction(objects);
public void someFunction(ArrayList<Object> objects) {
// do something with objects
}
Is there a more efficient or "standard" way of doing this? Is what I am doing "wrong" in the first place?
The purpose of converting it to ArrayList of class Object is that I have created an external library to process ArrayList of generic Objects.
If you are able to change the function's signature to taking an ArrayList<? extends Object> objects or an ArrayList<?> objects (or even better, List instead of ArrayList), you will be able to pass your ArrayList<SomeClass> directly.
The reason an ArrayList<SomeClass> is not an ArrayList<Object> is that an ArrayList<Object> would accept that you add() any kind of Object into it, which is not something you can do with an ArrayList<SomeClass>. On the other hand, an ArrayList<? extends Object> will allow you to retrieve elements from the list, but not add elements, so ArrayList<SomeClass> can safely be assigned to it.
Since you created the external library, I think it would be easier to modify the function signature to accept lists of any type. This can be accomplished using the unbounded wildcard ?:
public static void someFunction(List<?> objects) {
// whatever
}
Then you don't need to make any conversions to call it:
public static void main(String[] args) {
List<String> words = new ArrayList<>();
someFunction(words);
}
Also, unless you have a good reason not to, it would be better to accept any List in someFunction instead of limiting your input to ArrayLists. This makes your code more flexible and easier to change in the future.
A simple way to convert a List<SubFoo> to a List<Foo> is to use Collections.unmodifiableList(listOfSubFoos), which is perfectly type-safe and actually enforces that you can't do anything bad with it (like adding a DifferentSubFoo).
It is possible to transform the type parameters of a type in arbitrary ways with two casts:
ArrayList<SomeClass> l1 = ...;
ArrayList<Object> l2 = (ArrayList<Object>) (Object) l1;
But, as Aasmund Eldhuset also says in his answer: This is probably not a good idea! It is better to give a more suitable type to l2 instead, like ArrayList<?>.
This code gives you an compile warning saying Type safetyThat: Unchecked cast from Object to ArrayList<Object> for a reason. If for example a String is added to l2 and then someone reads l1 and expects a SomeClass they will get a very unexpected ClassCastException.

How to convert List<String> to List<Integer>?

I am getting String of constants in List<String>. I need List<Integer>. By the basic way,
I will iterate and cast into Integer.
Is there any better solution?
Nope, there's no other way.
But casting is not possible in this case, you need to do use Integer.parseInt(stringValue).
List<String> listStrings = ...
List<Integer> listIntegers = new ArrayList<Integer>(listStrings.size());
for(String current:listStrings){
listIntegers.add(Integer.parseInt(current));
}
There is a way to do this.
You could use the Adapter Pattern and create a class which implements List<Integer>, but internally accesses your List<String> casting the values between Integer and String. As long as you fulfill all the contracts, any API which requires a List<Integer> will be able to work with this class just like with a native List<Integer>.
This might seem cumbersome and inefficient, but when you need to pass a List<Integer> to an API which only accesses some values of the list, it can be more efficient to cast some of them on-demand ("lazy evaluation") instead of casting all of them. It also saves memory, because you won't have both the string- and the integer representation of your whole list in memory at the same time.
for(String str : listOfString) {
listOfInteger.add(Integer.valueOf(str));
}
There's no direct way to do this.
Look at Google Guava's Lists class, you can do something as:
List<Integer> result = Lists.transform(stringList, new Function<String, Integer>() {
#Override
public Integer apply(#Nullable String input) {
return Integer.parsInt(input)
}
});
As far as I remember, it will transform each item lazily.
With Java 8 there is some new solution:
List<Integer> listOfIntegers = listOfStrings.stream().map(Integer::valueOf).collect(Collectors.toList());
There is not an explicit and direct way to change the parametrised type of a collection. You should create another one and transfer the data while modifying it.
The other way around has already been answered.
Looks like looping is the only option. If you do not 'want' to loop yourself, there are some classes which can be used (e.g. from Apache Commons).
Using guava:
List<String> strings = Lists.newArrayList("1", "2", "3");
List<Integer> ints = Lists.transform(strings, new Function<String, Integer>(){
public Integer apply(String input) {
return Integer.valueOf(input);
}
});
It's not just a matter of casting, because String and Integer are fundamentally different objects.
You must loop through the list and parse each one int a new Integer:
List<Integer> intList = new ArrayList<Integer>();
for(String numeric : stringList)
{
intList.add(Integer.parseInt(numeric));
}

is there a better alternative to List<T> initalization than invoking Arrays.asList?

Is there a better alternative to using Arrays.asList as a List bulk initializer? Of concern is that this one is verbose and involves an extraneous class and method.
List<Double> myList = new ArrayList<Double>(Arrays.asList(3.01d, 4.02d, 5.03d));
Edit: This question pertains to a bulk initialization which would usually have more than the three values shown in the example.
If you know that you won't need to add anything to the list later, you can just do
List<Double> myList = Arrays.asList(3.01d, 4.02d, 5.03d);
I'm pretty sure the list returned from Arrays.asList can be modified, but only in that you can change the elements that are there -- you can't add new elements to it.
Use guava,
List<Double> myList = Lists.newArrayList(3.01d, 4.02d, 5.03d));
Doesn't match the question 100%, but adding this answer in case the wanting to instatiate a List was just to be able to immediately do something with the values in it, vs. just instantiating a List for the sake of just doing the instantiation alone.
With Java 8 you can use Stream.of(T... values), then manipulate the steam via the stream API to get the results you would want from the list of values.
For example, to get the max value of a series of values ...
int maxValue = Stream.of(10, 5, 25).max(Integer::compareTo).get();
The above example is also helpful for when you want Math.max(#,#) type functionality, but have more than two arguments to process.
Since java 9 you are able to use List.of factory method:
static <E> List<E> of​(E... elements)
Returns an immutable list containing an arbitrary number of elements.
Or use guava:
public static <E> ImmutableList<E> of(E e1, E e2,...)
Returns an immutable list containing the given elements, in order.
Yes, you can do it like this:
List<Double> myList = Arrays.asList(new Double[]{3.01d, 4.02d, 5.03d});
// or
List<Double> myList = Arrays.asList(3.01d, 4.02d, 5.03d);
Another option would be as an anonymous inner class:
List<Double> myList = new ArrayList() {
{
add(3.01d);
add(4.02d);
add(5.03d);
}
};

How to lowercase every element of a collection efficiently?

What's the most efficient way to lower case every element of a List or Set?
My idea for a List:
final List<String> strings = new ArrayList<String>();
strings.add("HELLO");
strings.add("WORLD");
for(int i=0,l=strings.size();i<l;++i)
{
strings.add(strings.remove(0).toLowerCase());
}
Is there a better, faster way? How would this example look like for a Set? As there is currently no method for applying an operation to each element of a Set (or List) can it be done without creating an additional temporary Set?
Something like this would be nice:
Set<String> strings = new HashSet<String>();
strings.apply(
function (element)
{ this.replace(element, element.toLowerCase();) }
);
Thanks,
Yet another solution, but with Java 8 and above:
List<String> result = strings.stream()
.map(String::toLowerCase)
.collect(Collectors.toList());
This seems like a fairly clean solution for lists. It should allow for the particular List implementation being used to provide an implementation that is optimal for both the traversal of the list--in linear time--and the replacing of the string--in constant time.
public static void replace(List<String> strings)
{
ListIterator<String> iterator = strings.listIterator();
while (iterator.hasNext())
{
iterator.set(iterator.next().toLowerCase());
}
}
This is the best that I can come up with for sets. As others have said, the operation cannot be performed in-place in the set for a number of reasons. The lower-case string may need to be placed in a different location in the set than the string it is replacing. Moreover, the lower-case string may not be added to the set at all if it is identical to another lower-case string that has already been added (e.g., "HELLO" and "Hello" will both yield "hello", which will only be added to the set once).
public static void replace(Set<String> strings)
{
String[] stringsArray = strings.toArray(new String[0]);
for (int i=0; i<stringsArray.length; ++i)
{
stringsArray[i] = stringsArray[i].toLowerCase();
}
strings.clear();
strings.addAll(Arrays.asList(stringsArray));
}
You can do this with Google Collections:
Collection<String> lowerCaseStrings = Collections2.transform(strings,
new Function<String, String>() {
public String apply(String str) {
return str.toLowerCase();
}
}
);
If you are fine with changing the input list here is one more way to achieve it.
strings.replaceAll(String::toLowerCase)
Well, there is no real elegant solution due to two facts:
Strings in Java are immutable
Java gives you no real nice map(f, list) function as you have in functional languages.
Asymptotically speaking, you can't get a better run time than your current method. You will have to create a new string using toLowerCase() and you will need to iterate by yourself over the list and generate each new lower-case string, replacing it with the existing one.
Try CollectionUtils#transform in Commons Collections for an in-place solution, or Collections2#transform in Guava if you need a live view.
This is probably faster:
for(int i=0,l=strings.size();i<l;++i)
{
strings.set(i, strings.get(i).toLowerCase());
}
I don't believe it is possible to do the manipulation in place (without creating another Collection) if you change strings to be a Set. This is because you can only iterate over the Set using an iterator or a for each loop, and cannot insert new objects whilst doing so (it throws an exception)
Referring to the ListIterator method in the accepted (Matthew T. Staebler's) solution. How is using the ListIterator better than the method here?
public static Set<String> replace(List<String> strings) {
Set<String> set = new HashSet<>();
for (String s: strings)
set.add(s.toLowerCase());
return set;
}
I was looking for similar stuff, but was stuck because my ArrayList object was not declared as GENERIC and it was available as raw List type object from somewhere. I was just getting an ArrayList object "_products". So, what I did is mentioned below and it worked for me perfectly ::
List<String> dbProducts = _products;
for(int i = 0; i<dbProducts.size(); i++) {
dbProducts.add(dbProducts.get(i).toLowerCase());
}
That is, I first took my available _products and made a GENERIC list object (As I were getting only strings in same) then I applied the toLowerCase() method on list elements which was not working previously because of non-generic ArrayList object.
And the method toLowerCase() we are using here is of String class.
String java.lang.String.toLowerCase()
not of ArrayList or Object class.
Please correct if m wrong. Newbie in JAVA seeks guidance. :)
Using JAVA 8 parallel stream it becomes faster
List<String> output= new ArrayList<>();
List<String> input= new ArrayList<>();
input.add("A");
input.add("B");
input.add("C");
input.add("D");
input.stream().parallel().map((item) -> item.toLowerCase())
.collect(Collectors.toCollection(() -> output));

Categories

Resources