Как вывести ArrayList в Java через toString() и задать свой формат

Чтобы вывести ArrayList в Java, переопределите toString() у класса элемента или соберите строки отдельным способом. ArrayList сам вызывает toString() каждого элемента, поэтому System.out.println(list) печатает весь список в квадратных скобках.

На примере Employee вы зададите формат объекта, выведете записи по одной строке и получите собственный разделитель списка. В конце сравните ручной метод, record, генерацию IDE, Lombok и потоковый вывод.

Как подготовить ArrayList с записями сотрудников

Предположим, что список хранит записи сотрудников. Объявите переменную через интерфейс List<Employee>, а объект создайте через ромбовидный оператор.

List<Employee> empRecords = new ArrayList<>();
empRecords.add(new Employee(101, "SJ", 19, "Writer"));
empRecords.add(new Employee(102, "RS", 17, "Developer"));

ArrayList сохраняет порядок добавления записей и поддерживает доступ по индексу. Поэтому цикл проходит элементы в том же порядке, в котором список их получил.

Переопределение методов в Java на примере toString()

Переопределение метода в Java меняет реализацию унаследованного метода в классе-наследнике. Для toString() сохраните ту же сигнатуру и модификатор доступа public.

Аннотация @Override просит компилятор проверить переопределение. Если вы ошибетесь в имени или параметрах метода, компилятор укажет на проблему.

Класс Employee с собственным toString()

Сохраните класс в файле Employee.java. Метод возвращает одну строку с четырьмя полями.

public class Employee {

    private int employeeId;
    private String employeeName;
    private int employeeAge;
    private String employeeDesignation;

    public Employee(int employeeId, String employeeName, int employeeAge,
            String employeeDesignation) {
        this.employeeId = employeeId;
        this.employeeName = employeeName;
        this.employeeAge = employeeAge;
        this.employeeDesignation = employeeDesignation;
    }

    @Override
    public String toString() {
        return "Employee [employeeId=" + employeeId
                + ", employeeName=" + employeeName
                + ", employeeAge=" + employeeAge
                + ", employeeDesignation=" + employeeDesignation + "]";
    }
}

Аннотация @Override особенно полезна при переименовании метода. Она не дает незаметно создать новый метод вместо переопределения.

Как ArrayList вызывает toString() элементов

У ArrayList метод toString() унаследован от AbstractCollection. Реализация проходит по элементам и вызывает toString() каждого объекта.

Вывод списка одной строкой и поэлементно

Создайте рядом файл StoreAndRetrieveEmployeeRecords.java. Первый вызов println печатает весь список, а цикл выводит записи по отдельности.

import java.util.ArrayList;
import java.util.List;

public class StoreAndRetrieveEmployeeRecords {
    public static void main(String[] args) {
        List<Employee> empRecords = new ArrayList<>();
        empRecords.add(new Employee(101, "SJ", 19, "Writer"));
        empRecords.add(new Employee(102, "RS", 17, "Developer"));
        empRecords.add(new Employee(103, "ZR", 25, "Supporter"));

        System.out.println(empRecords);

        for (Employee emp : empRecords) {
            System.out.println(emp);
        }
    }
}

Вызов System.out.println(empRecords) сам вызывает toString() у каждого Employee. Поэтому список появляется одной строкой в формате [Employee [...], Employee [...]].

[Employee [employeeId=101, employeeName=SJ, employeeAge=19, employeeDesignation=Writer], Employee [employeeId=102, employeeName=RS, employeeAge=17, employeeDesignation=Developer], Employee [employeeId=103, employeeName=ZR, employeeAge=25, employeeDesignation=Supporter]]
Employee [employeeId=101, employeeName=SJ, employeeAge=19, employeeDesignation=Writer]
Employee [employeeId=102, employeeName=RS, employeeAge=17, employeeDesignation=Developer]
Employee [employeeId=103, employeeName=ZR, employeeAge=25, employeeDesignation=Supporter]

Первая строка содержит весь ArrayList. Следующие три строки появляются из цикла for-each.

Что выведет ArrayList без переопределения toString()

Если класс Employee не переопределяет toString(), вызывается реализация класса Object. Она возвращает имя класса, символ @ и хеш-код в шестнадцатеричном виде.

Для отдельного примера уберите метод toString() из класса:

public class Employee {
    private int employeeId;
    private String employeeName;

    public Employee(int employeeId, String employeeName) {
        this.employeeId = employeeId;
        this.employeeName = employeeName;
    }
}

Создайте список и добавьте один объект:

List<Employee> employees = new ArrayList<>();
employees.add(new Employee(101, "SJ"));
System.out.println(employees);

Результат будет похож на этот:

[Employee@1db9742]

Формула Object.toString() выглядит так: getClass().getName() + "@" + Integer.toHexString(hashCode()). Значение после @ меняется от запуска к запуску, поэтому не используйте его как постоянный идентификатор.

Поля сотрудника такая строка не показывает. Переопределите toString(), если вывод должен содержать значения полей.

Форматы вывода списка с разделителем

Выберите способ по месту использования результата. forEach сразу пишет строки в консоль, а потоковые операции сначала собирают одну строку.

Каждый элемент с новой строки через forEach

empRecords.forEach(System.out::println);

Ссылка на метод заменяет тело цикла с вызовом println:

Employee [employeeId=101, employeeName=SJ, employeeAge=19, employeeDesignation=Writer]
Employee [employeeId=102, employeeName=RS, employeeAge=17, employeeDesignation=Developer]
Employee [employeeId=103, employeeName=ZR, employeeAge=25, employeeDesignation=Supporter]

Строка с разделителем через String.join()

String.join() объединяет строки. Сначала преобразуйте сотрудников через toString().

String result = String.join(" | ",
        empRecords.stream().map(Employee::toString)
                .collect(java.util.stream.Collectors.toList()));
System.out.println(result);
Employee [employeeId=101, employeeName=SJ, employeeAge=19, employeeDesignation=Writer] | Employee [employeeId=102, employeeName=RS, employeeAge=17, employeeDesignation=Developer] | Employee [employeeId=103, employeeName=ZR, employeeAge=25, employeeDesignation=Supporter]

Перенос строки через Collectors.joining()

Коллектор соберет одну строку с переносами между записями. Такой результат удобно передать в другой метод или записать в файл.

import java.util.stream.Collectors;

String result = empRecords.stream()
        .map(Employee::toString)
        .collect(Collectors.joining("\n"));
System.out.println(result);
Employee [employeeId=101, employeeName=SJ, employeeAge=19, employeeDesignation=Writer]
Employee [employeeId=102, employeeName=RS, employeeAge=17, employeeDesignation=Developer]
Employee [employeeId=103, employeeName=ZR, employeeAge=25, employeeDesignation=Supporter]

Форматирование через геттеры без toString()

Геттеры подходят, когда формат нужен только в одном месте. Сеттеры меняют поля объекта и к выводу отношения не имеют.

Добавьте в класс Employee методы чтения четырех полей:

public int getEmployeeId() {
    return employeeId;
}

public String getEmployeeName() {
    return employeeName;
}

public int getEmployeeAge() {
    return employeeAge;
}

public String getEmployeeDesignation() {
    return employeeDesignation;
}

Вставьте цикл в main после заполнения списка:

for (Employee emp : empRecords) {
    System.out.printf("id=%d, name=%s, age=%d, role=%s%n",
            emp.getEmployeeId(),
            emp.getEmployeeName(),
            emp.getEmployeeAge(),
            emp.getEmployeeDesignation());
}
id=101, name=SJ, age=19, role=Writer
id=102, name=RS, age=17, role=Developer
id=103, name=ZR, age=25, role=Supporter

Здесь printf получает значения напрямую через геттеры. Удаление toString() из Employee не изменит результат этого цикла.

Как получить toString() без ручного кода

Ручная реализация дает полный контроль над строкой. Для простых объектов можно использовать record, генерацию IDE или библиотеку Lombok.

record

record автоматически генерирует toString() с именем записи и значениями ее компонентов.

public record Employee(int employeeId, String employeeName,
                       int employeeAge, String employeeDesignation) {
}

Вызов System.out.println(employee) выведет имя record и значения его компонентов. Такой вариант подходит для моделей данных с неизменяемыми полями.

Генерация в IntelliJ IDEA и Eclipse

IntelliJ IDEA и Eclipse умеют вставлять метод toString() в класс через инструменты генерации кода. Откройте команду генерации в среде, выберите toString() и отметьте поля.

Если команда не находится или среда предлагает другой набор пунктов, вставьте метод вручную из примера класса Employee в этой статье. После генерации проверьте список полей: пароли, токены и персональные данные в строку добавлять не стоит.

Lombok @ToString

Аннотация @ToString из Lombok генерирует метод во время сборки.

Если проект уже использует Lombok, добавьте @ToString над объявлением класса. При необходимости исключите поле аннотацией @ToString.Exclude.

Подводные камни при выводе объектов

Не включайте в toString() пароли, токены и персональные данные, если объект попадает в логи. Журнал может оказаться доступен другим сотрудникам или системам.

Взаимные ссылки объектов могут зациклить вызовы toString(). Такая рекурсия может завершиться ошибкой StackOverflowError.

Если объект A выводит объект B, а B снова выводит A, возвращайте идентификатор связанного объекта. Такой формат прекращает повторный обход полей.

Какой способ выбрать для ArrayList

СпособЧто пишет разработчикФормат выводаКогда подходит
Ручной toString()Метод и шаблон строкиЛюбой формат объектаФормат нужен во всем проекте
recordКомпоненты recordАвтоматическая строка recordНужна модель данных с неизменяемыми полями
Генерация IDEПоля выбираются в диалогеСгенерированный шаблонНужен обычный класс без библиотеки
LombokАннотация @ToStringАвтоматический шаблонПроект уже использует Lombok
Collectors.joining()Разделитель спискаОдна строка или переносыФормат нужен только при конкретном выводе

Как задать формат объекта и списка

Переопределите toString(), если Employee должен одинаково отображаться в логах и консоли. Тогда System.out.println(empRecords) напечатает весь список через значения элементов.

Используйте forEach для строк по одной записи. Берите String.join() или Collectors.joining(), когда нужен свой разделитель. Для простых моделей рассмотрите record, генерацию IDE или Lombok. Геттеры оставляют форматирование в месте вывода и не требуют переопределения метода.

Совет от нашей редакции: перед использованием формата в логах проверьте состав полей. Строка не должна раскрывать пароль, токен или персональные данные.

Комментарии

Оставьте свой комментарий
Пока никто не оставил комментариев