从数组中删除元素时如何摆脱 NullPointerException?[复制]

How to get rid of NullPointerException when removing elements from array? [duplicate]

提问人:Evgeniy 提问时间:11/23/2022 更新时间:11/23/2022 访问量:137

问:

给定以下任务。我们有一个和一个类。类的每个实例都存储在类的数组中。我需要一种方法,通过以下方式删除数组中的实例。EmployeeCompanyEmployeeEmployee[] employeesCompanyEmployeeEmployee[] employeesid

我设法编写了以下代码:

public class Employee {
    protected final int id;
    protected String name;

    public Employee(int id, String name) {
        this.id = id;
        this.name= name;
    }
    public int getId() {
        return id;
    }
}

public class Company {
    private Employee[] employees;
    private int size;
    private static final int defaultCapacity = 5;
    
    public Company() {
        this(defaultCapacity);
    }
    
    public Company(int capacity) {
        if (capacity <= 0)
             throw new RuntimeException("capacity is required");
        employees = new Employee[capacity];
    }

    public Employee removeEmployee(int id) {
        Collection<Employee> employeeList = Arrays.asList(employees)
                                                  .stream()
                                                  .filter(Objects::nonNull)
                                                  .collect(Collectors.toList());
        
        Employee[] employeeArray = employeeList.toArray(Employee[]::new);
        for (int i = 0; i < size; i++) {
            if(employeeArray[i].getId() == id) {
                Employee removedEmployee = employees[i];
                employeeList.remove(employeeArray[i]);
                employees = employeeList
                            .stream()
                            .filter(Objects::nonNull)
                            .toArray(Employee[]::new);
                return removedEmployee;
            }
        }
        return null;
    }

}

问题是,如果找不到要删除的元素,我的方法就会抛出。public Employee removeEmployee(int id)NullPointerException

问题:

  1. 例如,如何使用 Streams API 和 Optional 重写方法以摆脱方法中的 NullPointerException?public Employee removeEmployee(int id)public Employee removeEmployee(int id)

注意:成功删除元素后,必须减少类中声明的数组的长度。Employee[] employeesCompany

java 数组 nullpointerexception java-stream

评论

2赞 David 11/23/2022
这回答了你的问题吗?什么是 NullPointerException,如何修复它?
3赞 CryptoFool 11/23/2022
这是我见过的最复杂的代码,用于维护和删除简单的对象列表。为什么不将员工存储在一个中,避免重建阵列的所有麻烦呢?我也不明白使用流进行此操作的复杂性。在通过流构建单独的列表后,您最终会遍历整个数组。我不明白这能给你带来什么。List<Employee>
2赞 Chaosfire 11/23/2022
为什么你把员工保留为数组而不是?它更适合您的用例的数据结构,并且您已经在方法中使用了列表。ListremoveEmployee
2赞 Chaosfire 11/23/2022
如果是家庭作业,我认为你也不应该使用流。你的作业可能需要你学习如何调整数组的大小,移动其元素,保持当前元素的计数等等。至少你应该尝试以这种方式实现它。
4赞 Edward Peters 11/23/2022
通常,如果家庭作业告诉你使用s,它意味着s--这个想法是你必须学会自己做细节。因此,您仍然不应该混合所有更高级的功能,例如流。arrayarray

答:

2赞 Lorenz Hetterich 11/23/2022 #1

这里有很多方法可以摆脱 NullPointerException。 如果要继续使用流 API,可能需要使用 filterfindAny。 例如,可以将该方法修改为以下内容:

public Employee removeEmployee(int id) {
    Optional<Employee> employee = Arrays.stream(employees)
        .filter(Objects::nonNull)
        .filter(x -> x.getId() == id).
        .findAny();
    if(employee.isEmpty())
        return null;
    employees = Arrays.stream(employees).filter(x -> x != employee.get()).toArray(Employee[]::new);
    return employee.get();
}

但是,我强烈建议使用 List 甚至 Map 而不是 Array,因为这会使事情变得更容易、更快捷:employees

public Employee removeEmployee(int id){
    Optional<Employee> toRemove = employees.stream().filter(x -> x.getId() == id).findAny();
    if(toRemove.isEmpty())
        return null;
    employees.remove(toRemove.get());
    return toRemove.get();

}

