Checking if a HashMap is empty without causing NullPointerException / IndexOutOfBoundsException? - java

So am doing a mail server kind of program for my java course, (I would call myself a beginner in Java) and while I have completed all the required parts, there are some problems with the program that while the assignment doesn't require it, I would like to know why this is behaving the way and how to fix it. So to summarize it I have two methods that check how many mail items a specific user has and another one that returns the mail item that is first in the users mailbox, (by LIFO rule).
So while if a user both A.Has a mailbox and B.Has at least 1 mail in said mailbox there isn't a problem, this doesn't really work when a and/or b isn't true. And that is what I tried to fix. I thought it would be as simple as checking both if the mailbox.containsKey(who)==false and/or mailbox.get(who).get(0) == null would be enough but when I do this i get java.lang.IndexOutOfBoundsException: Index: 0, Size: 0.
I've tried multiple types of either checking if it's ==true/false null etc.. I've tried to just throw the exception all together but nothing seems to work and while maybe throwing the exception could work in some way I would want to solve it in another way as the throw command seems a bit lazy
Here is my method in which I check both A and B:
private boolean errorHandling(String who){
if (mailbox.containsKey(who)==false || mailbox.get(who).get(0) == null ){
return true;
}
else{
return false;
}
}
and this method is utilized by these two methods:
public MailItem getNextMailItem(String who)
{
if (!errorHandling(who) ){
MailItem item2 = mailbox.get(who).get(0);
mailbox.get(who).remove(0);
return item2;
} else{
System.out.println("that user dosent have a mailbox");
return null;
}
}
public int howManyMailItems(String who)
{
if (!errorHandling(who) ){ //To check if user has a mailbox
return mailbox.get(who).size();
}
else{
System.out.println("that user dosent have a mailbox");
return 0;
}
}
I am not sure if you need more of my code to help me or not. Tell me if that is the case but to clarify, the "mailbox" is the hashmap and the who parameter is the person you want to check his or her mailbox. Thank you.

