android 按多个字段和多个条件排序列表

yb3bgrhw  于 2023-04-10  发布在  Android
关注(0)|答案(6)|浏览(208)

我有以下代码:

list.sort(Comparator
    .comparing(ProgrammData::getEnd)
    .thenComparing(ProgrammData::getStart).reversed());

我的问题是我想让我的列表按多个东西排序:1.)将它们分组为未来事件和过去事件(通过检查System.currentMilliseconds()是否大于结束时间戳)2.)按开始升序对未来事件排序3.)按结束降序对过去事件排序
我可以用Java 8 Lambda来做这个吗?或者我需要另一种排序项目的方法吗?
示例:

events could look like this:
name, start, end
event1, 2022-02-220100, 2022-02-220300 
event2, 2022-02-220200, 2022-02-241800
event3, 2022-02-251200, 2022-02-281500
event4, 2022-02-261600, 2022-02-262100

if now() is 2022-02-221200 So the order should be:

event3 (next item in the future)
event4 (2nd next item in the future)
event2 (Ended closer to now than event1)
event1 (Longest in the past)
kh212irz

kh212irz1#

正如我所理解的这个问题,你依赖于一个 * 现有的API*,它期望一个事件列表,并希望它们作为一个单个列表一起处理,并根据它们的 * 开始 * 和 * 结束 * 日期-时间进行排序。这是可以实现的。
我假设存储在ProgrammData对象中的与时间相关的事件数据的类型是String。如果不是这样,并且这些字段是例如遗留类型Date,那么只需要做一些小的更改。
我的想法是将所有功能封装在TwoWaySorting中,这样所有的实现细节都被抽象掉了。在客户端代码中,只需要TwoWaySorting.getSorted(programmData)这一行就可以生成一个事件的排序列表。
由于TwoWaySorting类的责任与状态无关,因此它的所有行为都用 static 修饰符标记,其构造函数为 private。它有一个嵌套的静态类Element,它是ProgrammData顶部的 Package 器(如果我假设ProgrammData是现有IPA的一部分是正确的,那么它必须按原样使用而不做任何更改)。 Package 后的ProgrammData对象的相关数据以方便的形式进行排序
下面是getSorted()方法的简要概述:

  • 获取示例Comparator,基于 * 当前时间 *;
  • 在事件列表上创建 stream,并用Element类的示例 * Package * 每个事件;
    • 对元素进行排序;
  • 提取 * 原始事件 * 并将它们收集到 * 列表 * 中。

为了 Package 事件,静态方法LocalDateTime.parse()接受CharSequence和适当的formatter用于解析基于字符串的时间相关数据。
TwoWaySorting类的核心是getComparator()方法返回的comparator,让我们仔细研究一下。
它的第一部分负责根据currentTime将元素分为两组:

Comparator.<Element, Boolean>comparing(element -> element.getEnd().isBefore(currentTime))

顾名思义,LocalDateTime类的示例方法isBefore()返回true如果 * 此日期-时间 * 对象作为参数传递的 * 日期-时间对象 * 之前。
根据boolean值的自然排序顺序,falsetrue之前。因此,对于在未来结束的invent,isBefore()将产生false,这意味着它将出现在排序列表的 * 开头 *。

    • 比较器**的第二部分负责 * 过去 * 和 * 未来 * 事件的排序:
.thenComparingLong(element -> element.getEnd().isAfter(currentTime) ?
                                        element.getStart().toEpochSecond(ZoneOffset.of("+00:00")) :
                                        element.getEnd().toEpochSecond(ZoneOffset.of("+00:00")) * -1);

返回:一个字典顺序比较器,由thislong sort key组成

方法thenComparingLong()(* 上面显示了javadoc的引用 *)返回一个聚合比较器,它由之前获得的 comparator(分离 * 过去 * 和 * 未来 * 事件)和一个 comparator 组成,该 *comparator * 基于作为参数提供的ToLongFunction,它相应地将元素与该 * 函数 * 提取的long值进行比较。
方法toEpochSecond()从日期时间对象中提取 epoch 的 * 秒数 * 作为long

  • 我假设它足以完成此任务,因为在示例中,时间是以分钟的精度描述的。ZoneOffsettoEpochSecond()作为参数所期望的,在这种情况下,对结果没有影响,格林威治的偏移量可以用任何其他有效偏移量替换。*

由于 future 事件必须按升序排序toEpochSecond()产生的值按原样使用,对于 past 事件必须按降序排序它乘以-1以反转比较结果。

