53

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,

Chris
  • 15,429
  • 19
  • 72
  • 74

14 Answers14

130

Yet another solution, but with Java 8 and above:

List<String> result = strings.stream()
                             .map(String::toLowerCase)
                             .collect(Collectors.toList());
E. Nwofor
  • 9
  • 1
  • 4
Igor G.
  • 6,955
  • 6
  • 26
  • 26
  • 1
    Though this is more elegant, this is not the more performant solution. [Angelica's benchmark](https://jaxenter.com/java-performance-tutorial-how-fast-are-the-java-8-streams-118830.html) for finding the max value in a list of boxed ints: ArrayList, for-loop : 6.55 ms ArrayList, seq. stream: 8.33 ms – thisisananth Jun 22 '18 at 16:35
  • 1
    @thisisananth a benchmark (if we dare to call the undocumented methodology a benchmark) of an operation that searches for a maximum in an `int[]` array, is entirely unrelated to an operation converting a collection of strings to lower case. A list of boxed ints would be a `List`, by the way, but that linked article uses `int[]`, which is an array of primitive ints. And since that will yield an `IntStream`, a serious developer would use `max()` rather than `reduce(Integer.MIN_VALUE, Math::max)` for that. – Holger Dec 05 '19 at 09:34
  • Also if there is really large List it should be mentioned that there is an parrallelStream that can improve the performance for larger list. – SkateScout Jan 28 '22 at 08:06
36

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));
}
Matthew T. Staebler
  • 4,756
  • 19
  • 21
  • Referring to your first solution: how would one return a ListIterator as List? – leba-lev May 07 '12 at 22:13
  • @rookie, Your question isn't clear. Are you asking how to use a ListIterator as a List? If so, then you can't. An Iterator is not a collection, but a pointer into one. – Jeremy May 08 '12 at 14:28
  • 2
    With Java 8, the first example can be simplified to `strings.replaceAll(String::toLowerCase);` You could do a similar thing with the second, using `Arrays.asList(stringsArray).replaceAll(String::toLowerCase);` – Holger Dec 05 '19 at 09:27
13

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();
            }
        }
    );
Roman
  • 64,384
  • 92
  • 238
  • 332
6

If you are fine with changing the input list here is one more way to achieve it.

strings.replaceAll(String::toLowerCase)

Arindam Nayak
  • 7,346
  • 4
  • 32
  • 48
  • 1
    This won't work with immutable collections, like ones created using List.of(). Although, an elegant solution. – Domadin Jun 24 '21 at 11:28
4

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.

Yuval Adam
  • 161,610
  • 92
  • 305
  • 395
2

Try CollectionUtils#transform in Commons Collections for an in-place solution, or Collections2#transform in Guava if you need a live view.

thSoft
  • 21,755
  • 5
  • 88
  • 103
1

This is probably faster:

for(int i=0,l=strings.size();i<l;++i)
{
  strings.set(i, strings.get(i).toLowerCase());
}
Maurice Perry
  • 32,610
  • 9
  • 70
  • 97
0

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;
}
leba-lev
  • 2,788
  • 10
  • 33
  • 43
0

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)

Tarski
  • 5,360
  • 4
  • 38
  • 47
0

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. :)

0

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));
Ankur Gupta
  • 301
  • 4
  • 12
0

Have used Java8 streams for lowercase of every element in a Collection.

List<String> groupNamesList =  new ArrayList<>();
groupNamesList.add("GroupOne");
groupNamesList.add("GroupTwo");
groupNamesList.add("GroupThree");
groupNamesList = groupNamesList.stream().map(groupName -> groupName.toLowerCase()).collect(Collectors.toList());

Output:

[groupone,grouptwo,groupthree]
anand krish
  • 4,281
  • 4
  • 44
  • 47
0
private static void getOccuranceCount() {
    List<String> listdemo=Arrays.asList("saradhi","bill","mike","Bill","MIKE");
   
    Map<String, Long> collect = listdemo.stream().map(**String::toLowerCase**).collect(Collectors.groupingBy(e->e,Collectors.counting()));
    System.out.println("collector printing is : "+collect);
}
Destroy666
  • 892
  • 12
  • 19
Raj
  • 1
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 23 '23 at 03:02
0

For UppperCase

val originalList = arrayListOf("apple", "banana", "cherry")

val upperCaseList = originalList.map { it.toUpperCase() }

For LowerCase

val originalList = arrayListOf("Apple", "Banana", "Cherry")

val lowerCaseList = originalList.map { it.toLowerCase() }