There are couple of problems in our code which i listed below
First Problem
The if condition is wrong and which will be true even if map doesn't contain key
if (mailbox.containsKey(who)==false || mailbox.get(who).get(0) == null)
// mailbox.containsKey(who) will return false if map doesn't contain key
// then false==false ---> always true
So you are returning true even key is not present in map, And there is not necessity of errorHandling method. you can just use containsKey
if (containsKey(who) ){ // if key exists then proceed
MailItem item2 = mailbox.get(who).get(0);
mailbox.get(who).remove(0);
return item2; }
else{ // else don't proceed
System.out.println("that user dosent have a mailbox");
return null;
}
You can even avoid this `if condition by using getOrDefault
default V getOrDefault(Object key, V defaultValue)
Second Problem
Second part of if condition is wrong mailbox.get(who).get(0) == null you might have empty list for that key, so you can refactor your code as below
List<MailItem> listItem = mailbox.get(who, new ArraysList<>()); //get list if key exists or else get empty list
if(!listItems.isEmpty()) {
return listItems.remove(0); //if list has items delete at index 0
}
else{
System.out.println("that user dosent have a mailbox");
return null;
}

Related

throwing an exception and continuing to receive data

In a piece of code I'm currently working on, I'm taking in an ArrayList of data to be added to an object, from a user. I'm checking each item in the ArrayList against the current list of that type of data for the object, in order to make sure it's not in the list already.
Is there a way to throw an exception for one item in the passed- in list, to tell the user it's in the list already- and then to keep going and add the next items in the passed-in list to my current list if they aren't there already?
Update: i solved the problem by surrounding that block of code with a try/catch. Here's my code to clarify:
public void addCategories(ArrayList<BookCategory>categories) {
boolean exists;
for(int index = 0; index <categories.size(); index++) {//iterate through passed array list
try {
//for each element, check if it exists in the current category list.
exists = checkBookCategory(categories.get(index));
if (exists == false)
{subjectCategories.add(categories.get(index));}
else {
throw new IllegalArgumentException("Item " + categories.get(index) + " already in list."); }
}catch(IllegalArgumentException ie) {
System.out.println(ie);
} }
}
Thanks #JimGarrison!
The short answer is that yes, you can do this, but it usually is highly discouraged.
Some sample pseudocode:
for (Item i : inputList)
{
try
{
myObject.addItem(i);
}
catch (MyCustomDuplicateItemException ex)
{
// Tell the user there was a duplicate
}
}
However, this is using exceptions for what should be flow control. Ideally you would write the addItem() method to return a boolean value (i.e. true) if the item was successfully added, and the other value (false) if the item was a duplicate and NOT throw an exception.

Do i also need to return string outside if-else condition ,when the return type is String

The compiler keeps telling to return string.I used if-else condition and from within if-else i have returned string.The compiler problem will be solved if i put return statement at the end of the code but my problem will start .I dont understand how to solve this problem.I want to return right when its right.
Code:
public String isValidID(String id) {
if(getId().length()!=6) {//checks for 6 digit id
return "wrong";
}
else{//checks if digit contains invalid entry
for(int i=0;i<getId().length();i++){
if(Id.charAt(i)>'9'||Id.charAt(i)<'0')
return "wrong";
}
}
}
In reply to my comment to add return "right" at the end, you said:
but that will make the program return "right " always.Becoz after if-else condition check ,the compiler will execute rest statement and will return "right" always
Now the source of your confusion is clear: return doesn't just set the value the method will return, it also exits the method, immediately. So for instance, the return "wrong"; inside your loop will exit from the loop, and the method, immediately. This is true of all the languages I've seen that use the return keyword (whereas there are some languages, like VB/VB.Net, where you assign a value to the function's name to set the return value, but execution continues normally until you use "exit").
That's why adding return "right"; at the end is how you resolve this, because the code won't reach that point if it ever reached return "wrong"; during the program flow above it:
public String isValidID(String id) {
if(getId().length()!=6) {//checks for 6 digit id
return "wrong"; // <== Exits immediately if reached
}
else{//checks if digit contains invalid entry
for(int i=0;i<getId().length();i++){
if(Id.charAt(i)>'9'||Id.charAt(i)<'0')
return "wrong"; // <== Exits immediately if reached
}
}
return "right"; // <== Exits immediately if reached
// (granted, we would have exited
// below anyway :-) )
}
In any condition, you need to return "something". In your code it possible that return never executed in a certain condition. Suppose your program execution comes to if(Id.charAt(i)>'9'||Id.charAt(i)<'0') and it never gets true then what the method will return? So, You need to write the code in a manner that in condition method execution will execute a return statement which returns a String object.
Just imagine a conditon Suppose
1. getId().length()!=6 -> false
2. getId().length() is 0
3. for(int i=0;i<getId().length();i++) will never enter the loop.
Then what should the method returns when you call it.
If I can understand your logic you can just use :
public String isValidID(String id) {
return id.matches("\\d{6}") ? "correct" : "wrong";
}
You check the length if it is 6 or not then you check if all characters are digits, you can combine both of them in one instruction, just check if the input is a number with length 6 with regex.
If you want a clean solution use boolean instead of a String in this case you can use :
public boolean isValidID(String id) {
return id.matches("\\d{6}");
}
Inside your else you have another if statement, so your return its not always reached. You need another return after the for cycle.
And you are checking if something is wright or wrong you should return Boolean true or false.
public boolean isValidID(String id) { if(getId().length()!=6) {//checks for 6 digit id return false; }
else{//checks if digit contains invalid entry for(int i=0;i'9'||Id.charAt(i)<'0') return false; }
return true;// when nothing false was found.
}
}
So in your case, I would have done public boolean isValidID instead.
Here, the compiler tells you to return something in case the length of the ID is not correct and the components of the ID (so the caracters) are between 0 and 9 (for example if your ID is something like 00ggg89, then I suppose it is wrong, but if your ID is 000000, then it could be right. Here is what I would have done
public boolean isValidID(String id) {
return id.matches("[0-9]{6}");
}
Hope this helps ! :D
public String isValidID(String id) {
String result = "right";
if(getId().length()!=6) {//checks for 6 digit id
result = "wrong";
}
else{//checks if digit contains invalid entry
for(int i=0;i<getId().length();i++){
if(Id.charAt(i)>'9'||Id.charAt(i)<'0')
result = "wrong";
}
}
return result;
}
Edit: In case the first statement is not valid it will never get to a return statement. Thats why you have to return a String in every possible case.

Better way of returning from condition

Which way of returning from condition is better , Like the process1 and process 2 both does the same. But I want to know better way returning.
In both cases I don't want to enter inside of loop, I just want to return. I would like to know that, Is there any performance difference If I put return before control passes to end. I don't want Java Virtual Machine to check end of loop and returning from there. I thought If I put return Immediately when the condition not satisfied, then I could see minor performance difference and also code readability. Please suggest me the best way.
Let us consider the below scenarios.
Process1:
public Method()
{ //Method
Company company = new Company(); //Object
if (null != Address && null = Address.location()) //Condition
{
return company; //I want to return
}
for (Location location: Address.location())
{
//forloop
}
return company; //return
}
Process2:
public Method()
{
Company company = new Company();
if (null != Address && null != Address.location())
{
//enters loop
}
return company; // return
}
There will be some performance impact. Iterating complete objects from the for loop to verify the condition.
For example:
We can write like this.
if(condition is false){
return ;
else{
for(DataType ref: collection){
if(true){
return;// return from here, so that it will not iterate remaining elements.
}
}
}
ex 2:
if there is a logic after the if and that should not be executed, if the object is null.
if(object is null){
return ;
}
//Remaining logic here will not be executed, if the object is null. it's a good way of writing.
ex 3:
if there is no logic after the if and else, then directly return from the end of method.
if(object is null){
return
}else{
//process logic and return.
}
you can write something like this.
if(object is not null){
// return either from here.
}
return here is also fine...

DFS tree traversal function modification

Please find below my implementation for DFS.
protected void DFS(String search) {
for(Tree<T> child : leafs) {
if(child.value.equals(search))
return;
else
child.DFS(search);
System.out.println(child.value);
}
}
The objective is to stop traversal on finding the node whose value is in the variable search. However, the above function goes on traversing the tree even beyond the declared search node. Could someone help me modify the above function?
Thank you.
Edit 1
protected boolean DFS(String anaphorKey) {
boolean found = false;
for(Tree<T> child : leafs) {
if(child.head.equals(anaphorKey))
return true;
found = child.DFS(anaphorKey);
if(found == true)
break;
System.out.println(child.head);
//System.out.println("anaphorKey: "+anaphorKey);
}
return found;
}
Tried implementing the given answer suggestion (#SJuan76). The implementation above isn't working as desired. Could you point me to the place where code is not as per the logic suggested?
rookie, might I suggest an implementation using the classic for-loop (as opposed to the enhanced for-loop being used now) which allows integration of your stop-condition a bit better, something like:
protected boolean DFS(String key) {
boolean found = false;
for(int i = 0; i < leafs.size() && !found; i++) {
Tree<T> child = leafs.get(i);
if(child.head.equals(key))
found = true;
else
found = child.DFS(key);
}
return found;
}
So as soon as your found condition is hit, the 'found' becomes true and your loop stops.
What you may have forgotten is the "found = child.DFS(key)" portion of the recursion, where you need to remember the result of your recursive calls so ALL your for-loops on up the chain all break as soon as you return.
Hope that helps.
Option A (Nice): the function returns a value, when the node is found it returns a different value that if the node was not found. When you call to method, if you get the found value you stop the loop and return the found value too.
Option B (Ugly): When found, thow an Exception (better if it is your own implementation of it). Don't forget to catch it.
Option C (Uglier): The same with global (static) variables.
UPDATE 1:
It looks like your method should run ok now, can you check (System.out.println) if your value is ever found?
In a more personal opinion, I would find
protected boolean DFS(String anaphorKey) {
for(Tree<T> child : leafs) {
if(child.head.equals(anaphorKey))
return true;
if(child.DFS(anaphorKey)) // No need to store value. No need to check == true (it is implicit)
return true; // If we are in this line the value was found, always return true
System.out.println(child.head);
//System.out.println("anaphorKey: "+anaphorKey);
}
return false; // If the method did not exit previously it was because the value was not found, so in this line always return false
}
more readable (but it should work exactly as your implementation)

Error with return in Java?

I get an error in the code from this part of my code:
public boolean findCustomer(String inPersonalNumber){
// check if personal number already exist
for (int i=0; i<customerList.size();i++) {
if(customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)){
return true;
}
}
return true;
}
When I remove the first return true and instead to the last return true, it don't get the error in my eclipse code, but why can't I have the first place and would this be the same? Thanks!
EDIT: The error message from eclipse say: This method must return a result of type boolean. I'm confused because isn't that what I have done?!
Yes, a break must be in the code
Can I write the method in some other way?
EDIT NUMBER 2
Why isn't this code working?
public boolean findCustomer(String inPersonalNumber){
// check if personal number already exist
for (int i=0; i<customerList.size();i++) {
if(customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)){
return true;
}
else {
return false;
}
}
}
This method returns a boolean value so I don't understand why I get an error!? The code looks right to me?
Your edit #2 doesn't compile because there is a possibility that your code won't enter the for-loop. This will be the case if customerList.size() is 0. To fix this, you'll simply need to add a return statement after the for-loop as well:
// check if personal number already exist
for (int i=0; i<customerList.size();i++) {
if(customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)){
return true;
}
else {
return false;
}
}
return false;
Another point here is that this code doesn't logically make much sense: it will only return true or false based on the first item in your list. And this is probably not what you want. So take a closer look at several of the other answer here, many of which are good examples for how you can do this.
public boolean findCustomer(String inPersonalNumber){
boolean result = false;
// check if personal number already exist
for (int i=0; i<customerList.size();i++) {
if(customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)){
result = true;
break;
}
}
return result ;
}
When I remove the first return true and instead to the last return
true, it don't get the error in my eclipse code, but why can't I have
the first place and would this be the same?
If you remove the second return statement the code would be able to run and not return a value - this is not possible as you defined the method to have a return type of Boolean. So it must always return a value no matter what.
Just change the second return statement to false, should do what you want.
Looks like you have turned off the Build Automatically feature of eclipse. It maybe complaining about an error that used to be present when you still hadn't typed in your code fully! This can also happen if you have back-dated your system for some reason.
Also, shouldn't you be returning false if the condition doesn't satisfy?
public boolean findCustomer(String inPersonalNumber) {
// check if personal number already exist
for (int i = 0; i < customerList.size(); i++) {
if (customerList.get(i).getCustomerPersonalNumber().equals(inPersonalNumber)) {
return true;
}
}
return false;
}
First return will return only in case of all conditions satisfied, but this method should be returning boolean as per code. It would be expecting a return in failure case also.
Removing first return won't affect compilation as it has a return in second place which will work without any condtions.
Edit : Answer for your second question
This code has two return's, but what if your customerList is size 0, in that case also, method must return boolean. right? for that only, compiler is asking.
BTW, code doesn't have null checks.
Your final code could be this. Keeping multiple return statements in code in not a good practice.
public boolean findCustomer(String inPersonalNumber) {
boolean retVal = false;
if (!(inPersonalNumber == null || inPersonalNumber.trim().equals("")
|| customerList == null || customerList.size() == 0)) { // inputs are valid to run this check
// check if personal number already exist
for (int i = 0; i < customerList.size(); i++) {
if (inPersonalNumber.equals(customerList.get(i).getCustomerPersonalNumber()) { // to avoid NPE, kept inPersonalNumber in check
retVal = true;
break;
}
}
}
return retVal;
}
Because your for loop looses meaning if you're returning true anyway.
If you want to stop loop use break; instead of first return.

Categories