注:

  • 为了将上面描述的两个比较器组合在一起,我们必须显式地提供泛型类型信息,如:<Element,Boolean>comparing()。如果没有显式声明,编译器没有足够的数据来确定变量element类型,在comparing()thenComparingLong()中,其类型将被推断为**Object。如果我们只使用这些静态方法中的类型编译器将根据getComparator()方法的 * 返回类型 * 正确推断为Element。但对于我们的情况,我们需要显式提供此信息。

for information on the syntax of generic methods, take a look at this tutorial
TwoWaySortingclass

public class TwoWaySorting {
    private static final DateTimeFormatter PD_FORMATTER
            = DateTimeFormatter.ofPattern("yyyy-MM-ddHHmm");

    private TwoWaySorting() {} // no way and no need to instantiate this class

    private static Comparator<Element> getComparator() {
        LocalDateTime currentTime = LocalDateTime.now();

        return Comparator.<Element, Boolean>comparing(element -> element.getEnd().isBefore(currentTime))
                         .thenComparingLong(element -> element.getEnd().isAfter(currentTime) ?
                                            element.getStart().toEpochSecond(ZoneOffset.of("+00:00")) :
                                            element.getEnd().toEpochSecond(ZoneOffset.of("+00:00")) * -1);
    }

    public static List<ProgrammData> getSorted(List<ProgrammData> programmData) {
        Comparator<Element> twoWayComparator = getComparator();

        return programmData.stream()
                .map(TwoWaySorting::parseData)
                .sorted(twoWayComparator)
                .map(Element::getData)
                .collect(Collectors.toList());
    }

    private static Element parseData(ProgrammData data) {
        return new Element(data,
                           LocalDateTime.parse(data.getStart(), PD_FORMATTER),
                           LocalDateTime.parse(data.getEnd(), PD_FORMATTER));
    }

    private static class Element {
        private ProgrammData data;
        private LocalDateTime start;
        private LocalDateTime end;

        // constructor and getters
    }
}

这个解决方案是为了 * 干净 * 和 * 可重用 *。所以在main()中,除了 source list 之外,只有一行代码可以获得一个排序的列表并将其打印在控制台上。

注意:getSorted()不会导致源的突变,而是创建一个新的列表。

public static void main(String[] args) {
    List<ProgrammData> programmData = // a list dummy ProgrammData objects
            List.of(new ProgrammData("event1", "2022-02-220100", "2022-02-220300"),
                    new ProgrammData("event2", "2022-02-220200", "2022-02-241800"),
                    new ProgrammData("event3", "2022-02-251200", "2022-02-281500"),
                    new ProgrammData("event4", "2022-02-261600", "2022-02-262100"));

    TwoWaySorting.getSorted(programmData)
                 .forEach(System.out::println);
}

输出(与提供的示例相同)

ProgrammData [event3, 2022-02-251200, 2022-02-281500]
ProgrammData [event4, 2022-02-261600, 2022-02-262100]
ProgrammData [event2, 2022-02-220200, 2022-02-241800]
ProgrammData [event1, 2022-02-220100, 2022-02-220300]
pkwftd7m

pkwftd7m2#

试试这个:

final long currentTime = System.currentTimeMillis();
    list.sort((el1, el2) -> {
        if (el1.equals(el2)) {
            return 0;
        }
        boolean isEl1Future = el1.getEnd().getTime() > currentTime;
        boolean isEl2Future = el2.getEnd().getTime() > currentTime;
        if (isEl1Future != isEl2Future) {
            return isEl1Future ? -1 : 1;
        }
        if (Boolean.TRUE.equals(isEl1Future)) {
            return el1.getStart().before(el2.getStart()) ? -1 : 1;
        }
        return el1.getEnd().after(el2.getEnd()) ? -1 : 1;
    });
yr9zkbsy

yr9zkbsy3#

有很多事情需要注意。首先,一个是.thenComparing(...)方法,它只在前面的比较结果相等时才会发生。你可以在文档中阅读更多关于它的行为。
第二,如果我是你的话,如果我可以用一个简单的比较器来解决这个问题,我就不会过多地使用流。假设你正在寻找ProgrammData列表的一个新示例,我以流的风格编写了我的代码,但是Comparator可以与Listsort方法一起使用。

