8

I have conversion to Map problem in Core Java.

Below is requirement:
Given a String array below

String str[] = {"abc","123","def","456","ghi","789","lmn","101112","opq"};          

Convert it into a Map such that the resultant output is below

Output

======      ======         
key         Value        
======      ======                  
abc          true       
123          false       
def          true      
456          false

The above should be printed for each element in the array. I have written the code but it's not working and I'm stuck. Please let me know how it can be resolved. Thanks in advance.

import java.util.HashMap;       
import java.util.Iterator;       
import java.util.Map;       

public class CoversionToMap {

/**
 * @param args
 */
public static void main(String[] args) {
    String str[] = {"abc","123","def","456","ghi","789","lmn","101112","opq"};
    Map m = new HashMap();
    for(int i=0;i<str.length;i++){
        if(Integer.parseInt(str[i]) < 0){
            m.put(str[i],true);
        }else{
            m.put(str[i],false);
        }
    }
    //Print the map values finally
    printMap(m);
}   

public static void printMap(Map mp) {
        Iterator it = mp.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pairs = (Map.Entry)it.next();          
            System.out.println(pairs.getKey() + " = " + pairs.getValue());
        }
}  
}           

exception:

Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"       
    at java.lang.NumberFormatException.forInputString(Unknown Source)       
    at java.lang.Integer.parseInt(Unknown Source)       
    at java.lang.Integer.parseInt(Unknown Source)       
    at CoversionToMap.main(CoversionToMap.java:22)       
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
Deepak
  • 2,094
  • 8
  • 35
  • 48
  • do you have an output? do you have an error? – talnicolas Sep 29 '11 at 12:44
  • updated @ talnicolas,guys im really stuck and with no help.i have come here finally need your help. – Deepak Sep 29 '11 at 12:45
  • do the numbers only have to be associated with false or is it just an alternation thing? – talnicolas Sep 29 '11 at 12:47
  • 2
    How about reading the javadoc of Integer.parseInt? How would you parse negative numbers if every not-numeric string gives a negative number when parsed? See http://download.oracle.com/javase/6/docs/api/java/lang/Integer.html#parseInt%28java.lang.String%29. There are thousands of methods in the standard API. You won't be able to guess what each of them does. Reading their documentation is the way to know what they do and how they work. – JB Nizet Sep 29 '11 at 12:47
  • alpabets with true and numeris with false; – Deepak Sep 29 '11 at 12:48
  • guys thank you so much for your reply.Im highly thankful to you.you made my day Stackoverflow.i was banging my head against the wall the entire day for the solution. – Deepak Sep 29 '11 at 12:55

12 Answers12

9

Everyone is suggesting using exception handling for this, there is nothing exceptional here to warrant using exceptions like this, you don't try turning left in your car and if you crash go right do you? Something like this should do it

Map<String, Boolean> m = new HashMap<String, Boolean>();
for (String str: strs) {
    m.put(str, isInteger(str));
}

public boolean isInteger(String str) {
    int size = str.length();

    for (int i = 0; i < size; i++) {
        if (!Character.isDigit(str.charAt(i))) {
            return false;
        }
    }

    return size > 0;
}

Much clearer and more efficient that catching throwing exception, even when there are 99% integers as the integer value is not even needed so no conversion required.

