LinkedList.pollLast() выдает исключение NullPointerException

#java #linked-list #nullpointerexception #deque

#java #связанный список #исключение nullpointerexception #отмена

Вопрос:

Я использую Java 6 Collecetions API. Мне нужна коллекция, в которой должно быть только N элементов. Я имею в виду, что если я добавлю новый элемент, а в коллекции уже есть N элементов, то последний элемент должен быть удален, а новый добавлен в начало коллекции. Для этого у меня есть следующий фрагмент кода:

 class A {

  int N = 100;
  Deque dq = new LinkedList();

  void add(Object o) {
    synchronized (o) { 
      if (dq.size() == N) {
        dq.pollLast();
      }
      dq.add(o);
    }
  }

  Deque getDq() {
    return new LinkedList(dq);
  }
}
  

К объекту с типом A могут обращаться многие пользователи одновременно, чтобы добавить новый элемент. На практике я получил исключение NullPointerException с ним:

 Caused by: java.lang.NullPointerException
   at java.util.LinkedList.remove(LinkedList.java:790)
   at java.util.LinkedList.removeLast(LinkedList.java:144)
   at java.util.LinkedList.pollLast(LinkedList.java:573)
   at A.add(A.java:9)
  

В контракте Deque.pollLast() ничего не говорится об исключении NullPointerException:

Извлекает и удаляет последний элемент этого списка или возвращает null, если этот список пуст.

Также синхронизируется добавление элементов.

Кто-нибудь знает, в чем может быть причина исключения?

Спасибо за любые идеи

Комментарии:

1. Вы должны синхронизировать общий объект (т. Е. список this или какой-либо специальный объект блокировки), а не параметр. Хотя понятия не имею, вызывает ли это вашу проблему.

2. Это тот код, который вы используете? Если да, то на каком языке вы используете? Dequeu не имеет открытого поля size , к которому вы могли бы получить доступ подобным образом.

3. @user: это метод, вы получаете к нему доступ без () . Я знаю, что это, вероятно, опечатка, но это означает, что код, который вы нам показываете, не является кодом, в котором проблема. Пожалуйста, создайте SSCCE .

4. Все обращения к dq правильно синхронизированы? Если нет, то это, вероятно, ваша основная проблема.

Ответ №1:

Я предполагаю, что сикронизация выполнена не для того объекта! Должно быть, dq но не o !

 ... synchronized (dg) { ...
  

Комментарии:

1. Я согласен с этим. Вы не предотвращаете одновременный доступ к вашему Deque, блокируя элемент, который вы пытаетесь добавить / удалить.

Ответ №2:

Я запустил добавление вашего кода, используя следующий тест

     A a = new A();
    for (int i = 0; i < 200; i  )
    {
        a.add(i);
    }
    System.out.println(a.dq);
  

И все это, кажется, работает правильно. Можете ли вы предоставить более подробную информацию о состоянии приложения, когда получите NPE? Что это за объект, который вы пытаетесь добавить? Каково состояние удаления из очереди в это время?

Кроме того, вы упомянули

если я добавляю новый элемент, а в коллекции уже есть N элементов, то последний элемент должен быть удален, а новый добавлен в начало коллекции

Ваш код этого не делает. Прямо сейчас оно добавляется в конец коллекции. Чтобы добавить его в заголовок, измените

 dq.add(o)
  

Для

 dq.addFirst(o)
  

Комментарии:

1. Я думаю, что реальной проблемой здесь является синхронизация, а не сам алгоритм. Хотя это может быть и неправильно.

2. Очень верно. Я пытался исключить проблемы с алгоритмом и запросить дополнительную информацию о том, как используется dq и каково его состояние. Вероятно, dq используется каким-то другим несинхронизированным способом через средство доступа getDq() (как упоминали другие).

Ответ №3:

смотрите этот javadoc, в нем говорится

  Removes and returns the last element from this list.
  

сначала он удаляет объект, поэтому, если он равен null, тогда генерируется исключение NullPointerException:

поэтому синхронизируйте метод add (..) и проверьте размер, прежде чем dq.pollLast();

Комментарии:

1. LinkedList.pollLast() не выдает исключение, если код хорошо синхронизирован: