18

I am trying to determine the correct way of changing all the values in a List using the new lambdas feature in the upcoming release of Java 8 without creating a **new** List.

This pertains to times when a List is passed in by a caller and needs to have a function applied to change all the contents to a new value. For example, the way Collections.sort(list) changes a list in-place.

What is the easiest way given this transforming function, and this starting list, to apply the function to all the values in the original list without making a new list?

String function(String s){ 
    return s.toUppercase(); // or something else more complex
}

List<String> list = Arrays.asList("Bob", "Steve", "Jim", "Arbby");

The usual way of applying a change to all the values in-place was this:

for (int i = 0; i < list.size(); i++) {
    list.set(i, function( list.get(i) );
}

Does lambdas and Java 8 offer:

  • an easier and more expressive way?
  • a way to do this without setting up all the scaffolding of the for(..) loop?
cigien
  • 57,834
  • 11
  • 73
  • 112
The Coordinator
  • 13,007
  • 11
  • 44
  • 73

3 Answers3

33
    List<String> list = Arrays.asList("Bob", "Steve", "Jim", "Arbby");
    list.replaceAll(String::toUpperCase);
Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
2

With a popular library Guava you can create a computing view on the list which doesn't allocate memory for a new array, i.e.:

upperCaseStrings = Lists.transform(strings, String::toUpperCase)

A better solution is proposed by @StuartMarks, however, I am leaving this answer as it allows to also change a generic type of the collection.


Another option is to declare a static method like mutate which takes list and lambda as a parameter, and import it as a static method i.e.:


mutate(strings, String::toUpperCase);

A possible implementation for mutate:

@SuppressWarnings({"unchecked"})
public static  List mutate(List list, Function function) {
    List objList = list;

    for (int i = 0; i 

Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
  • Does the Guava Lists.transform(...) actually modify the values of the original list? Or is it a changed view of the original list? For your `mutate` function. That should work. too bad Java did not include that as a default static method for `Lists` or on all lists. – The Coordinator Oct 31 '13 at 09:25
  • 1
    It's an immutable computing view of an original list, it does not modify it. It's ok for a single pass over a list, but might be not for multiple passes. – Andrey Chaschev Oct 31 '13 at 09:36
  • 1
    Interesting approach of changing the type of the list. You could make `objList` be of type `List` and move the `@SuppressWarnings` directly onto that declaration; this avoids accidentally suppressing unrelated warnings elsewhere in the method. However, I'm not sure this is a good idea. The `mutate` function looks like it consumes a List and returns a List. I think most programmers would be surprised to discover that it returns the **same** list. (The name `mutate` is a clue, though.) I'd say it's preferable to use streams to create and return a new `List`. – Stuart Marks Oct 31 '13 at 15:49
  • Yeah, this is controversial. Seems to be a normal thing in dynamic-typed languages and absolutely uncommon for static languages. Perhaps initially declaring a list as `List>` for this purpose is a better way to go. I will gray it out. – Andrey Chaschev Oct 31 '13 at 20:12
-1

You seem to have overlooked forEach, which can be used to mutate the elements of a List. Of course, that doesn't cover your use case entirely—it won't help if your elements are immutable or if you want to replace them completely. [edit: the next sentence relates to streams, not to collections as specified in the question] There is a good reason why you can't do those operations: they aren't parallel-friendly (this actually applies to forEach also) and the Stream API strongly discourages the use of sequential-only operations.

Maurice Naftalin
  • 10,354
  • 2
  • 24
  • 15
  • 1
    `forEach` on a List only allows the elements to be iterated. It does not allow the changing of the value within the list. – The Coordinator Oct 31 '13 at 10:03
  • 1
    The -1 is unjustified. The OP's question asked about "transforming all the values" in the list. This is ambiguous. It could mean either 1) mutating each list element in-place, or 2) replacing each element with a different element. This answer addresses the first cast. – Stuart Marks Oct 31 '13 at 15:07
  • 1
    @StuartMarks The title is very clear "change a List's values in-place" and the example given is explicitly clear on "The usual way of applying a change to all the values in-place". – The Coordinator Oct 31 '13 at 19:01
  • Way is changing the value of a stream's contents not parallel friendly? – K.Nicholas Feb 08 '17 at 15:26
  • Very reasonable question, given the way I phrased that. Of course, transforming the value of stream elements is exactly what the map operation does. I was thinking about changing stream values in-place, rather than passing on transformed values. That would need a mechanism outside of the standard streams framework. But that scenario is off-topic, and looking back I don't think my comment was helpful. – Maurice Naftalin Sep 03 '17 at 13:11