Skip to main content

static boolan Integer.isInteger(String)

78 replies [Last post]

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
klopperq
Offline
Joined: 2005-09-23
Points: 0

This is where utility classes are so usefull...

public class NumberUtils{
public static boolean isInteger(Number num){
....
}
}

Then we don't have to bloat the APIs and if you are one of those (very few IMHO) people who need to use something like this on a lot of projects its quite reusable...

chandra
Offline
Joined: 2003-06-12
Points: 0

+1

jwenting
Offline
Joined: 2003-12-02
Points: 0

and I also want isOne(String s), isTwo(String s), isThree(String s) ... all the way to isMaxIntMinusOne(String s).
And of course for negative numbers as well, and every other primitive wrapper type.

s690716
Offline
Joined: 2004-03-04
Points: 0

And don't forget support of octal / hexadecimal represented numbers... so we also need methods like isOctal(String), isHexadecimal(String), isBinary(String) ...

Why a compact API with a small memory footprint, if every method could be integrated in the JRE? And we need a package for primary grade excercise support!

monika_krug
Offline
Joined: 2004-10-14
Points: 0

The only reason against my suggestion that I agree with is that it would result in the work of parsing done twice when it is used for parsing a string that is most likely to be an integer anyway.

How could this be avoided?

One possibility is caching the parsed String and number (when isInteger returns true). Then, when parseInt() is called on the same String as isInteger() was called on before, it could be returned immediately.
But this would result in the Integer class holding a reference to the String and to hold the int value, both in static variables. Could this be a problem? A String plus an int is not much memory. Is the comparison too much additional overhead? Probably not, the parsing takes much longer than that.

Another possibility is implementing the isInteger() method in a way that is much shorter than parseInt(). But how could this be done? How long does string.match(regex) need? Is it as long as parseInt()? If it is a lot shorter, isInteger could be implemented somewhat like this:
[code]public boolean isInteger(String s)
{
if (s==null) return false;
int n = s.length(); /* to avoid calling s.length() repeatedly */
if (n == 0 || n > 11 /* 11 is "-2147483648".length() */) return false;

boolean positiveNumber = false, negativeNumber = false;

/* to avoid calling matches() twice: */
if (s.substring(1).matches("\\d+"))
{
/* to avoid calling charAt(0) thrice */
char first = s.charAt(0);
if (first == '-')
{
negativeNumber = true;
}
else if ( first == '+' || (Character.isDigit(first) && n <= 10) )
{
positiveNumber = true;
}

if(positiveNumber)
{
if (n < 10) return true;
/* == only do actual parsing in the other cases == */
}

if (negativeNumber)
{
if(n < 11) return true;
/* == only do actual parsing in the other cases == */
}
return false;
}[/code]

As I said, this assumes that string.matches(regex) would be a lot faster. I am not sure it is.

It would still add overhead for numbers close to the Min and Max.

Monika.

kirillcool
Offline
Joined: 2004-11-17
Points: 0

Monika, soooo many problems in the functions that you've wrote that i don't even know where to start:

1. It should work for all radixes, so the regex is not good
2. You can't check the length, otherwise 999999999 is OK
3. If you check the length, you should take the radix into account
4. How can it match \\d++ [b]and[/b] have a leading minus?

In addition, what static variable should hold the reference? What about multi-thread safe or even two consecutive calls to [i]isInteger[/i]?

Kirill
http://jroller.com/page/kirillcool/Weblog?catname=/Java

monika_krug
Offline
Joined: 2004-10-14
Points: 0

> 1. It should work for all radixes, so the regex is
> not good

Okay, this only works for base 10, but this is the most common use. It's the isInteger(String), not the isInteger(String, int base) method.

> 2. You can't check the length, otherwise 999999999 is
> OK

Look at my code again: Only for length less than the max true is returned and for longer false, in case of the length identical to the max length actual parsing is done. I did not write how this is done, just put a comment that it is done there.

> 3. If you check the length, you should take the radix
> into account

As 1.

> 4. How can it match \\d++ [b]and[/b] have a leading
> minus?

Look closely. I match substring(1).

> In addition, what static variable should hold the
> reference?

An additional static variable inside the Integer class.

> What about multi-thread safe

Okay, that is a serious problem. But it can be guaranteed to function correctly if the part where the cached String is compared to the parameter and (if equal) the cached int is returned is in a synchronized block. Then in multi-thread use there will only be the original performance problem, but it will work correctly.

> or even two consecutive calls to [i]isInteger[/i]?

The second one will overwrite the cached values from the first one. What else would you expect?

Monika.

frank1russo
Offline
Joined: 2004-03-31
Points: 0

This is simple to do with regular expressions:

/** regex pattern for an integer */
private static final Pattern intPattern = Pattern.compile("\\d+");

/** Returns true iff the provided string contains only digits */
public static final boolean isInteger(String str) {
return intPattern.matcher(str).matches();
}

kirillcool
Offline
Joined: 2004-11-17
Points: 0

So, you suppose that a string consisting of a thousand 9's is a valid integer in Java?

kirillcool
Offline
Joined: 2004-11-17
Points: 0

Here is how the code for [b]parseInt[/b] looks in Tiger, judge for yourself how easy it would be to reuse it for the [b]isInteger[/b] function:
[pre]
public static int parseInt(String s, int radix)
throws NumberFormatException
{
if (s == null) {
throw new NumberFormatException("null");
}

if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}

if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}

