0

I'm a newbie in Java, but i'm taking a course where we write a web-app which is able to save, store, edit and show resumes. I'm stuck with the 'save' method, where we send a resume as parameter and have to check by resume's UUID:

  • if the UUID already exists in the array - an error message will be shown,

  • if the UUID is not in the array - the resume will be saved.

I've made my method with a temp boolean, but according a task it's not right, so i'm trying to solve it with a for loop and an if statement with break, but i can't understand how to save the resume because NPE throw in if statement. Here is my code:

    public class ArrayStorage {
    private Resume[] storage = new Resume[10000];
    private int size;

    public void save(Resume resume) {
        for (int i = 0; i <= size; i++) {
            if (storage[i].getUuid().equals(r.getUuid())) {
                System.out.println("ERROR: Resume with " + storage[i].getUuid() + " is already exist!");
                break;
            }
            storage[size] = resume;
            size++;
        }
    }

Size variable is there to avoid checking the whole array, just elements that were stored.

Edited. I forgot an equal sign in a for loop, and this is an error

Exception in thread "main" java.lang.NullPointerException
at com.webapp.storage.ArrayStorage.save(ArrayStorage.java:23)
at com.webapp.MainTestArrayStorage.main(MainTestArrayStorage.java:22)

In MainTestArrayStorage i have those fields

 Resume r1 = new Resume();
    r1.setUuid("uuid1");
    Resume r2 = new Resume();
    r2.setUuid("uuid2");
    Resume r3 = new Resume();
    r3.setUuid("uuid3");
    Resume r4 = new Resume();
    r4.setUuid("uuid1");

    ARRAY_STORAGE.save(r1);
    ARRAY_STORAGE.save(r2);
    ARRAY_STORAGE.save(r3);
    ARRAY_STORAGE.save(r4);

The last line is for check method, that shouldn't save it, because it has the same UUID If you need any additional information i'll be glad to answer. Thanks.

P.S. Sorry for my English, i'm not good at it.

  • Possible duplicate of [What is a NullPointerException, and how do I fix it?](https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – default locale Aug 08 '18 at 12:53
  • Could you please post code of `Resume` class ? – NullPointer Aug 08 '18 at 12:54
  • Did you debug and check what is null? I'd assume it's `resume` or the its uuid. Also using a map might be better but that could be part of a future lesson so feel free to ignore that. – Thomas Aug 08 '18 at 12:54
  • Most probably, you're not initializing elements of your array. Check out this answer: https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it#23852556 Many people face NullPointerExceptions daily, that's why we have a canonical question (I've posted a link above). Please, read it carefully, you'll be able to identify the problematic null object with some debugging. – default locale Aug 08 '18 at 12:54
  • 1
    `private Resume[] storage = new Resume[10000];` Creates an array with 10000 null objects – GBlodgett Aug 08 '18 at 12:55
  • 1
    Init size with 0 – dWinder Aug 08 '18 at 12:57
  • 1
    @GBlodgett that's right but have a look at how he's iterating over the array: only over the first `size` elements, i.e. when the array is empty, there's no iteration at all. After that the element at index 0 is should be a resume (assuming the parameter is not null) and thus the next call should only check that one. – Thomas Aug 08 '18 at 12:58
  • @NullPointer Resume contains only String uuid with getter and setter. – Dmitrii Kozlovskii Aug 08 '18 at 13:06
  • @DmitriiKozlovskii Thank you for the code. Where have you initialized `size` in the code ? In the code current state, It will never go inside the for loop. – NullPointer Aug 08 '18 at 14:02
  • @NullPointer It goes now inside the loop and stuck on if statement with NPE.Thanks to your answer! – Dmitrii Kozlovskii Aug 08 '18 at 14:17
  • @DmI have tried to debug the program based on our inputs but for me it is not going inside the loop. Something is missing in the given code. Could you please post the code class by class ? – NullPointer Aug 08 '18 at 14:24
  • @NullPointer did you try to pass objects how i shown it below start post? – Dmitrii Kozlovskii Aug 08 '18 at 14:43
  • It should enter the for loop since `i <= size` is true (both are 0 initially). But it will throw the NPE at the if statement because `storage[0]` is null (this has nothing to do with the `Resume` class. It's just that the array contains only nulls and you call a method on it's first element). You can look at my answer to see how you can change the order of your checks to prevent this situation. – Malte Hartwig Aug 08 '18 at 14:44
  • @MalteHartwig i guess first if statement doesn't help, because it will save 'uuid1' because the last ellement (for example in my code it is 'r4' resume with 'uuid1') will be 'null' – Dmitrii Kozlovskii Aug 08 '18 at 14:51
  • 1
    @DmitriiKozlovskii I guess you edited it multiple times so I had the older version of the code **i < size** . Yes, in this case as @Malte Hartwig correctly said your `storage[0]` is null. Refer his answer. If you still have some issues you can ask. I hope you are using IDE. Just put a debugger and see the value of `storage[i]` once it reaches inside the loop. – NullPointer Aug 08 '18 at 14:54
  • 1
    The exception has nothing to do with duplicate uuids or your `Resume` type at all. Imagine you start your program fresh and the storage is empty. You call `save` for the first time. It gets to the for loop. `i` is 0. In the if statement, you get `storage[i]`, which will be null. then you call `getUuid` on it: `storage[i].getUuid()` -> `null.getUuid()` -> NPE. So even if you'd only add 1 single element, your code will already fail. – Malte Hartwig Aug 08 '18 at 14:56
  • 1
    Ok, got it. I'll read carefully all answers and will ask questions if face new issues. Thank you for your help! – Dmitrii Kozlovskii Aug 08 '18 at 15:05
  • I would recommend you to accept @MalteHartwig answer so It will help others as well. – NullPointer Aug 08 '18 at 15:07
  • 1
    I just saw you edited your post so that the condition is now `i <= size`. Was that your original code, i.e. was your question wrong at the start? I'm asking because this is critical information and is most likely the reason for your error. For future reference: __please make sure you post a [mcve], i.e. code that runs and matches yours - we can't help if what we see doesn't produce the problem or doesn't do what your code is doing__ – Thomas Aug 08 '18 at 15:16
  • @Thomas yes, it was my original code, where i got the NPE, without '<=' it doesnt dive into for loop. Thank for the link, i definitely had to check my code, before i posted it. My question is still the same - how to avoid NPE and don't save doubled 'UUID'. Thank you again,Thomas, i'm very appreciate your help. – Dmitrii Kozlovskii Aug 08 '18 at 15:46
  • Well, the "how to avoid NPE" part is easy: make sure you're not accessing any null elements, either by checking for null or by making sure what you access can't be null (e.g. `size` always reflects the number of non-null elements at the start of your array with no holes in between, you don't add any null resume and each resume always has a uuid). – Thomas Aug 09 '18 at 09:28

6 Answers6

2

I think your are confusing size and the end condition of the for loop here. The way it works in your example is that it will go through the array until it finds or duplicate or until a NPE is thrown because storage[i] is null. But because you increase both the index (i) and the stop condition (size) at the same time, you never stop. You will break when you find a duplicate, get a NPE when you find an empty slot, or an IndexArrayOutOfBoundsException when you reach the end of the full storage.

In cases like this, it is helpful to first get a very good idea of what you need to do before writing any code. If you cannot describe the method in normal words, you will have a hard time writing it.

Try this:

  • Iterate through the whole storage
  • if you find an empty slot, put the new resume into the storage
  • stop when you find a duplicate, show an error, and abort

Now write it down step by step:

public void save(Resume resume) {
    for (int i = 0; i < storage.length; i++) {
        if (storage[i] == null) {
            // good result: found an empty slot
            storage[i] = resume;
            size = i + 1;
            return;
        }
        if (storage[i].getUuid().equals(resume.getUuid())) {
            // bad result: found a duplicate
            System.out.println("ERROR: Resume with " + storage[i].getUuid() + " is already exist!");
            return;
        }
    }

    // bad result: storage full
    System.out.println("ERROR: Storage full");
}
Malte Hartwig
  • 4,477
  • 2
  • 14
  • 30
  • Notice that according the question requirement if UUID already exist then you should not save him - with your code, if there is a empty slot (remove resume of something like it) the condition will not fulfill – dWinder Aug 08 '18 at 13:06
  • That is definitely a possibility, this code is not robust against the side effects of other potential methods manipulating the `storage`. For the sake of keeping the example simple, I tried to stay close to their approach and only focused on the issues with Op's original code: unconventional looping, invalid stop condition, missing null check... – Malte Hartwig Aug 08 '18 at 13:42
  • @MalteHartwig thank you, thank you, thank you! It works now! But what the difference if i'll use 'break' instead of 'return'? I've checked with break and it also worked. – Dmitrii Kozlovskii Aug 08 '18 at 16:03
  • @DmitriiKozlovskii It is an important difference if you have code **after** the loop you want to abort. In my code example, replacing the two `return` statements with `break`s would always have it print the "Storage full" error. That's because `break` aborts only the `for` loop, while `return` aborts the whole method (i.e. also preventing the last sysout). – Malte Hartwig Aug 09 '18 at 08:56
1

You didn't tell us where you get the NPE so let's analyze your code (you should debug though):

public void save(Resume resume) {
    //size is initialized to 0, so all is fine here
    for (int i = 0; i < size; i++) {
        //now here's the candidate for the NPE
        if (storage[i].getUuid().equals(resume.getUuid())) {
            System.out.println("ERROR: Resume with " + storage[i].getUuid() + " is already exist!");
            break;
        }
        //this could cause the NPE in a later iteration
        storage[size] = resume;
        size++;
    }
}

As you can see the only part where an NPE should happen (assuming no concurrent access to that class) is storage[i].getUuid().equals(resume.getUuid()).

What could be null here?

  1. storage[i] might be null if when calling storage[size] = resume; resume was null
  2. storage[i].getUuid() might return null
  3. resume could be null
  4. resume.getUuid() could return null but that should not cause the NPE immediately

As you can see, the first 3 options might cause the NPE immediately and there could be 2 reasons for this:

  1. you pass a null resume
  2. the resume you pass has no UUID

The first reason might cause the NPE right away, due to the call resume.getUuid(). It also might cause the NPE in the second call because then storage[size] = resume; would be equivalent to storage[0] = null; (all but the first call should see size > 0).

The second reason would cause the next call to get the NPE because then you'd call equals() on a null UUID.

To fix that, and assuming a resume must always have a UUID, I'd add the following at the top of the save(Resume resume) method:

public void save(Resume resume) {
  if( resume == null || resume.getUuid() == null ) {
    //Log the error here
    return;
  }
  //the rest of your code goes here
}

UPDATE:

I just saw you changed your loop condition to i <= size which probably causes the error (and which your original question didn't contain): if size is 0 (no elements yet), you'll still execute the loop body once (because i == 0) and hence call storage[0].getUuid(), which is bound to produce an NPE because all elements in storage are still null. Change the condition to i < size and it should be ok.

Thomas
  • 87,414
  • 12
  • 119
  • 157
  • I pass the UUID from Test class where objects were created and after that pass to ArrayStorage class – Dmitrii Kozlovskii Aug 08 '18 at 13:20
  • @DmitriiKozlovskii please see my update. Also could you tell us which call to `save()` causes the NPE? Is it the first as I'd assume? – Thomas Aug 08 '18 at 15:14
  • yes, it is. In the if statement, but i also need to avoid duplicate 'uuid' and it is my obstacle too. – Dmitrii Kozlovskii Aug 08 '18 at 15:49
  • you were right, i've used answer of Malte Hartwig and everything works now sooo well! Is it ok if i ask here (in comments section) next issues with this tack? Thanks a lot! – Dmitrii Kozlovskii Aug 08 '18 at 16:09
  • @DmitriiKozlovskii it depends. If you're still struggling with the same problem then I'd say it should be ok. If it is another problem then another question would be more appropriate. – Thomas Aug 09 '18 at 09:29
0

Prior of inserting, check first if the storage is empty and initialize it with at least one element. Also verify that the storage is not full before trying to add new element.

Try this out :

public class ArrayStorage {
    private Resume[] storage = new Resume[10000];
    private int size;

    public void save(Resume resume) {
        if(size == 0){
            storage[size] = resume;
            size++;
        } else {
            for (int i = 0; i < size; i++) {
                if(size == storage.length){
                    System.out.println("ERROR: Storage is full");
                    break;
                }
                if (storage[i].getUuid().equals(r.getUuid())) {
                    System.out.println("ERROR: Resume with " + storage[i].getUuid() + " is already exist!");
                    break;
                }
                storage[size] = resume;
                size++;
            }
        }
    }
}
-1

Without the actual error message I assume that you ran into a NPE due to resume.getUuid() returning null (maybe a resume is also null to test you). I also assume that resumes in storage are valid (they have a Uuid).

public class ArrayStorage {
private Resume[] storage = new Resume[10000];
private int size;

public void save(Resume resume) {
    for (int i = 0; i < size; i++) {
        if (resumse != null 
                 && resume.getUuid() != null 
                 && storage[i].getUuid().equals(resume.getUuid())) {
            System.out.println("ERROR: Resume with " + storage[i].getUuid() + " is already exist!");
            break;
        }
        storage[size] = resume;
        size++;
    }
}
Atlantis
  • 1
  • 2
  • It has nothing to do with the `Resume` object. The exception is thrown here: `storage[i].getUuid()` because `storage[i]` is null. – Malte Hartwig Aug 08 '18 at 14:51
-1

You should use a List instead of an Array. Lists have variable size and you dont have to create ten thousand null objects. You also wouldnt have to check for the size, just iterate over all objects in the List and compare the UUIDs. Of course only if you dont HAVE to use Arrays.

Ahorn
  • 649
  • 4
  • 19
-1

Why do not you consider Set instead array? You could implement UUID-based equals and hashCode in your Resume class, then simple use HashMap.putIfAbsent to store your data.

Another approach (jdk 8+) is to use Stream.anyMath:

if (!Stream.of(storage).parallel().anyMatch(v -> v.getUuid().equals(r.getUuid())) { storage[size++] = resume; }

S. Kadakov
  • 861
  • 1
  • 6
  • 15