tags:

views:

117

answers:

6

I hate to code what is already available. I can't seem to find in Jakarta commons a method that would to the following:

long x= 12345;
int[] digits = toDigitsArray(x); //[1,2,3,4,5]

So before I code my own toDigitsArray() method does anyone know if the equivalent exists either in j2se or in commons?

+1  A: 

Haven't seen one.

Something along "while (n != 0) { push(n % 10); n = n / 10}" should be enough.

If you don't want to mess with lists, then do it twice. First to calculate the number of entries in the array, and second to populate the array.

Thorbjørn Ravn Andersen
Much better than the string solution.
Bill K
Unfortunately the mod operator won't work if the long exceeds the bounds of an integer.
Yishai
+1  A: 

I think this could work, or at least take you further.

long x = 11234;
String[] s = Long.toString(x).split("");
int[] r = new int[s.length - 1];
for (int i = 1; i < s.length; i++)
  r[i - 1] = Integer.parseInt(s[i]);
rmarimon
That only works if x is >= 0
Yishai
Totally true.
rmarimon
I don't know how you would define the problem if x was < 0. Would each number be negative, or would the first digit be negative, or should the method return a flag?
Bill K
+4  A: 

How about x.toString().toCharArray() ? The result will be an array of chars rather than an array of ints as you appear to be looking for in your example, but you could cast the chars to ints as you used them, or depending on what you're trying to do with it, chars could be fine.

Jay
+2  A: 

No such function exists as far as I can see. Rolling your own has some gotchas which are worth discussing.

I ran three options (splitting a string and doing a modulus operation and making a char array) through some parameters, and this is the most "efficient" I could see. Note some assumptions:

  1. We have to support Long
  2. We retain the fact the sign bit of the number, but not in the array.

First the modulus route, which on first glance should be the most efficient. However, if the number is truly long (as in exceeds the maximum value of an int) then the mod operator overflows and gives junk results. So then we have to move to BigInteger (as far as I can tell, if someone sees a better solution, please comment):

public static void main(String[] args) {
    long x = 981212131233123L;
    int[] r = new int[calculateSize(x)];
    boolean positive = fillArrayWithDigits(x, r);
    //r = [9, 8, 1, 2, 1 ...
}

    private static boolean fillArrayWithDigits(long y, int[] r) {
        boolean positive = y >= 0;
        y = Math.abs(y);
        BigInteger ten = BigInteger.valueOf(10);
        for (int i = r.length; i > 0; i--) {
            r[i-1] = BigInteger.valueOf(y).mod(ten).intValue();
            y /= 10L;
        }
        return positive;
    }

    private static int calculateSize(long y) {
        int size = 0;
        do {
            size++;
            y /= 10L;
        } while (y != 0);
        return size;
    }

At the end of this, you have a boolean indicating if the number was positive, and an array containing the numbers.

In terms of doing a string parse, the "best" I could come up with was:

public static void main(String[] args) {
    long x = 981212131233123L;
    boolean positive = x >= 0;
    x = Math.abs(x);
    String[] s = Long.toString(x).split("");
    int[] r = new int[s.length - 1];
    for (int i = r.length; i > 0; i--) {
        r[i-1] = Integer.parseInt(s[i]);
    }
    //r = [9, 8, 1, 2, 1 ...
}

The char array method was very similar:

    public static void main(String[] args) {
        long x = 981212131233123L;
        boolean positive = x >= 0;
        x = Math.abs(x);
        char[] s = Long.toString(x).toCharArray();
        int[] r = new int[s.length];
        for (int i = r.length - 1; i > -1; i--) {
            r[i] = Character.digit(s[i], 10);
        }
        //r = [9, 8 ,1, 2, 1 ....
    }

But seems the most efficient

Yishai
i think the last one is the best
newacct
A: 

I came up with 4 versions of the method.

1- Uses String.toCharArray and Character.getNumericValue() 2- Uses modulus with an array 3- Uses modulus with an Deque 4- Uses String.split()

When testing with very large values and Long.MAX_VALUE all four give correct results.

When testing with Long.MIN_VALUE it turns out that Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE so you cannot rely on it to get rid of the minus sign!! So I had to get rid of it so I modified the versions. The modulus versions also give wrong values with Long.MIN_VALUE, the digits are correct but they have all negative signs.

Now for performance, I did a few tests where I ran each method 10 000 000 times with the same input, this a sample result but it is very representative of the others:

input: -5507235864653430347

1:5110

2:7235

3:7564

4:25487

So it turn out that the winner so far is method 1. 2 and 3 are not reliable and 4 is very slow.

public static int[] toIntArray(long x) {
 int neg = (x<0)?1:0;
 final char[] c = String.valueOf(x).toCharArray();
 final int s = c.length-neg;
 final int[] d = new int[s];
 for (int i = 0; i < s; i++) {
  d[i] = Character.getNumericValue(c[i+neg]);
 }
 if (neg==1) {
  d[0] = d[0] * -1;
 }
 return d;
}

public static int[] toIntArray2(long x) {
 int neg = (x<0)?1:0;
 final int s = String.valueOf(x).length()-neg;
 final int[] d = new int[s];
 for(int i =s-1 ; i > -1; i--) {
  d[i] = (int) (x%10);
  x = x/10;
 }
 return d;
}

public static Object[] toIntArray3(long x) {
 Deque<Integer> d = new ArrayDeque<Integer>(10);
 if(x==0){
  d.push(0);
  return d.toArray();
 }
 while(x != 0) {
  d.push((int) (x%10));
  x = x/10;
 }
 return  d.toArray();
}

public static int[] toIntArray4(long x) {
 int neg = (x<0)?1:0;
 final String[] c = String.valueOf(x).split("");
 final int s = c.length-neg;
 final int[] d = new int[s-1];
 for (int i = 1; i < s; i++) {
  d[i-1] = Integer.parseInt(c[i+neg]);
 }
 if (neg==1) {
  d[0] = d[0] * -1;
 }
 return d;
}
svachon
A: 

First, I'm not aware of any existing library method that does what you want, and I cannot think of a good way to search for one. (And if anyone can solve THAT problem, they deserve a Nobel prize!!)

All the answers that involve creating / splitting Strings, or using BigInteger are unnecessarily inefficient. Here's my solution:

public int[] toDigitsArray(long num) {
    if (num < 0) {
        throw new IllegalArgumentException("negative num");
    } else if (num == 0) {
        return new int[]{0};  // simplifies the rest of the code
    }
    int[] tmp = new int[20];  // big enough to hold all digits of Long.MAX_VALUE
    int i = tmp.length - 1;
    while (num != 0) {
        tmp[i--] = num % 10;
        num = num / 10;
    }
    int[] res = new int[tmp.length - i];
    System.arraycopy(tmp, i + 1, res.length, res, 0);
    return res;
}

(I haven't compiled / tested this ... but focus on the algorithm.)

Stephen C