private List<ProgrammData> sortedProgramms(List<ProgrammData> dataList) {
    final LocalDateTime now = LocalDateTime.now();
    return dataList.stream()
                    .sorted((e1, e2) -> {
                        if (e1.getStart().isAfter(now) && e2.getStart().isAfter(now)) {
                            return e1.getStart().compareTo(e2.getStart());
                        }
                        return e2.getEnd().compareTo(e1.getEnd());
                    })
                    .collect(Collectors.toList());
 }

如果没有更精确的给定时钟,LocalDateTime().now()在内部使用System.currentTimeMillis()。

xt0899hw

xt0899hw4#

你需要把过去和未来的事件分别放在不同的列表中,并进行相应的排序,最后一步是把两个列表连接起来。

public static void main(String[] args) {
    ProgrammData programmData1 = new ProgrammData("a", LocalDateTime.now().plusDays(1),
        LocalDateTime.now().plusDays(1));
    ProgrammData programmData2 = new ProgrammData("b", LocalDateTime.now().plusDays(2),
        LocalDateTime.now().plusDays(2));
    ProgrammData programmData3 = new ProgrammData("c", LocalDateTime.now().minusDays(1),
        LocalDateTime.now().minusDays(1));
    ProgrammData programmData4 = new ProgrammData("c", LocalDateTime.now().minusDays(2),
        LocalDateTime.now().minusDays(2));

    List<ProgrammData> programmDataList = new ArrayList<>();

    programmDataList.add(programmData1);
    programmDataList.add(programmData2);
    programmDataList.add(programmData3);
    programmDataList.add(programmData4);

    final List<ProgrammData> collect = programmDataList.stream().sorted(Comparator
        .comparing(ProgrammData::end)).toList();

    LocalDateTime localDateTime = LocalDateTime.now();
    final List<ProgrammData> pastEvents = collect.stream().filter(pd -> pd.end.isBefore(localDateTime))
        .sorted(Comparator
            .comparing(ProgrammData::end).reversed()).toList();
    final List<ProgrammData> futureEvents = collect.stream().filter(pd -> pd.end.isAfter(localDateTime)).toList();

    List<ProgrammData> sortedListAsRequired = new ArrayList<>();

    sortedListAsRequired.addAll(futureEvents);
    sortedListAsRequired.addAll(pastEvents);

    System.out.println(sortedListAsRequired);
  }

  static record ProgrammData(String name, LocalDateTime start, LocalDateTime end) {

  }

结果是这样的:

[ProgrammData[name=a, start=2022-02-23T18:08:59.564300200, end=2022-02-23T18:08:59.568806900], ProgrammData[name=b, start=2022-02-24T18:08:59.568806900, end=2022-02-24T18:08:59.568806900], ProgrammData[name=c, start=2022-02-21T18:08:59.568806900, end=2022-02-21T18:08:59.568806900], ProgrammData[name=c, start=2022-02-20T18:08:59.568806900, end=2022-02-20T18:08:59.568806900]]
gkl3eglg

gkl3eglg5#

你的例子看起来不正确-event 2是在未来,应该与3和4一起排序。无论如何,你应该把列表分成2,未来和过去的事件,并相应地排序。你可以用流来做,或者把列表分成两部分,选择你喜欢的。
对于设置:

public class ProgrammData {

    public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-ddHHmm");

    private String name;
    private LocalDateTime start;
    private LocalDateTime end;

    public ProgrammData(String name, String start, String end) {
        this.name = name;
        this.start = LocalDateTime.parse(start, FORMATTER);
        this.end = LocalDateTime.parse(end, FORMATTER);
    }

    //getters and setters

    @Override
    public String toString() {
        return "ProgrammData{" +
                "name='" + name + '\'' +
                ", start=" + start +
                ", end=" + end +
                '}';
    }
}

1.选项1 -流

public class ProgrammDataMain {

