Java 8高级排序

时间:2017-08-22 12:57:06

标签: java algorithm sorting

我有一个实体Activity,可以是parentchild

class Activity {
    long modificationDate;
    Activity parentActivity;
    Set<Activity> subActivities;
    boolean active;
}

我必须按照modification date对数据库中先前加载的活动进行排序,但仍然是grouped by他们的父活动。

规则:子E1活动可以更新,而其父P1不是。{因此,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请求执行,希望有人能为我提供解决方案。

修改

这不是一个学校项目。我正在提供提供数据库模型的休息服务。 人们认为我是个骗子:link

此时,http://localhost/activity上的get请求提供了未设置的set活动,如json。 我的网站调用此Web服务也有未分类的活动,活动表完全不可读。

2 个答案:

答案 0 :(得分:8)

所以我的想法如下。由于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...

在收集流中的所有结果时执行相同的操作。

答案 1 :(得分:5)

简介

我将开始使用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;

通过删除一些低效的子查询,而不是使用连接的子查询(别名临时表)和/或一元自连接,可以改进上述内容。

结果: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实现更简洁和优雅(尽管可读性可能需要进行一些格式更改)。