Исключение IndexOutOfBoundsException с использованием ArrayList

#java #arraylist #combobox

#java #arraylist #поле со списком

Вопрос:

У меня есть возможность для пользователя выбрать месяц. Я установил прослушиватель ItemListener для своего выпадающего списка, который содержит список месяцев. У меня есть установленное условие, при котором, если пользователь выберет «Февраль», список дней будет только до 29, иначе «Апрель», «июнь» и так далее будут иметь 30 дней в моем выпадающем списке. Но когда я выбираю «Февраль», он работает нормально, но когда я выбираю другой месяц, я получаю сообщение об ошибке.

IndexOutOfBoundsException: Index: 30, Size: 29

Я знаю, что эта ошибка возникает из-за того, что другой месяц имеет индекс 30. Я немного запутался, должен ли я удалить содержимое ArrayList или ComboBox? Любая помощь, как я могу это устранить?

 private ItemHandler handler = new ItemHandler();

ArrayList<String> daysList = new ArrayList<String>();

String[] daysObj = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15",
        "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"};
 DefaultComboBoxModel daysModel = new DefaultComboBoxModel(daysObj);

public AddEmployee() 
{
  setMonths();
  setDays();
  cbMonths.addItemListener(handler);
}

private void setDays()
{   
    for(int i = 0; i < daysObj.length; i  )
    {
        daysList.add(daysObj[i]);
    }

    cbDays.setModel((ComboBoxModel)daysModel);   
}

private class ItemHandler implements ItemListener
{
    int removeDays[] = {29,30};//array
    int remove[] = {30};
    @Override
    public void itemStateChanged(ItemEvent e) 
    {
        if(e.getSource() == cbMonths)//Check where combobox occured.
        {
            if(cbMonths.getSelectedItem().equals("February"))
            {
                for(int i = removeDays.length-1; i >= 0; i--)
                {
                    daysList.remove(removeDays[i]);//Remove given array from ArrayList using removeDays[]
                    System.out.println("NEW ELEMENT: " daysList);
                }

                for(String s : daysList)//Update ArrayList
                {
                    cbDays.addItem(s);
                    System.out.println("NEW LIST OF ARRAY: " s);
                }

            }
            else if(cbMonths.getSelectedItem().equals("April") || cbMonths.getSelectedItem().equals("June") || 
                    cbMonths.getSelectedItem().equals("September") || cbMonths.getSelectedItem().equals("November"))
            {
                for(int i = remove.length-1; i >= 0; i--)
                {
                    daysList.remove(remove[i]);
                    System.out.println(daysList);
                }

                for(String a : daysList)//Update ArrayList
                {
                    cbDays.addItem(a);
                    System.out.println("NEW LIST OF ARRAY: " a);
                }

            }
      }
}
  

Я попробовал метод removeAllItems() , но, похоже, он не работает. cbDays.removeAllItems();

Ответ №1:

Вместо выполнения сложных вычислений каждый раз, когда пользователь выбирает другой месяц (который трудно читать и подвержен ошибкам), лучше инициализировать некоторые статические модели, которые охватывают все случаи. Затем вашему обработчику с сохранением состояния нужно только выбрать правильную модель. Следующий пример основан на новом модном java 8 datetime API:

 private static String[] initDays( int number )
{
    String[] result = new String[ number];

    for ( int i = 0; i < result.length; i   )
    {
        result[i] = ""   ( i 1);
    }

    return resu<
}

private static final String[] days28 = initDays( 28);
private static final String[] days29 = initDays( 29);
private static final String[] days30 = initDays( 30);
private static final String[] days31 = initDays( 31);

private static final ComboBoxModel<String> model28 = new DefaultComboBoxModel<>(days28);
private static final ComboBoxModel<String> model29 = new DefaultComboBoxModel<>(days29);
private static final ComboBoxModel<String> model30 = new DefaultComboBoxModel<>(days30);
private static final ComboBoxModel<String> model31 = new DefaultComboBoxModel<>(days31);

private static final Set<Month> month30 = EnumSet.of(
         Month.FEBRUARY,
         Month.APRIL,
         Month.JUNE,
         Month.SEPTEMBER,
         Month.NOVEMBER
     );
private static final Set<Month> month31 = EnumSet.of(
         Month.JANUARY,
         Month.MARCH,
         Month.MAY,
         Month.JULY,
         Month.AUGUST,
         Month.OCTOBER,
         Month.DECEMBER
     );
private JComboBox<String> cbMonths = new JComboBox<>();
private JComboBox<String> cbDays = new JComboBox<>();

public void itemStateChanged(ItemEvent e)
{
    if(e.getSource() == cbMonths)//Check where combobox occured.
    {
        if(cbMonths.getSelectedItem().equals("February"))
        {
            Month selectedMonth = Month.valueOf( cbMonths.getItemAt( cbMonths.getSelectedIndex() ) );

            if ( month31.contains( selectedMonth ) )
            {
                cbDays.setModel( model31 );
            }
            else if ( month30.contains( selectedMonth ) )
            {
                cbDays.setModel( model30 );
            }
            else
            {
                if ( Year.isLeap( Instant.now().getLong( ChronoField.YEAR ) ) )
                {
                    cbDays.setModel( model29 );
                }
                else
                {
                    cbDays.setModel( model28 );
                }
            }
        }
    }
}
  

Дальнейшим улучшением может быть использование cbMonth, который напрямую вводится с помощью перечисления месяцев, и предоставление средства визуализации ячеек, которое отображает месяц в языковом стандарте пользователя.

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

1. Привет! @Heri Я все еще анализирую ваш код и уже пробовал его. В этом проекте я использовал GUI Builder. Должен ли я все еще вызывать private JComboBox<String> cbMonths = new JComboBox<>(); ? Потому что cbMonths и cbDays уже определены: D. Также я немного путаю вызов initDays() в конструкторе. Он должен быть объединен с объектом класса, верно? myObject.initDays(); ?

2. Строки, в которых я инициализирую cbMonths и cbDays, использовались в моем eclipse только для компиляции (и я скопировал его целиком сюда). Если у вас уже есть инициализированные экземпляры, просто используйте их.

3. initDays не вызывается в конструкторе. Оно статично и вызывается только один раз: когда класс загружается загрузчиком классов JVM.

4. Поскольку это уже определено в моем графическом интерфейсе, я прокомментировал, private JComboBox<String> cbMonths = new JComboBox<>(); потому что это выдает ошибку, и попытался запустить ваш код, но это не меняет дни, когда я выбрал «Февраль».

5. Извините, в моем наборе month30 есть ошибка: удалите там элемент FEBRUARY.

Ответ №2:

После инициализации через setDays() daysList содержит 31 элемент. Когда February выбрано, два элемента с индексом 29 и 30 удаляются:

     daysList.remove(removeDays[i])
  

daysList теперь у 29 него есть элементы. Когда выбран другой месяц, один элемент с индексом 30 удаляется

     daysList.remove(remove[i]);
  

Но dayslist имеет только 29 элементов, поэтому IndexOutOfBoundsException выбрасывается.

Поэтому одним из решений может быть повторная инициализация daysList при каждом itemStateChanged запуске.