Удаление каждого N-го элемента из списка подстановочных знаков - Java - PullRequest
1 голос
/ 01 мая 2020

Я пытаюсь создать метод, который удаляет каждый N-й элемент из списка неизвестного типа (подстановочный знак), однако, при каждом попытке сделать это, он не удаляет указанные элементы, но я не могу понять, почему. Я боролся с этим уже два дня, поэтому я публикую здесь последнее средство. Я заранее благодарю вас за любую помощь.

Код, который у меня есть в настоящее время, выглядит следующим образом:

public static void removeEveryNthElement(List<?> list, int n) {

    //Set list equal to an ArrayList because List is immutable
    list = new ArrayList<>(list);

    //If n is negative or zero throw an exception
    if(n <= 0) {
        throw new IllegalArgumentException("Integer n needs to be a positive number.");
    }

    //If the list is null, throw an exception
    if(list == null) {
        throw new NullPointerException("The list must not be null.");
    }

    //Remove every nth element in the list
    for(int i = 0; i < list.size(); i++) {
        if(i % n == 0) {
            list.remove(i);
        }
    }

Другой способ, которым я попытался, - заменить for для l oop следующим :

list.removeIf(i -> i % 3 == 0);

Однако, когда я делаю это таким образом, я получаю ошибку, что оператор% не определен для типа аргумента. Я также пытался использовать для l oop для отдельного добавления каждого элемента из списка в другой изменяемый список, но что бы я ни делал, мне не повезло. Если бы вы могли помочь мне с этим, я был бы очень признателен!

Ответы [ 3 ]

1 голос
/ 01 мая 2020

Следует иметь в виду, что создание новой коллекции на основе другой коллекции удаляет ссылки на оригинальную коллекцию - значения из коллекции копируются в новую - любая модификация не повлияет на что-либо вне метода объем. Вам нужно будет передать коллекцию, которая поддерживает удаление объекта из себя, или вернуть новую коллекцию из метода. Помните, что тип не определяет поведение объекта - он зависит от реализации, совместимой с классом, к которому вы приводите. Вот пример того, что я сказал о реализации бэкэнда (обе переменные имеют тип List, но реализация отличается).

Вот код, когда вы хотите сделать это «на месте» :

public static void main(String[] args) {
    List<Integer> list2 = new ArrayList<>();
    list2.add(1);
    list2.add(2);
    list2.add(3);
    list2.add(4);

    removeEveryNthElement(list2, 3); // deleted number 3 because it is 3rd element
}

public static void removeEveryNthElement(List<?> list, int n) {
    for (int i = 2; i < list.size(); i += 3) {
        list.remove(i);
    }
}

Но я бы порекомендовал не делать никаких операций, которые не прозрачны для программиста. Лучше читать и понимать большие программы, когда вы знаете, что передаете значение методу и «он что-то делает», а затем возвращаете значение, потому что оно изменилось. Для этого примера я использую дженерики и потоки:

public static void main(String[] args) {
    List<Integer> list1 = Arrays.asList(1, 2, 3, 4);
    list1 = removeEveryNthElement2(list1, 3); //deleted number 3
    System.out.println();
}

public static <T> List<T> removeEveryNthElement2(List<T> list, int n) {
    final Predicate<T> p = new Predicate<T>() {
        int i = 0;

        @Override
        public boolean test(final T t) {
            return ++i % n != 0;
        }
    };

    return list.stream().filter(p).collect(Collectors.toList());
}
1 голос
/ 01 мая 2020

Самая серьезная проблема с вашим кодом состоит в том, что удаление элемента с индексом i изменяет индекс всех следующих элементов, и поэтому ваше условие удаления элементов (i % n) является неправильным после удаления первого элемента.

Одним из способов решения проблемы является итерация в обратном порядке:

for (int i = list.size()-1; i >= 0; i--) {
    if (i % n == 0) {
        list.remove(i);
    }
}

Другим способом является увеличение i не на единицу, а на n и настройка его для удаленного элемента:

for (int i = 0; i < list.size(); i += n) {
    list.remove(i);
    i--;
}

и, поскольку i--;, за которым следует i += n;, совпадает с i += n-1;:

for (int i = 0; i < list.size(); i += n-1) {
    list.remove(i);
}

Дополнительное примечание: проверка if (list == null) бесполезно после оператора list = new ArrayList<>(list);, поскольку new ArrayList<>(list); уже генерирует исключение NullPointerException, если list равно нулю

0 голосов
/ 01 мая 2020

Брэндон, позвольте мне сначала предположить, что ваш метод - это side-effecting список, встроенный в вызывающий метод. Это разрешено, но может привести к трудным для понимания ошибкам в более сложном коде. Вместо этого попробуйте создать новый список в вашем методе и присвоить возвращаемое значение:

public class Remove {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h");
        list = removeElements(list, 3);
        System.out.println(list);
    }

    public static <T> List<T> removeElements(List<T> list, int n) {
        List<T> newList = new ArrayList<T>();
        for (int i = 0; i < list.size(); i++) {
            if (i % n != 0) {
                newList.add(list.get(i));
            }
        }
        return newList;
    }
}

В результате это упрощает метод, потому что мы больше не перебираем изменяемый список.

Посмотрите на Побочный эффект - что это? чтобы узнать больше о побочных эффектах.

...