int result = 0;
boolean negative = false;
int i = 0, max = s.length();
int limit;
int multmin;
int digit;

if (max > 0) {
if (s.charAt(0) == '-') {
negative = true;
limit = Integer.MIN_VALUE;
i++;
} else {
limit = -Integer.MAX_VALUE;
}
multmin = limit / radix;
if (i < max) {
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
} else {
result = -digit;
}
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
if (negative) {
if (i > 1) {
return result;
} else { /* Only got "-" */
throw NumberFormatException.forInputString(s);
}
} else {
return -result;
}
}
[/pre]
Kirill
http://jroller.com/page/kirillcool/Weblog?catname=/Java

Message was edited by: kirillcool

yishai
Offline
Joined: 2003-11-16
Points: 0

> Here is how the code for [b]parseInt[/b] looks in
> Tiger, judge for yourself how easy it would be to
> reuse it for the [b]isInteger[/b] function:

Here is my rewrite (fast and dirty, please no comments on style):

[pre]
private static int integer(String s, int radix, StringBuffer problem)
throws NumberFormatException
{
if (s == null || s.length() == 0) {
return problem(s, problem);
}

//I think these are fine. You can know what valid Radix's are ahead of time.
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}

if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}

int result = 0;
boolean negative = false;
int i = 0, max = s.length();
int limit;
int multmin;
int digit;

if (max > 0) {
if (s.charAt(0) == '-') {
negative = true;
limit = Integer.MIN_VALUE;
i++;
} else {
limit = -Integer.MAX_VALUE;
}
multmin = limit / radix;
if (i < max) {
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
return problem(s, problem);
} else {
result = -digit;
}
}
while (i < max) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
return problem(s, problem);
}
if (result < multmin) {
return problem(s, problem);
}
result *= radix;
if (result < limit + digit) {
return problem(s, problem);
}
result -= digit;
}
} else {
return problem(s, problem);
}
if (negative) {
if (i > 1) {
return result;
} else { /* Only got "-" */
return problem(s, problem);
}
} else {
return -result;
}
}

private static int problem(String sourceValue, StringBuffer problem) {
problem.append(sourceValue);
return 0;
}

public static int parseInt(String value, int radix) {
StringBuffer problem = new StringBuffer();
int result = integer(value, radix, problem);
if (problem.length() > 0) {
throw NumberFormatException.forInputString(problem.toString());
} else {
return result;
}
}

public static boolean isInteger(String value, int radix) {
StringBuffer problem = new StringBuffer();
integer(value, radix, problem);
return problem.length() == 0;
}
[/pre]

Of course we could imagine making more efficient code that doesn't rely on a StringBuffer creation every time, but fundamentally, it isn't a hard problem to refactor. I was also a little quick'n'dirty on the handling of the 0 length String boundary condition, but that is easily solvable if someone were spending the time on the problem.

i_salloum
Offline
Joined: 2004-11-24
Points: 0

i think regular expressions can help you there... you can write your code as

String input = "";
do{
System.out.print("insert a whole number: ");
input = bufferedReader.readLine();
} while (!Pattern.matches("\\d*",input));
int number = Integer.parseInt(input);

dondi_imperial
Offline
Joined: 2004-11-22
Points: 0

I think the original point here is that doing it like this:
[code]if(Integer.isInteger(someObject){
// do something
}[/code]
Is the cleanest, most readable and [b]correct[/b] way of doing it. Add to that the fact that the isInteger method is where it is supposed to be and that everyone will write the same code if this were available this would add to everyone's productivity in the long run.

Dondi

zander
Offline
Joined: 2003-06-13
Points: 0

> I think the original point here is that doing it like
> this:
> [code]if(Integer.isInteger(someObject){
> // do something
> }[/code]
> Is the cleanest, most readable and [b]correct[/b] way
> of doing it.

You are missing that doing a check before then requires to do the conversion later again for most cases, so this is actually slower.
Only if you don't convert after the check AND expect a high percentage to not be numbers (which will then throw exceptions) will this be faster.
Due to this, the exceptions way is more in line with Java and causes me to disagree that your way is the correct way.

> Add to that the fact that the isInteger
> method is where it is supposed to be and that
> everyone will write the same code if this were
> available this would add to everyone's productivity
> in the long run.

How do you reason that that is the class where it is suppost to be?
Why should it not equally logical be on Short, or Long?

If anything; I'd put it on java.lang.Number

monika_krug
Offline
Joined: 2004-10-14
Points: 0

As I said in the first posting:
"I have only ever needed this for ints, but if this method is added, Short.isShort(String), Double.isDouble(String) etc. should be added to the other wrapper classes, too."

It should not be added to Number, because Number.isNumber(String) would return true for e.g. a float, which is no use to us if we want an int.

Monika.

zander
Offline
Joined: 2003-06-13
Points: 0

Truth is; its soo darn easy to make this yourself (your example program can be done in almost halve the size) that an extra method seems silly.
Next think is your going to ask for a Number Number.add(Number, Number).
Hmm. Ok, thats not trivial, but anyway :)

Point is; the implementation is 2 lines, so if you need it then I suggest you put it in a library of your own.
Or, naturally, you learn to program using exceptions since that is the correct way of doing this anyway.

The exceptions way:
[code]
int number;
while(true) {
try {
number = Integer.parseInt(bufferedReader.readLine());
break;
} catch(NumberFormatException e) { }
}
[/code]

yishai
Offline
Joined: 2003-11-16
Points: 0

Sure it is easy to do yourself, but you have to rely on the try catch, which is not supposed to be used for flow control. There is no way, short of rewriting the whole JDK function to parse strings, to do this without exception catching (and throwing in the JDK). This is a performance nighmare under certain circumstances. (e.g., validate that all of the values in the second column of a 1,000,000 row CSV file are not numbers).

The frustrating thing is that it would be trivial to do in the underlying JDK code, but when you are stuck with the API, well you are stuck.

zander
Offline
Joined: 2003-06-13
Points: 0

> Sure it is easy to do yourself, but you have to rely
> on the try catch, which is not supposed to be used
> for flow control.

Whoa! Exceptions really [b]are all about[/b] flow control; how else can you look at it? If you don't see them as flow control, you are not using them optimally.

> This is a performance nighmare under certain
> circumstances. (e.g., validate that all of the values
> in the second column of a 1,000,000 row CSV file are
> not numbers).

Use Character.isDigit(char c)

yishai
Offline
Joined: 2003-11-16
Points: 0

> Whoa! Exceptions really [b]are all about[/b] flow
> control; how else can you look at it? If you don't
> see them as flow control, you are not using them
> optimally.

I think of exceptions, especially runtime exceptions, as this:

"Unchecked exceptions, being the opposite of checked exceptions, indicate that the program is in an unrecoverable state."

http://www.ryanlowe.ca/blog/archives/000453_throws_object.php

Using it for simply boolean flow control is a bad practice, performance-wise, not to mention error prone.

> Use Character.isDigit(char c)

This doesn't answer the formal question of is it an Integer, in the sense of will Integer.parseInt(String) throw an exception when you call it on a given value. Integer.parseInt(String) handles the minus sign, and that the number isn't too long, or too large.

zander
Offline
Joined: 2003-06-13
Points: 0

> > Use Character.isDigit(char c)
>
> This doesn't answer the formal question of, is it an
> Integer

Thats not what you asked; you asked:

> validate that all of the values
> in the second column of a 1,000,000 row CSV file are
> not numbers.

Where my answer was the right one.

If you have another usage of Integer.isInteger(String) that you feel should not be done using an exceptions-handling case, please do tell us. And if its common enough; you perhaps will convince more people that this is needed.

yishai
Offline
Joined: 2003-11-16
Points: 0

> > > Use Character.isDigit(char c)
> >
> > This doesn't answer the formal question of, is it
> an
> > Integer
>
> Thats not what you asked; you asked:
>
> > validate that all of the values
> > in the second column of a 1,000,000 row CSV file
> are
> > not numbers.
>
> Where my answer was the right one.

That is true. But what about a negative sign in front of the number? My point is, you can rewrite the understanding of a number for each case. But you shouldn't have to. That should be part of the core API. Rewrite it only if your's is different in some special circumstance.

> If you have another usage of
> Integer.isInteger(String) that you feel should not be
> done using an exceptions-handling case, please do
> tell us. And if its common enough; you perhaps will
> convince more people that this is needed.

I think the basic validation of user input is enough of a use-case. Using such an API makes code clearer, cleaner and faster than an attempt to parse just to see if it would work, and I'd be curious to hear what the case against it is.

zander
Offline
Joined: 2003-06-13
Points: 0

> I think the basic validation of user input is enough
> of a use-case.

How do you propose that to validate the input that comes from a file?

> Using such an API makes code clearer,
> cleaner and faster than an attempt to parse just to
> see if it would work

The funny thing is; you have to parse it to see if its OK. So you end up parsing twice; one time for check, and one time to convert.
It can be mathmatically proven that you can not make parsing faster if you want to do it correctly, so you end up doing the exact same work twice.

I'm repeating myself and you fail to address the issue of doing things twice (accept saying its acceptable for you).

yishai
Offline
Joined: 2003-11-16
Points: 0

> > Using such an API makes code clearer,
> > cleaner and faster than an attempt to parse just
> to
> > see if it would work
>
> The funny thing is; you have to parse it to see if
> its OK. So you end up parsing twice; one time for
> check, and one time to convert.
> It can be mathmatically proven that you can not make
> parsing faster if you want to do it correctly, so you
> end up doing the exact same work twice.
>
> I'm repeating myself and you fail to address the
> issue of doing things twice (accept saying its
> acceptable for you).

I addressed the performance complaint in another post, but here I would like to add that you don't always need to convert to parse. I did a little check through my own project, and found this real world case:

[pre]
boolean goodPageNum = true;
if (pageNum != null) {
try {
Integer.parseInt(pageNum);
}
catch (NumberFormatException ex) {
goodPageNum = false;
}
}
else {
goodPageNum = false;
}
if (goodPageNum) {
sb.append("&pagenum=");
sb.append(pageNum);
}
[/pre]

The sb StringBuffer here is being used to generate a URL for a GET to a report server. Basically it adds the parameter (as a string, this is a URL) if the string is an Integer.

Sure, you can think of ways to write that without exception handling, but the natural way is to look to the Integer class, and the Integer class can only answer with an exception.

Instead we could have done:

[pre]
if (Integer.isInteger(pageNum)) {
sb.append("&com.kashrus.report.pagenum=");
sb.append(pageNum);
}
[/pre]

zander
Offline
Joined: 2003-06-13
Points: 0

> I would like to add that you don't
> always need to convert to parse. I did a little check
> through my own project, and found this real world
> case:
[snip example]

This example smells; having leading-zeros end up in your stringbuffer. I suggest parsing and converting back using String.valueOf() so you won't have any problems.

> Instead we could have done:
>
> [pre]
> if (Integer.isInteger(pageNum)) {
> sb.append("&com.kashrus.report.pagenum=");
> sb.append(pageNum);
> }
> [/pre]

Or:
[pre]
try {
sb.append("&pagenum="+ String.valueOf(Integer.parseInt(pageNum)));
} catch(NumberFormatException e) {
//log problem here
}
[/pre]
Which is so much shorter then your 16-line version that I repeat that this, hmm, argument is based on a lack of experience in programming with exceptions on your side. I programmed c, perl and Java (and lots of others). I stick with Java since I embraced the advantages of exceptions. Your code example makes me think you did not.

yishai
Offline
Joined: 2003-11-16
Points: 0

> > I would like to add that you don't
> > always need to convert to parse. I did a little
> check
> > through my own project, and found this real world
> > case:
> [snip example]
>
> This example smells; having leading-zeros end up in
> your stringbuffer. I suggest parsing and converting
> back using String.valueOf() so you won't have any
> problems.

That depends on your assumptions of where the number came from. It wasn't typed in by a user, so leading zeros aren't possible, and anyway, they don't matter (the URL would work anyway). The example isn't quoted enough to understand the bounds checking, and frankly, that isn't relevant.

> > Instead we could have done:
> >
> > [pre]
> > if (Integer.isInteger(pageNum)) {
> >
> sb.append("&com.kashrus.report.pagenum=");
> > sb.append(pageNum);
> > }
> > [/pre]
>
> Or:
> [pre]
> try {
> sb.append("&pagenum="+
> num="+ String.valueOf(Integer.parseInt(pageNum)));
> } catch(NumberFormatException e) {
> //log problem here
> }
> [/pre]
> Which is so much shorter then your 16-line version
> that I repeat that this, hmm, argument is based on a
> lack of experience in programming with exceptions on
> your side. I programmed c, perl and Java (and lots
> of others). I stick with Java since I embraced the
> advantages of exceptions. Your code example makes me
> think you did not.

Shorter, yes. Readable, no. You have to look at that code carefully to know what your intent is. Very carefully. Also, your code is tremendously inefficient, which in this case isn't a problem, but in a loop it would be. You create an extra string, which is non-trivial to create, catch an exception for flow control, and it made you think about the problem differently. Your comment says it all. You want to log a problem. There is no problem. The page number not being a real number is an expected case, nothing worth noting, any more than noting if it was a 2 or a 3.

Also, this was someone else's code. Which is precisely my real-world point. You will see code like this more frequently in the real world because of the lack of an isInteger method.

So I would say that on both performance and readability, the isInteger case wins.

alexlamsl
Offline
Joined: 2004-09-02
Points: 0

I do think that the try-catch block in this case is clear and readable.

Although from a narrow point of view isInteger() looks clearer than the try-catch approach, on the Language wide level that would mean introducing tons more of these "readable" methods, which would increase the complexity of the core API by such a factor that it would be difficult both to maintain or to comprehend it for basic operations.

ulfzibis
Offline
Joined: 2005-02-18
Points: 0

This sounds realistic.

Although I like the approach by caching the parsing-result of the isInteger method for later usage by [i]valueOf[/i] or [i]parseInt[/i]. As you see, we have [u]just two methods[/u] for parsing a string to an integer. So [u]why not have three, all caching the result[/u].

Perhaps this is a good example for multi return values, to avoid the exception, see: http://forums.java.net/jive/thread.jspa?threadID=670

dondi_imperial
Offline
Joined: 2004-11-22
Points: 0

+1 I don't know why this was not included in java 1.0 :D