Инициализация ArrayList в одну строку - PullRequest
2501 голосов
/ 17 июня 2009

Я хочу создать список опций для целей тестирования. Сначала я сделал это:

ArrayList<String> places = new ArrayList<String>();
places.add("Buenos Aires");
places.add("Córdoba");
places.add("La Plata");

Затем я изменил код следующим образом:

ArrayList<String> places = new ArrayList<String>(
    Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));

Есть ли лучший способ сделать это?

Ответы [ 30 ]

1969 голосов
/ 17 июня 2009

Было бы проще, если бы вы просто объявили его как List - это должен быть ArrayList?

List<String> places = Arrays.asList("Buenos Aires", "Córdoba", "La Plata");

Или, если у вас есть только один элемент:

List<String> places = Collections.singletonList("Buenos Aires");

Это будет означать, что places является неизменным (попытка изменить его вызовет исключение UnsupportedOperationException).

Чтобы создать изменяемый список, который является конкретным ArrayList, вы можете создать ArrayList из неизменяемого списка:

ArrayList<String> places = new ArrayList<>(Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));
1859 голосов
/ 17 июня 2009

На самом деле, вероятно, "лучший" способ инициализации ArrayList - это метод, который вы написали, поскольку он не должен создавать новый List каким-либо образом:

ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");

Загвоздка в том, что для ссылки на этот экземпляр list требуется довольно много печатания.

Существуют альтернативы, такие как создание анонимного внутреннего класса с инициализатором экземпляра (также известный как «инициализация двойной скобкой»):

ArrayList<String> list = new ArrayList<String>() {{
    add("A");
    add("B");
    add("C");
}};

Тем не менее, я не слишком люблю этот метод, потому что в конечном итоге вы получаете подкласс ArrayList, который имеет инициализатор экземпляра, и этот класс создается только для создания одного объекта - который просто выглядит как немного излишне для меня.

Было бы неплохо, если бы было принято предложение Collection Literals для Project Coin (его планируется ввести в Java 7, но вряд ли оно будет частью Java 8 тоже.):

List<String> list = ["A", "B", "C"];

К сожалению, здесь вам это не поможет, поскольку он инициализирует неизменяемый List, а не ArrayList, и, более того, он еще не доступен, если он когда-либо будет.

751 голосов
/ 09 сентября 2010

Простой ответ

В Java 10, 11, 12 или более поздней версии:

var strings = List.of("foo", "bar", "baz");

В Java 9 или более поздней версии:

List<String> strings = List.of("foo", "bar", "baz");

Это даст вам неизменный List, поэтому его нельзя изменить.
Это то, что вы хотите в большинстве случаев, когда вы его предварительно заполняете.


Java 8 или более ранняя версия:

List<String> strings = Arrays.asList("foo", "bar", "baz");

Это даст вам List, обеспеченный массивом, поэтому он не может изменить длину.
Но вы можете позвонить List.set, так что он все еще изменчив.


Вы можете сделать Arrays.asList еще короче при статическом импорте:

List<String> strings = asList("foo", "bar", "baz");

Статический импорт:

import static java.util.Arrays.asList;  

Что любая современная IDE предложит и автоматически сделает для вас.
Например, в IntelliJ IDEA вы нажимаете Alt+Enter и выбираете Static import method....


Однако я не рекомендую сокращать метод Java 9 List.of, потому что просто of может сбить с толку.
List.of уже достаточно короткий и хорошо читается.


Использование Stream с

Почему это должен быть List?
В Java 8 или более поздней версии вы можете использовать Stream, который является более гибким:

Stream<String> strings = Stream.of("foo", "bar", "baz");

Вы можете объединить Stream s:

Stream<String> strings = Stream.concat(Stream.of("foo", "bar"),
                                       Stream.of("baz", "qux"));

Или вы можете перейти от Stream к List:

import static java.util.stream.Collectors.toList;

List<String> strings = Stream.of("foo", "bar", "baz").collect(toList());

Но желательно, просто используйте Stream, не собирая его в List.


Если вам действительно , в частности нужен java.util.ArrayList

(Вы, вероятно, нет.)
Цитировать JEP 269 (выделено мое):

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


Если вы хотите , оба предварительно заполнить ArrayList и добавить к нему впоследствии (почему?), Используйте

ArrayList<String> strings = new ArrayList<>(List.of("foo", "bar"));
strings.add("baz");

или в Java 8 или более ранней версии:

ArrayList<String> strings = new ArrayList<>(asList("foo", "bar"));
strings.add("baz");

или используя Stream:

import static java.util.stream.Collectors.toCollection;

ArrayList<String> strings = Stream.of("foo", "bar")
                             .collect(toCollection(ArrayList::new));
strings.add("baz");