    public static void main(String[] args) {
        //setup
        ProgrammData event1 = new ProgrammData("event1", "2022-02-220100", "2022-02-220300");
        ProgrammData event2 = new ProgrammData("event2", "2022-02-220200", "2022-02-241800");
        ProgrammData event3 = new ProgrammData("event3", "2022-02-251200", "2022-02-281500");
        ProgrammData event4 = new ProgrammData("event4", "2022-02-261600", "2022-02-262100");
        ProgrammData event5 = new ProgrammData("event5", "2022-02-220600", "2022-02-221159");

        LocalDateTime now = LocalDateTime.parse("2022-02-221200", ProgrammData.FORMATTER);

        List<ProgrammData> list = Arrays.asList(event1, event2, event3, event4, event5);

        //sort
        Comparator<ProgrammData> futureComparator = Comparator.comparing(ProgrammData::getStart);
        Comparator<ProgrammData> pastComparator = Comparator.comparing(ProgrammData::getEnd).reversed();
        list.stream().collect(Collectors.toMap(pd -> pd.getEnd().isBefore(now),
                pd -> {
                    Comparator<ProgrammData> comparator = pd.getEnd().isBefore(now) ? pastComparator : futureComparator;
                    Set<ProgrammData> set = new TreeSet<>(comparator);
                    set.add(pd);
                    return set;
                },
                (s1, s2) -> {
                    s1.addAll(s2);
                    return s1;
                }))
                .entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .map(Map.Entry::getValue)
                .collect(Collectors.toList())
                .forEach(set -> set.forEach(System.out::println));
    }
}

首先,我们将列表流式传输并收集到Map<Boolean, Set<ProgrammData>>中,true key将收集过去的事件,false - future。然后流式Map条目,按键排序以确保未来的事件- false key,在过去之前,并获得值(集合)。因为值在TreeSet中具有相应的比较器,所以它们已经排序。
1.选项2 -将列表拆分为2,对每个列表进行排序,再次收集到单个列表中

public class ProgrammDataMain {

    public static void main(String[] args) {
        //setup
        ProgrammData event1 = new ProgrammData("event1", "2022-02-220100", "2022-02-220300");
        ProgrammData event2 = new ProgrammData("event2", "2022-02-220200", "2022-02-241800");
        ProgrammData event3 = new ProgrammData("event3", "2022-02-251200", "2022-02-281500");
        ProgrammData event4 = new ProgrammData("event4", "2022-02-261600", "2022-02-262100");
        ProgrammData event5 = new ProgrammData("event5", "2022-02-220600", "2022-02-221159");

        LocalDateTime now = LocalDateTime.parse("2022-02-221200", ProgrammData.FORMATTER);

        List<ProgrammData> list = Arrays.asList(event1, event2, event3, event4, event5);

        //sort
        Comparator<ProgrammData> futureComparator = Comparator.comparing(ProgrammData::getStart);
        Comparator<ProgrammData> pastComparator = Comparator.comparing(ProgrammData::getEnd).reversed();
        //sort
        List<ProgrammData> futureList = new ArrayList<>();
        List<ProgrammData> pastList = new ArrayList<>();
        for (ProgrammData programmData : list) {
            if (programmData.getEnd().isBefore(now)) {
                pastList.add(programmData);
            } else {
                futureList.add(programmData);
            }
        }
        futureList.sort(futureComparator);
        pastList.sort(pastComparator);
        List<ProgrammData> resultList = new ArrayList<>(futureList);
        resultList.addAll(pastList);
        resultList.forEach(System.out::println);
    }
}

基本上,创建两个新列表,一个用于将来,另一个用于过去,对每个列表进行排序,将它们添加到新列表中。或者您可以将它们保存在初始列表中。
这两个选项的结果都是按顺序的-事件2、3、4(未来,按开始升序排序),事件5、1(过去,按结束降序排序)。

rqdpfwrv

rqdpfwrv6#

你的例子很混乱。正如你在标题中所说的,event2应该被处理为好像它在将来,由于它的结束时间(2022-02-241800)在现在之后(2022-02-221200),所以有序元素应该是

event2
event3
event4
event1

如果这是正确的,您可以尝试以下操作:

events.sort((e1, e2) -> {
    // -1: e1 and e2 in the past
    //  0: e1 and e2 in distinct periods
    // +1: e1 and e2 in the future
    int period = Integer.signum(
          Integer.signum(e1.getEnd().compareTo(now))
        + Integer.signum(e2.getEnd().compareTo(now))
    );
    if (period == 0) {
        return -e1.getEnd().compareTo(now);
    }
    // > 0: e1 is after e2
    // = 0: e1 is equal to e2
    // < 0: e1 is before e2
    int comparation = e1.getComparingDateTime(now).compareTo(
        e2.getComparingDateTime(now)
    );
    return period * comparation;
});

鉴于

class ProgramData {
    ...
    public LocalDateTime getComparingDateTime(LocalDateTime reference) {
        if (reference.isAfter(end)) {
            // Past
            return end;
        }
        // Future
        return start;
    }
    ...
}

相关问题