或者不使用 Stream API:

public Employee removeEmployee(int id){
    int idx;
    for(idx = 0; idx < employees.length; idx++){
        if(employees[idx] != null && employees[idx].getId() == id)
            break;
    }
    if(idx == employees.length)
        return null;

    Employee value = employees[idx];
    
    Employee[] newArr = new Employee[employees.length - 1];

    // the parameters here are left as an exercise to the reader :P
    System.arraycopy(newArr, ...);
    System.arraycopy(newArr, ...);

    employees = newArr;

    return value;

}

评论

1赞 Chaosfire 11/23/2022
请检查如何提出和回答家庭作业问题?。直接为学生提供解决方案完全不符合他的最佳利益。
0赞 Lorenz Hetterich 11/23/2022
你是对的。但是,由于只有最后一个函数才有资格作为任务的有效解决方案,因此我故意没有提供完整的解决方案。我认为完成 arraycopy 函数的参数需要了解解决方案的工作原理;)
0赞 Chaosfire 11/23/2022
是的,但基于流的解决方案可以工作,并且可以按原样使用。使用它很可能会使他的任务失败,但最终他将一无所获。我们都知道,对于任何学生来说,拥有现成的解决方案都可能具有相当大的诱惑力:)
0赞 Lorenz Hetterich 11/23/2022
实际上,我认为即使是最后一个解决方案也会失败。虽然问题指出“长度......必须减少“,我认为实际要求是减小大小并保留一个数组,其大小为非 null 元素,后跟容量大小为 null 元素。(至少这对我来说更有意义)
1赞 Alexander Ivanchenko 11/23/2022 #2

成功删除元素后,必须减少在类中声明的数组 employees 的长度Employee[]Company

在这种情况下,Streams 不会给你带来很多好处。

您应该做的是找到具有目标的元素,如果存在这样的元素,则通过复制除找到的元素之外的所有元素,在内存中分配一个长度较小的新数组,并分配对新数组的引用。id1employees

为了减少长度,我们可以利用 .首先复制目标之前的元素,然后复制目标之后的元素。System.arraycopy()

这就是普通的基于索引的 -loop 的样子。for

public Employee removeEmployee(int id) {
    Employee result = null;
    int index = -1;
    
    for (int i = 0; i < employees.length; i++) {
        if (employees[i] != null && employees[i].getId() == id) {
            result = employees[i];
            employees[i] = null;
            break;
        }
    }
    if (result != null) {
        reduceLength(index);
    }
    return result;
}

public void reduceLength(int i) {
    Employee[] newEmployees = new Employee[employees.length - 1];
    System.arraycopy(employees, 0, newEmployees, 0, i);
    System.arraycopy(employees, i + 1, newEmployees, i, employees.length - (i + 1));
    employees = newEmployees;
}

If you want to do weird stuff and use Stream API and Optional at all costs, here how it can be done (but I would recommend to stick with the code above):

public Optional<Employee> removeEmployee(int id) {
    Optional<Integer> index = IntStream.range(0, employees.length)
        .filter(i -> employees[i] != null)
        .filter(i -> employees[i].getId() == id)
        .boxed()      // otherwise will get OptionalInt which lacks map() method
        .findFirst();
    
    Optional<Employee> result = index.map(i -> employees[i]);
    index.ifPresent(this::reduceLength);
    
    return result;
}
0赞 Chaosfire 11/23/2022 #3

Considering it's homework and constraints mentioned, i believe you are supposed to do all the work using the array only.

I'll provide some guideline and leave the actual implementation to you:

public class Company {

  private Employee[] employees;
  private int size;

  public Employee removeEmployee(int id) {
    int index = -1;
    //find the index of employee with required id, you have mostly done that
    if (index == -1) {
      return null;
    }
    //save found employee to variable
    //remove from array
    //shift array to the left

    //do not forget to use and reassign size variable where appropriate
  }

  //some extra
  public void addEmployee(Employee employee) {
    //resize array if necessary
    //add employee at correct position in array

    //do not forget to use and reassign size variable where appropriate
  }
}

If you get stuck, you can look at the class, your task is basically a simplified version of it. I strongly advise you to use this as source of inspiration only and not to copy the source code!!!ArrayList