Но опять же, лучше просто использовать Stream напрямую, а не собирать его в List.


Программа для интерфейсов, а не для реализаций

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

Что вы, скорее всего, не делаете.

Обычно вам следует просто объявить переменные в наиболее общем интерфейсе, который вы собираетесь использовать (например, Iterable, Collection или List), и инициализировать их конкретной реализацией (например, ArrayList, LinkedList или Arrays.asList()).

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

Например, если вы передаете ArrayList void method(...):

// Iterable if you just need iteration, for (String s : strings):
void method(Iterable<String> strings) { 
    for (String s : strings) { ... } 
}

// Collection if you also need .size(), .isEmpty(), or .stream():
void method(Collection<String> strings) {
    if (!strings.isEmpty()) { strings.stream()... }
}

// List if you also need .get(index):
void method(List<String> strings) {
    strings.get(...)
}

// Don't declare a specific list implementation
// unless you're sure you need it:
void method(ArrayList<String> strings) {
    ??? // You don't want to limit yourself to just ArrayList
}

Другим примером всегда будет объявление переменной InputStream, хотя обычно это FileInputStream или BufferedInputStream, потому что однажды вы или кто-то еще захотите использовать какой-то другой тип InputStream.

107 голосов
/ 30 августа 2011

Если вам нужен простой список размером 1:

List<String> strings = new ArrayList<String>(Collections.singletonList("A"));

Если вам нужен список из нескольких объектов:

List<String> strings = new ArrayList<String>();
Collections.addAll(strings,"A","B","C","D");
54 голосов
/ 29 июля 2013

С Гуава Вы можете написать:

ArrayList<String> places = Lists.newArrayList("Buenos Aires", "Córdoba", "La Plata");

В Гуаве есть и другие полезные статические конструкторы. Вы можете прочитать о них здесь .

32 голосов
/ 04 апреля 2014

Литералы коллекции не попали в Java 8, но можно использовать Stream API для инициализации списка в одну довольно длинную строку:

List<String> places = Stream.of("Buenos Aires", "Córdoba", "La Plata").collect(Collectors.toList());

Если вам необходимо убедиться, что ваш List является ArrayList:

ArrayList<String> places = Stream.of("Buenos Aires", "Córdoba", "La Plata").collect(Collectors.toCollection(ArrayList::new));
29 голосов
/ 12 мая 2010
import com.google.common.collect.ImmutableList;

....

List<String> places = ImmutableList.of("Buenos Aires", "Córdoba", "La Plata");
23 голосов
/ 02 февраля 2017

С Java 9, как предложено в JDK Enhancement Proposal - 269 , это может быть достигнуто с использованием литералов коллекции теперь как -

List<String> list = List.of("A", "B", "C");

Set<String> set = Set.of("A", "B", "C");

Также аналогичный подход будет применяться и к Map -

Map<String, String> map = Map.of("k1", "v1", "k2", "v2", "k3", "v3")

, что аналогично предложению Collection Literals , как заявлено @coobird. Также уточняется в документе JEP -


Альтернатива

Языковые изменения были рассмотрены несколько раз и отклонены:

Предложение по монетам проекта, 29 марта 2009 г.

Предложение по монетам проекта, 30 марта 2009 г.

Обсуждение JEP 186 по lambda-dev, январь-март 2014 года

язык предложения были отложены в пользу предпочтения на основе библиотеки как Суммировано в этом сообщении.

Related => Какой смысл перегружать методы фабрики удобства для коллекций в Java 9

22 голосов
/ 04 мая 2010

Вы можете создать фабричный метод:

public static ArrayList<String> createArrayList(String ... elements) {
  ArrayList<String> list = new ArrayList<String>();
  for (String element : elements) {
    list.add(element);
  }
  return list;
}

....

ArrayList<String> places = createArrayList(
  "São Paulo", "Rio de Janeiro", "Brasília");

Но это не намного лучше, чем ваш первый рефакторинг.

Для большей гибкости он может быть общим:

public static <T> ArrayList<T> createArrayList(T ... elements) {
  ArrayList<T> list = new ArrayList<T>();
  for (T element : elements) {
    list.add(element);
  }
  return list;
}
15 голосов
/ 06 октября 2017

В Java 9 мы можем легко инициализировать ArrayList в одну строку:

List<String> places = List.of("Buenos Aires", "Córdoba", "La Plata");

или

List<String> places = new ArrayList<>(List.of("Buenos Aires", "Córdoba", "La Plata"));

Этот новый подход Java 9 имеет много преимуществ по сравнению с предыдущими:

  1. Эффективность использования пространства
  2. Неизменность
  3. Thread Safe

Для получения более подробной информации смотрите этот пост -> В чем разница между List.of и Arrays.asList?

...