Как удалить диапазон строковых объектов из arraylist на основе первых инициалов, введенных пользователем?

#java #string #object #arraylist

#java #строка #объект #arraylist

Вопрос:

Итак, у меня есть проект, в котором мне нужно создать arraylist из всех пятидесяти состояний. Затем я должен разрешить пользователю вводить две разные буквы алфавита. Затем я должен удалить все состояния, начинающиеся с этих букв, и все состояния между ними. Например, если кто-то ввел «D-I», все состояния, которые начинаются с D-I, должны быть удалены.

Я уже разработал эту программу, однако, когда я запускаю ее, она не только удаляет все состояния в диапазоне вводимых мною букв, но также удаляет все состояния перед первой буквой. Например. если я введу «D-I», все состояния, начинающиеся с A-I, будут удалены, хотя должны быть все состояния, начинающиеся с D-I.

Кто-нибудь может мне помочь с этим?

 import java.util.ArrayList;
import java.util.Scanner;

public class States {
    public static void main(String[] args) {
        String removeStates;
        String firstInitial;
        String secondInitial;
        String stateInitial;
        int first = 0;
        int last = 0;
        
        Scanner input = new Scanner(System.in);
        ArrayList<String> states = new ArrayList<>();
        
        states.add("Alabama");
        states.add("Alaska");
        states.add("Arizona");
        states.add("Arkansas");
        states.add("California");
        states.add("Colorado");
        states.add("Connecticut");
        states.add("Delaware");
        states.add("District of Columbia");
        states.add("Florida");
        states.add("Georgia");
        states.add("Hawaii");
        states.add("Idaho");
        states.add("Illinois");
        states.add("Indiana");
        states.add("Iowa");
        states.add("Kansas");
        states.add("Kentucky");
        states.add("Louisiana");
        states.add("Maine");
        states.add("Maryland");
        states.add("Massachusetts");
        states.add("Michigan");
        states.add("Minnesota");
        states.add("Mississippi");
        states.add("Missouri");
        states.add("Montana");
        states.add("Nebraska");
        states.add("Nevada");
        states.add("New Hampshire");
        states.add("New Jersey");
        states.add("New Mexico");
        states.add("New York");
        states.add("North Carolina");
        states.add("North Dakota");
        states.add("Ohio");
        states.add("Oklahoma");
        states.add("Oregon");
        states.add("Pennsylvania");
        states.add("Rhode Island");
        states.add("South Carolina");
        states.add("South Dakota");
        states.add("Tennessee");
        states.add("Texas");
        states.add("Utah");
        states.add("Vermont");
        states.add("Virginia");
        states.add("Washington");
        states.add("West Virginia");
        states.add("Wisconsin");
        states.add("Wyoming");
        
        System.out.println(states.toString());
        
        System.out.print("Enter the range of the states you would like to remove based on the first initials (e.g. A-D [not case sensitive]): ");
        
        removeStates = input.nextLine();
        
        firstInitial = removeStates.substring(0, 1);
        
        secondInitial = removeStates.substring(2);
        
        for (int i = 0; i < states.size(); i  ) {
            stateInitial = states.get(i).substring(0, 1);
            
            if (stateInitial.equalsIgnoreCase(firstInitial) || stateInitial.equalsIgnoreCase(secondInitial)) {
            
                for (int j = i 1; j>0; j--){
                    states.remove(i);
                    i--;
                }
            }
        }
        System.out.println(states.toString());

    }
}  

Вот как выглядит мой вывод прямо сейчас. Проблема находится в нижней / третьей строке. Как вы можете видеть, когда я ввел «G-K» (кстати, регистр не чувствителен), были удалены все состояния, начинающиеся с A-K, вместо тех, которые начинаются с G-K

 [Alabama, Alaska, Arizona, Arkansas, California, Colorado, Connecticut, Delaware, District of Columbia, Florida, Georgia, Hawaii, Idaho, Illinois, Indiana, Iowa, Kansas, Kentucky, Louisiana, Maine, Maryland, Massachusetts, Michigan, Minnesota, Mississippi, Missouri, Montana, Nebraska, Nevada, New Hampshire, New Jersey, New Mexico, New York, North Carolina, North Dakota, Ohio, Oklahoma, Oregon, Pennsylvania, Rhode Island, South Carolina, South Dakota, Tennessee, Texas, Utah, Vermont, Virginia, Washington, West Virginia, Wisconsin, Wyoming]
