Расширенный поиск Java 8
У меня есть объект Activity
, который может быть parent
или child
.
class Activity {
long modificationDate;
Activity parentActivity;
Set<Activity> subActivities;
boolean active;
}
Мне нужно отсортировать действия, ранее загруженные из базы данных, их modification date
, но все еще grouped by
их родительскую активность.
Правила: Ребенок E1
может обновляться, а родительский P1
- нет. Таким образом, changeDate E1
может быть больше, чем P1
.
Если ребенок обновлен, its parent has to rise
верхний список со всеми его дочерними элементами
Пример: активность | дата модификации
E1 | 01 june
P2 | 01 june
P1 | 01 june
P3 | 02 june
E1 | 10 july
После сортировки он должен дать
P1 | 01 june
E1 | 10 july
E1 | 01 june
P3 | 02 june
P2 | 01 june
База Java
Set<Activity> actives = getRepository().findActiveActivities().stream()
.sorted(Comparator.comparing(Activity::getModificationDate).reversed())
.collect(Collectors.toCollection(LinkedHashSet::new));
actives = actives.stream().sorted((Activity a, Activity b) -> {
//sort logic
}).collect(Collectors.toCollection(LinkedHashSet::new));
Я действительно не знаю, можно ли выполнить запрос sql, надеюсь, что кто-то может предоставить мне решение.
EDIT:
Это не школьный проект. Я делаю службу отдыха, которая предоставляет модели баз данных.
Proff для мышления ppl Я лжец: ссылка
В этот момент запрос get на http://localhost/activity
предоставляет заданные действия, несортированные, как json.
Мой веб-сайт, вызывающий этот веб-сервис, также имеет несортированные действия, а таблица действий полностью нечитаема.
Ответы
Ответ 1
Итак, моя идея такова. Поскольку в действии parent
содержится множество всех детей, мы можем использовать это, чтобы получить список родителей, отсортированный по последнему обновленному ребенку.
List<Activity> collect = list.stream().filter(activity -> activity.getParent() == null)
.sorted((o1, o2) -> Long.compare(o2.children.stream().max(Comparator.comparing(Activity::getUpdateDate)).orElse(o2)
.getUpdateDate(), o1.children.stream().max(Comparator.comparing(Activity::getUpdateDate)).orElse(o1).getUpdateDate()))
.collect(Collectors.toCollection(LinkedList::new));
Теперь создайте List
, где будет сохранен результат.
List<Activity> result = collect.stream().flatMap(set ->
Stream.concat(Stream.of(set), set.getChildren().stream()
.sorted((o1, o2) -> Long.compare(o2.getUpdateDate(), o1.getUpdateDate())))
).collect(Collectors.toCollection(LinkedList::new);
Вы получите List
действий по вашему желанию, сначала родительский, затем его дети отсортированы по updateDate и т.д.
ОБНОВЛЕНИЕ
Если дерево имеет несколько уровней, вам нужно будет рекурсивно получить все его дочерние элементы, а затем отсортировать их.
Метод получения всех детей Activity
public static Stream<Activity> getChildren(Activity activity) {
List<Activity> activities = new LinkedList<>();
recursiveCall(activities, activity);
return activities.stream();
}
public static void recursiveCall(List<Activity> activities, Activity activity) {
if(activity.children.isEmpty()) {
return;
}
activity.getChildren().forEach(child -> {
activities.add(child);
recursiveCall(activities, child);
});
}
И используйте его следующим образом:
list.stream().filter(activity -> activity.getParent() == null)
.flatMap(YourClass::getChildren)
.sorted...
Сделайте то же самое, когда вы соберете все результаты внутри потока.
Ответ 2
Введение
Я начну использовать SQL-подход и последую позже с Java.
(SQL - моя любовь).
Подход SQL
Я действительно не знаю, может ли он быть выполненным sql-запросом, надеюсь, что кто-то сможет предоставить мне решение.
Это можно решить с помощью SQL-подхода. Вот первый неоптимизированный пример:
Схема
create table Activity(
name varchar(2),
modificationDate int,
parentActivity varchar(2)
);
insert into activity values('E1', 1, 'P1' );
insert into activity values('P2', 1, null );
insert into activity values('P1', 1, null );
insert into activity values('P3', 2, null );
insert into activity values('E1', 10, 'P1' );
Запрос (пример игры с версией 1)
SELECT *
FROM
( SELECT a1.*,
(SELECT max(modificationDate)
FROM Activity a2
GROUP BY a2.parentActivity
HAVING a2.parentActivity = a1.name) AS childMod,
1 AS isParent
FROM Activity a1
WHERE a1.parentActivity IS NULL
UNION SELECT a3.*,
(SELECT max(modificationDate)
FROM Activity a4
GROUP BY a4.name
HAVING a3.name = a4.name), 0 AS isParent
FROM Activity a3
WHERE a3.parentActivity IS NOT NULL ) AS TEMP
ORDER BY childMod DESC,
isParent DESC,
temp.modificationDate DESC;
Вышеописанное можно улучшить, удалив часть неэффективного субобъекта и вместо этого используя объединенные подзапросы (aliased temp table) и/или унарные self-соединения.
Результат: http://i.prntscr.com/hnTeXtQzR5_BRui3TXJFSw.png
Запрос (пример игры с версией 2)
select
*
FROM
(
select name, modificationDate, childMaxMod, 1 as isParent from
Activity a1 left outer join
(select ParentActivity, max(modificationDate) as childMaxMod from Activity group by ParentActivity) as a2
on a1.name = a2.parentActivity
WHERE a1.parentActivity IS NULL
UNION
select name, modificationDate, childMaxMod, 0 as isParent from
Activity a1 inner join
(select ParentActivity, max(modificationDate) as childMaxMod from Activity group by ParentActivity) as a2
on a1.parentActivity = a2.parentActivity
) as Temp
order by childMaxMod desc, isParent desc, modificationDate desc;
Подход Java
Решение Luca неплохое и представляет собой чисто функциональное решение java 8.
Я размещаю свой собственный вариант, но он использует императивное программирование на Java.
Класс действия
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Builder
@Data
@AllArgsConstructor
class Activity {
long modificationDate;
String name;
Activity parentActivity;
Set<Activity> subActivities = new HashSet();
boolean active;
public String debugString;
public int hashCode(){
return Objects.hash(modificationDate, name, subActivities, active);
}
}
Раздел кода - настройка
public static void main(String[] args) {
//SpringApplication.run(MneServiceApplication.class, args);
ArrayList<Activity> set = new ArrayList<Activity>();
Activity p1 = Activity.builder().modificationDate(0).name("P1").subActivities(new HashSet<>()).build();
Activity e1 = Activity.builder().modificationDate(10).name("E1").subActivities(new HashSet<>()).build();
Activity e1_2 = Activity.builder().modificationDate(01).name("E1").subActivities(new HashSet<>()).build();
Activity p3 = Activity.builder().modificationDate(02).name("P3").subActivities(new HashSet<>()).build();
Activity p2 = Activity.builder().modificationDate(01).name("P2").subActivities(new HashSet<>()).build();
p1.getSubActivities().add(e1);
p1.getSubActivities().add(e1_2);
e1.setParentActivity(p1);
e1_2.setParentActivity(p1);
set.add(p1);
set.add(e1);
set.add(e1_2);
set.add(p3);
set.add(p2);
Код - логика (продолжение)
Set<Activity> actives = set.stream()
.sorted(Comparator.comparing(Activity::getModificationDate).reversed())
.collect(Collectors.toCollection(LinkedHashSet::new));
HashMap<Activity, Optional> maxChildModifications = new HashMap<Activity, Optional>();
actives = actives.stream().sorted((Activity a, Activity b) -> {
DecimalFormat decimalFormat = new DecimalFormat("0000000000000000");
String comparisonValue_A = getComparisonString2(maxChildModifications, a, decimalFormat);
String comparisonValue_B = getComparisonString2(maxChildModifications, b, decimalFormat);
a.debugString = comparisonValue_A;
b.debugString = comparisonValue_B;
return comparisonValue_B.compareTo(comparisonValue_A);
}).collect(Collectors.toCollection(LinkedHashSet::new));
for(Activity activity:actives){
System.out.println(activity.getName() + " " + activity.getDebugString());
}
}
private static String getComparisonString2(HashMap<Activity, Optional> maxChildModificationsLookup, Activity activity, DecimalFormat decimalFormat) {
//Is a Parent or a Child
Activity lookupActivity = activity.getParentActivity();;
if(lookupActivity == null){ //It is a parent
lookupActivity = activity;
}
String comparisonValue_B = "";
Long isParent_B = 0L;
Long maxChildMod_B = 0L;
Optional<Activity> maxChildActivityOptional_B = getMaxChildModActivity(lookupActivity, maxChildModificationsLookup);
if(maxChildActivityOptional_B.isPresent()) {
maxChildMod_B = maxChildActivityOptional_B.get().getModificationDate();
}
if(activity.getSubActivities().size() > 0){
isParent_B = 1L; //Or use some other comparison system than the concatenated string
}else{
isParent_B = 0L;
}
comparisonValue_B = String.valueOf( decimalFormat.format(maxChildMod_B)).concat("-").concat(String.valueOf(isParent_B));
comparisonValue_B = comparisonValue_B .concat("-").concat(decimalFormat.format(activity.getModificationDate()));
return comparisonValue_B;
}
private static Optional<Activity> getMaxChildModActivity(Activity a, HashMap<Activity, Optional> maxChildModificationsLookup) {
Optional<Activity> result = maxChildModificationsLookup.get(a);
if(result!=null){
return result;
}
result = a.getSubActivities().stream().max((Activity a2, Activity b2) ->
{
return new Long(a2.getModificationDate()).compareTo(b2.getModificationDate());
}
);
maxChildModificationsLookup.put(a, result);
return result;
}
Результат:
P1 0000000000000010-1-0000000000000000
E1 0000000000000010-0-0000000000000010
E1 0000000000000010-0-0000000000000001
P3 0000000000000000-0-0000000000000002
P2 0000000000000000-0-0000000000000001
Очевидно, что мое SQL-решение и решения Luca Java 8 более сжатые и элегантные, чем вышеупомянутая реализация Java (хотя для удобочитаемости могут потребоваться некоторые изменения форматирования).