views:

95

answers:

6

Whats 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 exmaple 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,

A: 

This is probably faster:

for(int i=0,l=strings.size();i<l;++i)
{
  strings.set(i, strings.get(i).toLowerCase());
}
Maurice Perry
This is fine for an `ArrayList`, but the performance is `O(N^2)` for a `LinkedList`.
Stephen C
Yes. for yours, the opposit is true.
Maurice Perry
Sorry, for the original algorithm, the opposit is true.
Maurice Perry
+1  A: 

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 A
+1  A: 

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

thSoft
A: 

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
+2  A: 

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
+1  A: 

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