Enter the range of the states you would like to remove based on the first initials (e.g. A-D [not case sensitive]): g-k
[Louisiana, Maine, Maryland, Massachusetts, Michigan, Minnesota, Mississippi, Missouri, Montana, Nebraska, Nevada, New Hampshire, New Jersey, New Mexico, New York, North Carolina, North Dakota, Ohio, Oklahoma, Oregon, Pennsylvania, Rhode Island, South Carolina, South Dakota, Tennessee, Texas, Utah, Vermont, Virginia, Washington, West Virginia, Wisconsin, Wyoming]  

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

1. Строка#начинается с?

Ответ №1:

Это можно сделать в одном операторе с помощью removeIf() :

 states.removeIf(s -> s.charAt(0) >= 'D' amp;amp; s.charAt(0) <= 'I');
  

Вы можете заменить 'D' на removeStates.charAt(0) и 'I' на removeStates.charAt(2) .

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

1. Попробовал. Из списка ничего не было удалено.

2. @AmericanIdiot Вы вводите строчные буквы. Либо введите ввод в верхнем регистре, либо используйте removeStates like removeStates.toUpperCase().charAt(0) .

Ответ №2:

Вот альтернативный метод удаления нежелательных состояний, использующий streams:

 char firstInitial = removeStates.charAt(0);
char secondInitial = removeStates.charAt(2);

Collection<String> statesToRemove = states.stream()
       .filter(state -> state.charAt(0) >= firstInitial)
       .filter(state -> state.charAt(0) <= secondInitial)
       .collect(Collectors.toList());

states.removeAll(statesToRemove);
  

Вы также могли бы просто настроить фильтр так, чтобы он фильтровал только оставшиеся состояния, чтобы сделать его еще проще.

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

1. Но имейте в виду производительность (сложность кода): — поток неявно перебирает все элементы — метод removeAll снова перебирает все элементы списка. Итак, в этом случае вы получаете сложность кода 2N. Реализации removeIf (как подход на основе лямбда, упомянутый @Kartik, так и ответ, который я предоставил без лямбд, но с реализацией предиката) позволяют удалять ненужные элементы всего одним циклом по всем элементам, поэтому со сложностью N.

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

Ответ №3:

Вместо циклов вы могли бы просто использовать факты, которые

  • список отсортирован
  • компаратор строки сравнивает лексикографически, поэтому порядок равен A, Aa, Aaa, Aab, Abb, Baa,…
  • интерфейс списка реализует метод «removeIf», который позволяет вам проверять строку на соответствие правилу, которое вы реализуете

Итак, вместо ваших циклов for вы могли бы просто закодировать:

     firstInitial = removeStates.substring(0, 1);

    secondInitial = removeStates.substring(2);

    states.removeIf(new Predicate<String>() {

        @Override
        public boolean test(String t) {
            boolean isBeforeFirstInitial = (firstInitial.compareTo(t) >=0);
            boolean isAfterLastInitial = (secondInitial.compareTo(t) <=0);
            boolean startsWithLastInitial = t.startsWith(secondInitial);

            return !(isBeforeFirstInitial||isAfterLastInitial)||startsWithLastInitial;
        }
    });
  

не забудьте преобразовать инициалы в верхний регистр (в вашем тексте указано, что ввод НЕ чувствителен к регистру)

Ответ №4:

Не пытаюсь здесь показаться подлым, но это сработало, то, что вы написали:

 for (int j = i 1; j>0; j--){
                states.remove(i);
                i--;
            }
  

Допустим, у вас была первая буква ‘C’, первый индекс, который вы из внешнего цикла ввели бы во внутренний цикл, был бы i == 4 (Калифорния). Затем вы вызываете внутренний цикл с начальным значением для j = i 1 = 5 и постепенно перемещаете индексы массива вниз. Таким образом, вы удаляете 5 (что, кстати, равно единице до максимума.), затем 4, затем 3, затем 2 и так далее. Итак, как только вы нашли первый объект, вы удаляете все записи, ведущие к нему.

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

1. И кстати: лучшим способом извлечения одного символа из строки вместо подстроки был бы s.charAt(индекс) 🙂