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

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

Related

String to ArrayList<Long>

Currently i have:
String a = "123.5950,555,5973.1,6321.905,6411.810000000001,6591.855"
I can turn it into an array list of Strings then into array list of Longs:
ArrayList<String> vals = new ArrayList<String>(Arrays.asList(a.split(","));
ArrayList<Long> longs = new ArrayList<>();
for(String ks : vals){
longs.add(Long.parseLong(ks));
}
I tried to do this with Stream to make this more 'fun' but cant seem to be successful with this:
ArrayList<Long> longs = a.stream().map(Long::parseLong).collect(Collectors.toList());
I dont think the for loop is very elegant, how can i do it with Stream?
Edit: copied to original string wrong
You need to create a stream from the result of String.split:
final List<Long> longs = Arrays
.stream(a.split(","))
.map(Long::parseLong)
.collect(Collectors.toList());
Also, Collectors.toList() will return the List interface, not the concrete implementation ArrayList.
If you really need an array list, you'll need to copy it:
new ArrayList<>(longs);
Edit:
As #shmosel pointed out, you can collect directly to an array list with Collectors.toCollection(ArrayList::new)
You can't stream a String without splitting it up. Two ways to split to stream:
Arrays.stream(a.split(","))
or
Pattern.compile(",").splitAsStream(a)
Aside from that, collect(Collectors.toList()) returns List, not ArrayList. And I'm not sure why you expect parseLong() to work on those strings.
String a = "123.5950.555,5973.1,6321.905,6411.810000000001,6591.855";
List<Double> doubleList = Arrays.stream(a.split(","))
.map(Doubles::tryParse)
.collect(Collectors.toList());
System.out.println(doubleList);
Note: this uses Doubles#tryParse from Guava.

Equivalent to Arrays.asList() but for arrays?

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

How to make a new list with a property of an object which is in another list

Imagine that I have a list of certain objects:
List<Student>
And I need to generate another list including the ids of Students in the above list:
List<Integer>
Avoiding using a loop, is it possible to achieve this by using apache collections or guava?
Which methods should be useful for my case?
Java 8 way of doing it:-
List<Integer> idList = students.stream().map(Student::getId).collect(Collectors.toList());
With Guava you can use Function like -
private enum StudentToId implements Function<Student, Integer> {
INSTANCE;
#Override
public Integer apply(Student input) {
return input.getId();
}
}
and you can use this function to convert List of students to ids like -
Lists.transform(studentList, StudentToId.INSTANCE);
Surely it will loop in order to extract all ids, but remember guava methods returns view and Function will only be applied when you try to iterate over the List<Integer>
If you don't iterate, it will never apply the loop.
Note: Remember this is the view and if you want to iterate multiple times it will be better to copy the content in some other List<Integer> like
ImmutableList.copyOf(Iterables.transform(students, StudentToId.INSTANCE));
Thanks to Premraj for the alternative cool option, upvoted.
I have used apache CollectionUtils and BeanUtils. Accordingly, I am satisfied with performance of the following code:
List<Long> idList = (List<Long>) CollectionUtils.collect(objectList,
new BeanToPropertyValueTransformer("id"));
It is worth mentioning that, I will compare the performance of guava (Premraj provided) and collectionUtils I used above, and decide the faster one.
Java 8 lambda expression solution:
List<Integer> iDList = students.stream().map((student) -> student.getId()).collect(Collectors.toList());
If someone get here after a few years:
List<String> stringProperty = (List<String>) CollectionUtils.collect(listOfBeans, TransformerUtils.invokerTransformer("getProperty"));
You can use Eclipse Collections for this purpose
Student first = new Student(1);
Student second = new Student(2);
Student third = new Student(3);
MutableList<Student> list = Lists.mutable.of(first, second, third);
List<Integer> result = list.collect(Student::getId);
System.out.println(result); // [1, 2, 3]
The accepted answer can be written in a further shorter form in JDK 16 which includes a toList() method directly on Stream instances.
Java 16 solution
List<Integer> idList = students.stream().map(Student::getId).toList();
It is Mathematically impossible to do this without a loop. In order to create a mapping, F, of a discrete set of values to another discrete set of values, F must operate on each element in the originating set. (A loop is required to do this, basically.)
That being said:
Why do you need a new list? You could be approaching whatever problem you are solving in the wrong way.
If you have a list of Student, then you are only a step or two away, when iterating through this list, from iterating over the I.D. numbers of the students.
for(Student s : list)
{
int current_id = s.getID();
// Do something with current_id
}
If you have a different sort of problem, then comment/update the question and we'll try to help you.

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