vickirk
  • 3,979
  • 2
  • 22
  • 37
  • 3
    You may want to add a check for a minus symbol at the start – vickirk Sep 29 '11 at 13:08
  • 1
    true, but otherwise a nice solution (+1). But I'd make `isInteger()` `static` – Sean Patrick Floyd Sep 29 '11 at 13:25
  • And probably move it into a general utility class too – vickirk Sep 29 '11 at 13:32
  • 2
    yeah, but then you might as well use Commons / Lang in the first place :-) – Sean Patrick Floyd Sep 29 '11 at 13:33
  • I'd agree and would, but the op didn't like that idea when suggested by someone else :-( – vickirk Sep 29 '11 at 13:44
  • @vickirk: That's a terrible analogy. Exception handling is not about reacting in case of a crash. It is about making sure the program does not fail due to unexpected eventualities. – Charles Goodwin Sep 29 '11 at 16:10
  • 1
    How it is better? You have to catch the exception *anyway.* – user207421 Sep 30 '11 at 00:00
  • qvickirk: note that the your method doesn't guarantee that the number can be parsed to an `int`? It doesn't check the size of the number (which would effectively require parsing the integer), for example. – Joachim Sauer Sep 30 '11 at 05:58
  • 2
    -1 This is definitely not clearer than the shorter, clearer version using exception handling. Contrary to your understanding, exceptions aren't only for "crash" situations and are a valid and accepted programming methodology. Also, as parseInt() guarantees the String can be parsed, whereas yours doesn't, it's far more maintainable. – spikeheap Sep 30 '11 at 14:55
  • @Charles Goodwin "unexpected eventualities" and the fact that a string here does not contain digits is not unexpected. – vickirk Oct 03 '11 at 11:42
  • @EJP "You have to catch the exception anyway" What exepection? My code does not need to catch any exceptions – vickirk Oct 03 '11 at 11:43
  • @spikehead How can ``m.put(str, isInteger(str));`` not be clearer than several lines of exception handling. "exceptions aren't only for "crash" situations and are a valid and accepted programming methodology." accepted for handling ERRORS yes, but for implementing flow control or logic they most certainly are not. – vickirk Oct 03 '11 at 11:46
  • @Charles "It is about making sure the program does not fail due to unexpected eventualities" Yes, and there are no unexpected causes of exeptions here, we know for a fact 50% of string are going to result in exception thrown in this code, just to repeat, this is not unexpected, it should be handled by regular code. – vickirk Oct 03 '11 at 12:13
  • @vickirk Integer.parseInt() can throw a NumberFormatException. If your isInteger() method isn't identical to what's in Integer.parseInt() the exception is a practical possibility, so it must be caught. And if it is identical, what's its reason for existence? And you still haven't addressed the inherent circularity in your argument. – user207421 Oct 04 '11 at 00:19
  • @EJP I'm getting bored of this. Where in my code does it call parseInt? My code throws no exceptions, the code is above, read it rather than guessing what it does and how. – vickirk Oct 04 '11 at 01:01
  • @vickirk I'm not ;-) OK so you aren't calling parseInt(), you are writing your own. The logical consequence is that you have to replicate everything in the JDK that doesn't conform to your dogma. – user207421 Oct 04 '11 at 03:36
  • @EJP Last post on this. This replicates nothing in the java library, this functionality doe not exist, except something that exists in commons, which would be my preferred option. presumably your argument extends to apache commons library? I completely fail to see what the controversy is here, you can't get a simpler function and it should be so much faster as it doesn;t even have to convert to a number (look at the src of parseint) and doesn't use exceptions for decision making, exceptions are expensive, an expense which is acceptable for errors but not normal behavior, and more readable. – vickirk Oct 04 '11 at 11:23
  • This function doesn't handle decimals. For example: 1.202 – giZm0 Feb 05 '13 at 08:48
  • @giZm0 "This function doesn't handle decimals" No it doesn't but then the op never asked for decimals ;-) – vickirk Feb 05 '13 at 13:31
  • @vickirk,how do you handle decimals for the above code snippet? – Deepak Apr 25 '13 at 08:48
5

Integer.parseInt(..) throws an exception for invalid input.

Your if clause should look like this:

if (isNumber(str[i])) {
   ...
} else {
   ...
}

Where isNumber can be implemented in multiple ways. For example:

  • using try { Integer.parseInt(..) } catch (NumberFormatException ex) (see this related question)
  • using commons-lang NumberUtils.isNumber(..)
Community
  • 1
  • 1
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • i dont want NumberUtils function how to accomplish it in Java. – Deepak Sep 29 '11 at 12:47
  • 6
    Then use the first option – Rob Hruska Sep 29 '11 at 12:47
  • 4
    By using NumberUtils you will get a well tested, fast implementation, better than your own untested code. – flob Sep 29 '11 at 12:50
  • @Deepak - you have, but someone obviously have me -1. I was wondering which part he thinks is wrong – Bozho Sep 29 '11 at 13:02
  • @EJP because that is his problem, that's why it "doesn't work". Programmers don't post just "it doesn't work", they post exceptions/errors. He then updated the question, btw, so I'll remove the note – Bozho Sep 30 '11 at 05:55
3

You check if parseInt returns a number smaller than 0 to see if the input is non-numeric.

However, that method doesn't return any value at all, if the input is non-numeric. Instead it throws an exception, as you have seen.

The simplest way to do what you want is to catch that exception and act accordingly:

try {
  Integer.parseInt(str[i]);
  // str[i] is numeric
} catch (NumberFormatException ignored) {
  // str[i] is not numeric
}
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • 1
    Why are people suggesting this. Whether it is a number or not is a normal part of this process, it is not exceptional nor an error. Using exception handling in this way is wrong and given that is likely to happen 50% of the time it is going to be inefficient – vickirk Sep 29 '11 at 12:55
  • @vickirk: people suggest this because the Java API is crappy in this regard and there's no simple way to check "is this parseable as an `int`" without exception handling. Yes, third-party libraries do this, but that's not really relevant when your still learning. – Joachim Sauer Sep 29 '11 at 13:00
  • 1
    I'd argue figuring a simple way to check a string contains digits is very simple task, ideal for learning, rather than picking up bad habits from the start. – vickirk Sep 29 '11 at 13:13
  • @vickirk: possible. But before you can do that, you must first understand **why** the posted code doesn't work. – Joachim Sauer Sep 29 '11 at 13:15
  • good point on pointing out the cause, not convinced about the cure – vickirk Sep 29 '11 at 13:21
  • @vickirk this seems like a circular argument to me. You are saying not to use exceptions because it isn't an exceptional condition so you should use normal flow. But this is just begging the question. The fact is that there *is* an exception and you do have to catch it, and Integer.parseInt() *already* checks the input for being numeric. So why do all that twice? – user207421 Sep 30 '11 at 00:05
  • 1
    @EJB, because exception handling is expensive and despite what people have sadi are harder to follow, if I'm looking at the code trying to figure out what it does I should not need to look at the exception handling, thats for the exceptional cases, i.e. ERRORS, I'll look at that after i know what it's doing f I need to, many people use folding editors to hide that code for general browsing/editing. The fact that there is an exception for this as the string may not be a number when the caller believes it is, but in this case we know it is not going to be in 50% of cases, hardly exceptional. – vickirk Oct 03 '11 at 11:55
3

If you want to check if the string is a valid Java number you can use the method isNumber from the org.apache.commons.lang.math (doc here: http://commons.apache.org/lang/api-2.4/org/apache/commons/lang/math/NumberUtils.html).

This way you won't have to write your own implementation of isNumber

talnicolas
  • 13,885
  • 7
  • 36
  • 56
1

You need to use a try/catch block instead of testing the return value for parseInt.

try {
    Integer.parseInt(str[i]);
    m.put(str[i],true);
} catch(NumberFormatException e) {
    m.put(str[i],false);
}
Charles Goodwin
  • 6,402
  • 3
  • 34
  • 63
  • Using a try catch as regular flow control should be avoided, it would be much better to do a regex check – vickirk Sep 29 '11 at 12:52
  • @vickirk But *is* it regular flow control? Or is it handling the error cases? – user207421 Sep 30 '11 at 00:02
  • @EJP regular control is an if else statement, this is using an error handling mechanism provided for catching and recovering from that error, would you write a check if you didn't know how much was in your account, would you just let the bank bounce the cheque before you decided to pay with your credit card. the fact that the return value of parseInt is not even used screams out that something is not right. Any automatic code validation is going to report that, which would cause a failure of many people's QA proceedure – vickirk Oct 03 '11 at 12:03
1

Your error occurs here:

if(Integer.parseInt(str[i]) < 0){

Integer.parseInt throws a NumberFormatException when the input isn't a number, so you need to use a try/catch block, for example:

try{
    int number = Integer.parseInt(str[i]);
    m.put(str[i],false);
}catch NumberFormatException nfe{
    m.put(str[i],true);
}
spikeheap
  • 3,827
  • 1
  • 32
  • 47
  • 3
    Abuse of exception handling, forgive me if I've gone on about this a bit but it is just wrong, completely wrong, it is expected that these string are not always strings – vickirk Sep 29 '11 at 13:10
  • @vickirk it is wrong, but not so ultimately wrong. If you don't have an utility method provided, it is a problem of the designers of the Integer class, rather than yours. They didn't give you a validation utility rather than the exception, and they should have. So you just use their validation utility, which, unluckily, is the exception. – Bozho Sep 29 '11 at 13:42
  • 1
    This is not abuse of exception handling, but the intended use of the method. I have assumed that this trivial example is indeed trivial, and the poster will intend to use the value they have deemed to be an integer, in which case you'll have to catch the exception anyway. – spikeheap Sep 30 '11 at 14:59
1

Assuming you won't use any external libraries, you can also use a Regular Expression Matcher to do that. Just like

for (String element : str) {
     m.put(element, element.matches("\\d+"));
}

Note that this works only with non-negative integers, but you can adapt the regular expression to match the number formats you want to map as true. Also, if element is null, you'll get a NullPointerException, so a little defensive code is required here.

1

Here is an improved answer which can be used for numbers with negative value, decimal points etc. It uses Regular Expressions.

Here it it:

public class StringValidator {

    public static void printMap(Map<String, Boolean> map) {
        Iterator it = map.entrySet().iterator();
        for(Map.Entry<String, Boolean> entry:map.entrySet()){
            System.out.println(entry.getKey()+" = "+ entry.getValue());
        }
    }
}

class ValidateArray{
        public static void main(String[] args) {
        String str[] = {"abcd", "123", "101.112", "-1.54774"};
        Map<String, Boolean> m = new HashMap<String, Boolean>();
        for (String s : str) {
            m.put(s, isNumber(s));
        }
        StringValidator.printMap(m);
    }

    public static boolean isNumber(String str) {
        Pattern pattern = Pattern.compile("^-?\\d+\\.?\\d*$");
        Matcher matcher = pattern.matcher(str);
        return matcher.matches();
    }
}
Sastrija
  • 3,284
  • 6
  • 47
  • 64
0

Here's a more general way to validate, avoiding exceptions, and using what the Format subclasses already know. For example the SimpleDateFormat knows that Feb 31 is not valid, as long as you tell it not to be lenient.

import java.text.Format;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

public class ValidatesByParsePosition {

    private static NumberFormat _numFormat = NumberFormat.getInstance();
    private static SimpleDateFormat _dateFormat = new SimpleDateFormat(
            "MM/dd/yyyy");

    public static void printMap(Map<String, Boolean> map) {

        for (Map.Entry<String, Boolean> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }

    public static void main(String[] args) {

        System.out.println("Validating Nums with ParsePosition:");
        String numStrings[] = { "abcd", "123", "101.112", "-1.54774", "1.40t3" };
        Map<String, Boolean> rslts = new HashMap<String, Boolean>();
        for (String s : numStrings) {
            rslts.put(s, isOk(_numFormat, s));
        }
        ValidatesByParsePosition.printMap(rslts);

        System.out.println("\nValidating dates with ParsePosition:");
        String dateStrings[] = { "3/11/1952", "02/31/2013", "03/14/2014", 
                "05/25/2014", "3/uncle george/2015" };
        rslts = new HashMap<String, Boolean>();
        _dateFormat.setLenient(false);
        for (String s : dateStrings) {
            rslts.put(s, isOk(_dateFormat, s));
        }
        ValidatesByParsePosition.printMap(rslts);
    }

    public static boolean isOk(Format format, String str) {

        boolean isOK = true;
        int errorIndx = -1;
        int parseIndx = 0;

        ParsePosition pos = new ParsePosition(parseIndx);

        while (isOK && parseIndx < str.length() - 1) {
            format.parseObject(str, pos);
            parseIndx = pos.getIndex();
            errorIndx = pos.getErrorIndex();
            isOK = errorIndx < 0;
        }

        if (!isOK) {
            System.out.println("value \"" + str
                    + "\" not parsed; error at char index " + errorIndx);
        }

        return isOK;
    }

}
R.D. Alkire
  • 502
  • 1
  • 5
  • 14
0

Replace your parseInt line with a call to isInteger(str[i]) where isInteger is defined by:

public static boolean isInteger(String text) {
  try {
    new Integer(text);
    return true;
  } catch (NumberFormatException e) {
    return false;
  }
}
Guillaume
  • 22,694
  • 14
  • 56
  • 70
0

I would like to enter the contrary view on 'don't use exception handling' here. The following code:

try
{
  InputStream in = new FileInputStream(file);
}
catch (FileNotFoundException exc)
{
  // ...
}

is entirely equivalent to:

if (!file.exists())
{
  // ...
}
else
try
{
  InputStream in = new FileInputStream(file);
}
catch (FileNotFoundException exc)
{
 // ...
}

except that in the former case:

  1. The existence of the file is only checked once
  2. There is no timing-window between the two checks during which things can change.
  3. The processing at // ... is only programmed once.

So you don't see code like the second case. At least you shouldn't.

The present case is identical except that because it's a String there is no timing window. Integer.parseInt() has to check the input for validity anyway, and it throws an exception which must be caught somewhere anyway (unless you like RTEs stopping your threads). So why do everything twice?

The counter-argument that you shouldn't use exceptions for normal flow control just begs the question. Is it normal flow control? or is it an error in the input? [In fact I've always understood that principle to mean more specifically 'don't throw exceptions to your own code' within the method, and even then there are rare cases when it's the best answer. I'm not a fan of blanket rules of any kind.]

Another example detecting EOF on an ObjectInputStream. You do it by catching EOFException. There is no other way apart from prefixing a count to the stream, which is a design change and a format change. So, is EOF part of the normal flow, or is it an exception? and how can it be part of the normal flow given that it is only reported via an exception?

user207421
  • 305,947
  • 44
  • 307
  • 483
  • @downvoter please explain. Otherwise, and specifically unless you address the argument I have advanced here, you're just convincing me and everybody else that this 'don't use exceptions for flow control' stuff is just an unexamined and indefensible dogma. – user207421 Oct 04 '11 at 01:43
  • Not me, however probably as it is completely unrelated to the ops original question, but comments I made elsewhere. Try googling for "java exception flow control" for a wealth of best practice advice, or just see http://stackoverflow.com/questions/1546514/java-exceptions-as-control-flow I'd have commented on your post but don't want to get dragged into this any more, I've got work to do. – vickirk Oct 04 '11 at 11:28
  • Once again this just begs the question, which is whether it really *is* best practice, or merely an unexamined dogma whose original motivation has been forgotten in the mists of time: but not by me, because I was around at the time. My argument from opening files remains completely unaddressed, and that example is also unarguaby best practice for the reasons I have stated here. – user207421 Oct 07 '11 at 15:57
-1
boolean intVal = false;
    for(int i=0;i<str.length;i++) {
            intVal = false;
            try {
                if (Integer.parseInt(str[i]) > 0) {
                    intVal = true;
                }
            } catch (java.lang.NumberFormatException e) {
                intVal = false;
            }
        m.put(str[i], !intVal);
    }
Vaandu
  • 4,857
  • 12
  • 49
  • 75
  • 1
    Same for my downvote... Just trying to help! – Jean Logeart Sep 29 '11 at 13:43
  • Probably for the same reason I downvoted others, which going off the way some counts have gone negative at times other people are downvoting too, it uses exception handling for logic checking. I didn't down vote this particular one or a number of others that appeared after my answer and without commenting, that would be wrong given I added an answer – vickirk Sep 29 '11 at 13:49