Объединить 2 списка объектов с различными значениями полей
У меня есть 2 списка, в которых лежат объекты одного класса, их нужно объединить в один список. Класс:
public class Stat {
private String name;
private String date;
private Long work1;
private Long work2;
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) {
return false;
}
Stat that = (Stat) o;
return getName().equals(that.getName()) && getDate().equals(that.getDate());
}
//getters, setters, hashCode
}
В обоих списках могут совпадают имена и дата, но в первом лежит зн-е work1, во втором work2, количество элементов может отличаться. Например:
List1<Stat> list1 = {1. name = name1, date = date1, work1 = 4, work2 = null
2. name = name2, date = date2, work1 = 5, work2 = null}
List<Stat> list2 = {1. name = name1, date = date1, work1 = null, work2 = 3
2. name = name2, date = date2, work1 = null, work2 = 4
3. name = name3, date = date3, work1 = null, work2 = 5}
List<Stat> result = {1. name = name1, date = date1, work1 = 4, work2 = 3
2. name = name2, date = date2, work1 = 5, work2 = 4
3. name = name3, date = date3, work1 = null, work2 = 5}
Я сделал способом, который показан ниже, но хотел бы узнать у более опытных коллег, можно ли это сделать более изящно, потому что я не нашёл способа
private Set<Stat> method() {
Set<Stat> result = new HashSet<>();
addResult(list1, result);
addResult(list2, result);
return result;
}
private void addResult(List<Stat> stats, Set<Stat> result) {
for (Stat stat : stats) {
for (Stat res : result) {
if (stat.equals(res)) {
Long work1 = stat.getWork1();
if (work1 != null) {
res.setWork1(work1);
}
Long work2 = stat.getWork2();
if (work2 != null) {
res.setWork2(work2);
}
}
}
result.add(stat);
}
}
Ответы (1 шт):
Учитывая необходимость мерджить значения work1 и work2, вряд ли будет более короткое и изящное решение
Однако в текущем решении меня напрягает несколько моментов:
- Поиск по
Setидёт за линейное время (O(n)), что противоестественно для него result.add(stat)происходит для всех элементов (даже для тех, что уже есть вresult). Это выглядит странно с точки зрения логики алгоритма ("если элемент уже есть в Set - обновить имеющийся, иначе добавить этот") и не ломается лишь за счёт того, что элемент не добавляется повторно (в том числе не заменяет уже имеющийся элемент) в HashSet- Модифицируются элементы исходных массивов. В каких-то ситуациях это допустимо, но в общем случае в результирующей коллекции должны быть новые, смердженные элементы
Вариант с исправлением только пунктов 1 и 2:
private Collection<Stat> method()
{
Map<Integer, Stat> result = new HashMap<>();
addResult(list1, result);
addResult(list2, result);
return result.values();
}
private void addResult(List<Stat> stats, Map<Integer, Stat> result)
{
for (Stat stat : stats)
{
int key = stat.hashCode();
if (result.containsKey(key))
{
Stat res = result.get(key);
Long work1 = stat.getWork1();
if (work1 != null)
{
res.setWork1(work1);
}
Long work2 = stat.getWork2();
if (work2 != null)
{
res.setWork2(work2);
}
}
else
{
result.put(key, stat);
}
}
}
Вместо HashSet используется HashMap, чтобы не нужно было пробегать по всему result в поисках подходящего элемента. В качестве ключа HashMap используется hashcode от Stat - можно использовать и сам объект Stat, но такой код будет сложнее в понимании
Вариант с исправлением пунктов 1-3 (метод method выглядит так же как и в предыдущем примере):
private void addResult(List<Stat> stats, Map<Integer, Stat> result)
{
for (Stat stat : stats)
{
int key = stat.hashCode();
if (result.containsKey(key))
{
Stat res = result.get(key);
Long mergedWork1 = chooseWork(stat.getWork1(), res.getWork1());
Long mergedWork2 = chooseWork(stat.getWork2(), res.getWork2());
Stat mergedStat = new Stat(stat.getName(), stat.getDate(), mergedWork1, mergedWork2);
result.put(key, mergedStat);
}
else
{
result.put(key, stat);
}
}
}
private static Long chooseWork(Long firstWork, Long secondWork)
{
return firstWork == null ? secondWork : firstWork;
}
В данном случае в результирующую коллекцию попадают как оригинальные объекты Stat (если не требовался мердж для них), так и новые, смердженные