Java negative indexOf (counting from the end [length()] )

Whimusical picture Whimusical · May 18, 2012 · Viewed 38.4k times · Source

Is there any way in Java to found indexOf of a char starting from the end and having length() as a reference as other languages do?

   new String("abcd").reverseIndexOf("d"(,[4 or -0]))
or new String("abcd").indexOf("d",-0) // Should return a (-)1

...instead of the obvious

   new String("abcd").indexOf("d") - newString("abcd").length()     

Thanks!

Answer

rob picture rob · May 18, 2012

lastIndexOf(int ch) will start from the end and search backwards, returning the absolute index of the last occurrence. Then you could subtract that number from the length of the String and negate it, if that's what you really want.

You can also use lastIndexOf(int ch, int fromIndex)if you want to search backwards from a particular index.

To answer your question about what happens when you pass a negative number, you can dig into the source code for the String class. As it turns out, the indexOf implementation that is ultimately called resets a negative fromIndex value to zero:

static int indexOf(char[] source, int sourceOffset, int sourceCount,
                   char[] target, int targetOffset, int targetCount,
                   int fromIndex) {
if (fromIndex >= sourceCount) {
        return (targetCount == 0 ? sourceCount : -1);
}
    if (fromIndex < 0) {
        fromIndex = 0;
    }
    ...

Getting back to your second example:

"abcd".indexOf("d",-0)

...implementing a generic indexOf which accepts a negative index and returns the appropriate negative index (if there is one) is more complicated because Java does not distinguish between int 0 and int -0 (both will be represented as 0), and because String.indexOf normally returns -1 if the search string is not found. However, you can get close to what you want. Note there are a few caveats:

  1. String.indexOf normally returns -1 if the search string is not found. But because -1 is a valid index in our new implementation, we need to define a new contract. Integer.MIN_VALUE is now returned if the search string is not found.
  2. Because we cannot test for int -0, we cannot refer to the index of the last character as -0. For that reason, we use -1 to refer to the index of the last character, and continue counting backwards from there.
  3. For consistency with item 2, negative return values also start counting down starting with -1 as the index of the last character.

The code could be simplified, but I've intentionally made it verbose so you can easily step through it in a debugger.

package com.example.string;

public class StringExample {

    public static int indexOf(String str, String search, int fromIndex) {
        if (fromIndex < 0) {
            fromIndex = str.length() + fromIndex; // convert the negative index to a positive index, treating the negative index -1 as the index of the last character
            int index = str.lastIndexOf(search, fromIndex);
            if (index == -1) {
                index = Integer.MIN_VALUE; // String.indexOf normally returns -1 if the character is not found, but we need to define a new contract since -1 is a valid index for our new implementation
            }
            else {
                index = -(str.length() - index); // convert the result to a negative index--again, -1 is the index of the last character 
            }
            return index;
        }
        else {
            return str.indexOf(str, fromIndex);
        }
    }

    public static void main(String[] args) {
        System.out.println(indexOf("abcd", "d", -1)); // returns -1
        System.out.println(indexOf("adbcd", "d", -2)); // returns -4
    }
}