Как реорганизовать этот цикл? - программирование
Подтвердить что ты не робот

Как реорганизовать этот цикл?

У меня есть приложение, в котором я использую примитивные массивы и списки для класса под названием Item. Они используются взаимозаменяемо по наследственным причинам (я также хочу, чтобы это был только один тип, но так оно и есть).

Теперь я должен добавить новый метод, подобный этому, который работает через цикл for: каждый

public void something(Item... items) {
    for (Item i : items) {
        doStuff();
    }
}

public void something(List<Item> items) {
    for (Item i : items) {
        doStuff();
    }
}

Иными словами, точно такой же метод дважды для примитивных массивов и списков. Есть ли способ хорошо реорганизовать это в один метод?

4b9b3361

Ответ 1

Вы не можете не должны (*) делать это одним методом. Item[] и List<Item> являются несвязанными типами.

Вы должны сделать одну из перегрузок другой: либо something(Item... items) вызывает something(List<Item>), либо something(List<Item>) вызывает something(Item... items).

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

public void something(Item... items) {
  something(Arrays.asList(item));
}

Это дешево, потому что он не копирует массив, а скорее обертывает его: создание List равно O(1).

Если вы должны были вызвать перегрузку массива из перегрузки списка:

public void something(List<Item> items) {
  something(items.toArray(new Item[0]));
}

Это было бы дороже, так как вызов toArray должен создавать и заполнять массив: это операция O(n), где n - это размер списка. Тем не менее, он имеет небольшое преимущество в том, что something не сможет заменить содержимое List, так как любые обновления для массива просто отбрасываются после выполнения.


(*) Вы можете, но это было бы очень грубо, а не безопасно, так как вам нужно было бы принять параметр Object, так как нет другого общего супер-типа List<Item> и Item[]; и вам все равно придется повторять петли для двух типов; и вам придется обрабатывать возможность передачи полностью несвязанного типа (во время выполнения):

public void something(Object obj) {
  if (obj instanceof List) {
    for (Object element : (List<?>) obj) {
      Item item = (Item) element;  // Potential ClassCastException.
      doStuff();
    }
  } else if (obj instanceof Item[]) {
    for (Item item : (Item[]) obj) {
      doStuff();
    }
  } else {
    throw new IllegalArgumentException();
  }
}

Какой беспорядок. Поблагодарите создателя за перегрузки.

Ответ 2

Если вы используете Java 8, вы также можете просто вызвать forEach или map на Stream, и все будет готово, например

yourStream.forEach(doStuff());

где doStuff() - consumer, связанный с String или использующий yourStream.forEach(s -> doStuff()), если вы не хотите обрабатывать string и просто do stuff.

Вы можете получить поток следующим образом:

Stream.of(yourArray) // or Arrays.stream(yourArray)
      .forEach(doStuff());

и для вашего списка:

list.stream()
    .forEach(doStuff());

Основное преимущество использования потоков - это, вероятно, читаемость. Он может потерять производительность и может также проиграть, если вы не хотите вызывать Stream.of/Arrays.stream или Collection.stream() только для получения потока.

Если вы действительно хотите сохранить метод something(...) (возможность иметь дело с обоими: varargs и список), вам по-прежнему требуется перегруженный метод или использовать предложение Энди Тернера с помощью Object -параметра-метода.

Ответ 3

Вы можете реализовать единственный метод, в этом случае второй, потому что он имеет список как параметр. Вместо первого метода вы можете преобразовать массив в список с помощью Arrays.asList(items), а затем вы можете вызвать первый метод. Итак, в конце концов, у вас будет только один метод (который имеет список как параметр).

Кроме того, если список элементов содержит несколько элементов, вы можете использовать лямбда-выражения из Java 8:

items.foreach(item -> doStuff(item));

Итак, у вас не будет метода, который содержит только один цикл, и код будет легче читать.

Ответ 4

Вы должны достичь этого, передав List после преобразования его в массив.

Сохраните это как свой единственный метод,

public void something(Item... items) {
   for (Item i : items) {
       doStuff();
   }
}

и если вы хотите передать List<Item>, тогда пройдите так,

something(listItem.toArray(new Item[listItem.size()]))