Как реорганизовать этот цикл?
У меня есть приложение, в котором я использую примитивные массивы и списки для класса под названием Item. Они используются взаимозаменяемо по наследственным причинам (я также хочу, чтобы это был только один тип, но так оно и есть).
Теперь я должен добавить новый метод, подобный этому, который работает через цикл for: каждый
public void something(Item... items) {
for (Item i : items) {
doStuff();
}
}
public void something(List<Item> items) {
for (Item i : items) {
doStuff();
}
}
Иными словами, точно такой же метод дважды для примитивных массивов и списков. Есть ли способ хорошо реорганизовать это в один метод?
Ответы
Ответ 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()]))