【问题标题】:How to get rid of NullPointerException when removing elements from array?从数组中删除元素时如何摆脱 NullPointerException?
【发布时间】:2022-11-23 01:19:52
【问题描述】:

给出以下任务。我们有一个 Employee 和一个 Company 类。 Employee类的每个实例都存储在Company类的数组Employee[] employees中。我需要一个方法来删除 id 数组 Employee[] employees 中的 Employee 实例。

我设法编写了以下代码:

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. 我如何重写方法 public Employee removeEmployee(int id),例如,使用 Streams API 和 Optional 以去除方法 public Employee removeEmployee(int id) 中的 NullPointerException?

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

【问题讨论】:

  • 这是我见过的最复杂的维护和删除简单对象列表的代码。为什么不将员工存储在 List&lt;Employee&gt; 中并避免重建数组的所有麻烦?我也不明白为这个操作使用流的复杂性。在通过流构建单独的列表后,您最终会遍历整个数组。我不明白那能给你带来什么。
  • 为什么将员工保留为数组而不是List?它是更适合您的用例的数据结构,并且您已经在 removeEmployee 方法中使用了一个列表。
  • 是否绝对需要返回已删除的 Employee 对象?还是仅仅知道某个员工是否被删除就足够了?
  • 如果这是家庭作业,我认为您也不应该使用流。你的作业可能要求你学习如何调整数组的大小、移动它的元素、计算当前元素等等。至少你应该尝试以这种方式实现它。
  • 通常,如果作业告诉您使用arrays,则表示只是arrays - 这个想法是你必须学会​​自己做这些细节。因此,您仍然不应该混入所有更高级的功能,如流。

标签: java arrays nullpointerexception java-stream


【解决方案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 而不是 employees 的 Array,因为这会使事情变得更容易和更快:

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;

}

【讨论】:

  • 请查看How do I ask and answer homework questions?。直接向学生提供解决方案完全不符合他的最大利益。
  • 你是对的。但是,由于只有最后一个函数才有资格作为任务的有效解决方案,因此我有意没有提供完整的解决方案。我认为完成 arraycopy 函数的参数需要了解解决方案的工作原理;)
【解决方案2】:

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

你应该做的是找到目标为id 的元素,如果存在这样的元素,则取消其位置。

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

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

如果你想做一些奇怪的事情并不惜一切代价使用 Stream API 和 Optional,这里是如何完成的(但我建议坚持使用上面的代码):

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()
        .findFirst();
    
    Optional<Employee> result = index.map(i -> employees[i]);
    index.ifPresent(i -> employees[i] = null);
    
    return result;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-23
    相关资源
    最近更新 更多