Объясните пожалуйста, как добавлен элемент в TreeMap в следующем коде
Объясните пожалуйста вот этот код:
TreeSet<Station> connectedStations = connections.get(station);
connectedStations.addAll(stations.stream()
.filter(s -> !s.equals(station)).collect(Collectors.toList()))
Как этот код добавляет в TreeMap<Station, TreeSet<Station>> connections элемент(ы)?
На вход в метод addConnection() передается ArrayList<Station> stations, содержащий 2 объекта Fourth и Violet.
Первый объект из ArrayList передается в TreeMap с ключем-объект , а значение создается пустой TreeSet().
После выхода из итерации цикла for значение TreeSet<Station> connectedStations кажется, что никуда не передаются. Однако после исполнения этого кода объект добавляется в связку ключ-значение в connections (key: Fourth -> value: Violet).
Пытался отследить изменения при выполнении кода через дебаггер в IJ Idea, но так и не понял.
import core.Line;
import core.Station;
import java.util.*;
import java.util.stream.Collectors;
public class StationIndex
{
HashMap<Integer, Line> number2line;
TreeSet<Station> stations;
TreeMap<Station, TreeSet<Station>> connections;
public StationIndex()
{
number2line = new HashMap<>();
stations = new TreeSet<>();
connections = new TreeMap<>();
}
public void addStation(Station station)
{
stations.add(station);
}
public void addLine(Line line)
{
number2line.put(line.getNumber(), line);
}
public void addConnection(List<Station> stations)
{
for(Station station : stations)
{
if(!connections.containsKey(station)) {
connections.put(station, new TreeSet<>());
}
TreeSet<Station> connectedStations = connections.get(station);
connectedStations.addAll(stations.stream()
.filter(s -> !s.equals(station)).collect(Collectors.toList()));
}
}
public Line getLine(int number)
{
return number2line.get(number);
}
public Station getStation(String name)
{
for(Station station : stations)
{
if(station.getName().equalsIgnoreCase(name)) {
return station;
}
}
return null;
}
public Station getStation(String name, int lineNumber)
{
Station query = new Station(name, getLine(lineNumber));
Station station = stations.ceiling(query);
return station.equals(query) ? station : null;
}
public Set<Station> getConnectedStations(Station station)
{
if(connections.containsKey(station)) {
return connections.get(station);
}
return new TreeSet<>();
}
}
Ответы (1 шт):
Вроде бы в дебаггере всё понятно отображается, и в целом комментарий-уточнение под вопросом является верным. :)
Дебаггер находится внутри указанного метода addConnection.
Состояние "До" соответствует моменту на второй итерации, когда для station = "Violet" (Station@1303) был создан пустой TreeSet (TreeSet@1367, size=0), который был уже добавлен в мапу с ключом, равным текущему значению station.
Поэтому connectedStations, прочитанный по ключу station="Violet", ссылается на ТОТ ЖЕ пустой экземпляр TreeSet (TreeSet@1367, size=0).
При выполнении строки:
connectedStations.addAll(stations.stream()
.filter(s -> !s.equals(station)).collect(Collectors.toList()));
в "бывшее" пустое множество connectedStations добавляются все отфильтрованные элементы списка stations, НЕ равные текущему station, то есть добавляется список, включающий ПЕРВЫЙ элемент списка Fourth. Соответственно, аналогичное изменение видно и в мапе connections.
В состоянии "После" цикла for локальная ссылка из цикла connectedStations УЖЕ не существует, но мапа-поле класса connections содержит Map.Entry с ключом "Violet" (Station@1303) и значением-множеством (TreeSet@1367, size=1), содержащим ссылку на "Fourth" (Station@1216).
Код в методе addConnection можно было бы переписать:
- с использованием классического цикла и "нового" метода
Map::computeIfAbsent, который возвращает ссылку на значение для данного ключа (если ключ не добавлен в мапу, будет создан новый экземплярMap.Entryс пустым контейнеромTreeSet)
public void addConnection(List<Station> stations) {
// создаём множество-кеш на основе входного списка
Set<Station> exceptCurrent = new HashSet<>(stations);
for (Station current : stations) {
// убираем текущий элемент из кеша вместо его перегенерации
exceptCurrent.remove(current);
// копируем содержимое кеша в значение мапы
connections.computeIfAbsent(current, key -> new TreeSet<>())
.addAll(exceptCurrent);
// возвращаем текущий элемент в кеш
exceptCurrent.add(current);
}
}
Вероятно, вместо входного списка List<Station> также следовало бы использовать множество Set в котором гарантированно не было бы дубликатов.
