繁体   English   中英

从列表中获取最年轻的人<Person>其中 Person 在字符串 ISO8601 fromat 中具有出生日期

[英]Get youngest Person from List<Person> where Person has date of Birth in string ISO8601 fromat

class Person {
    String name;
    String id;
    String dateOfBirth;
    int salary;
 }

 List <Person> listOfPersons; // Consider that list is filled with some data

例如dateOfBirth = "2018-12-10T13:49:51.141Z";

出生日期在 ISO8601 中,可以使用Date.from(Instant.parse(dateOfBirth));转换为java.util.Date 如果这可以帮助对列表进行排序。

那么有没有办法在 JAVA 8 中使用流或不错的解决方案来从人员列表中获取最年轻的人员?

附加说明:无法更改 Person 类。

你可以这样做

Person youngestPerson = listOfPersons.stream()
.reduce((p1, p2) -> LocalDateTime.parse(p1.getStringDateofBirth()).isAfter(LocalDateTime.parse(p2.getStringDateofBirth()))?p1:p2)
.orElse(null)

UTC ISO-8601 日期字符串可以按字典顺序进行比较。 因此,我认为最优雅的 Java 8 解决方案是:

Person youngest = listOfPersons.stream()
    .max(Comparator.comparing(p -> p.dateOfBirth)).get();

如果您不信任字符串比较,或者您的时区无处不在,您可以根据需要解析日期:

Person youngest = listOfPersons.stream()
    .max(Comparator.comparing(p -> LocalDateTime.parse(p.dateOfBirth, DateTimeFormatter.ISO_DATE_TIME)))
    .get();

甚至

Person youngest = listOfPersons.stream()
    .max(Comparator.comparing(p -> Instant.parse(p.dateOfBirth))).get();

请注意,您可能应该使用LocalDateTime (或Instant )而不是Date ,因为 Java 8 已经远离了旧的日期/时间 API(这里有一个很好的概述)。 如果您使用LocalDateTime ,则需要使用ISO_DATE_TIME格式化程序在最后处理Z区域。

可运行的示例在这里。


感兴趣的 API:

  • Stream是 Java 8 的一种……可迭代的转换接口,支持许多方便的方法和方法链。 所有Collection对象都有一个stream()成员来获取一个。
  • Stream.max()根据您指定的某个Comparator返回流中的最大值(可以是 lambda 函数,或者在上述情况下,只是一个Comparator对象)。
    • 在这种情况下,返回值实际上是Optional<Person> (例如,如果列表为空,则可能没有值),因此get()获取该值,或者您可以将其保留为Optional如果您愿意,或者您如果您希望空列表为空,可以使用例如orElse(null)而不是get()
  • Comparator.comparing()是一个方便的小实用程序,它构造一个Comparator<T>给定一个将T映射到可比较值的函数。

所以在上面的代码中:

  • p -> p.dateOfBirth (或p -> LocalDateTime.parse(p.dateOfBirth) ,你的电话)是一个 lambda 函数,它将一个人映射到他们的生日。
  • Comparator.comparing(...) ,当给定所述键映射函数时,返回一个Comparator两个人的出生日期的 Comparator,根据您使用的是String还是LocalDateTime版本执行适当的比较。
  • max()返回具有最大日期的对象,即最年轻的人。
  • get()只是将PersonOptional<Person>中取出。

完整示例(链接到上面的 ideone):

import java.util.*;
import java.lang.*;
import java.io.*;
import java.time.*;
import java.time.format.*;

class Ideone {

    public static void main (String[] args) throws java.lang.Exception {
        
        Person youngest;
        
        // you could do string comparison if you know the timezones are consistent:
        
        youngest = listOfPersons.stream()
            .max(Comparator.comparing(p -> p.dateOfBirth)).get();
        
        System.out.println("youngest is: " + youngest.name + " " + youngest.dateOfBirth);

        // or you could just parse the date, a safer option:
        
        youngest = listOfPersons.stream()
            .max(Comparator.comparing(p -> LocalDateTime.parse(p.dateOfBirth, DateTimeFormatter.ISO_DATE_TIME))).get();
        
        System.out.println("youngest is: " + youngest.name + " " + youngest.dateOfBirth);
        
    }

    
    //------------------------------------
    // types from OP:
    
    static class Person{
        String name;
        String id;
        String dateOfBirth;
        int salary;
    }

    static List <Person> listOfPersons;

    
    //------------------------------------
    // boring example data initialization:
    
    // make an iso birthdate string given an age (for initializing data)
    static String fromAge (int years) {
        //return LocalDateTime.now().minusYears(years).toString();
        // add zone to emulate OP's input:
        return LocalDateTime.now().minusYears(years).toString() + 'Z';
    }
    
    // make up some data
    static {
        listOfPersons = List.of(
            // fine for example, but i wouldn't init like this in production
            new Person() {{ name="helga"; dateOfBirth=fromAge(22); }},
            new Person() {{ name="mortimer"; dateOfBirth=fromAge(48); }},
            new Person() {{ name="gertrude"; dateOfBirth=fromAge(6); }},
            new Person() {{ name="jebediah"; dateOfBirth=fromAge(39); }}
        );
        listOfPersons.forEach(p -> System.out.println(p.name + ' ' + p.dateOfBirth));
    }

    
} 

输出:

helga 2000-06-23T03:07:26.084850Z
mortimer 1974-06-23T03:07:26.126628Z
gertrude 2016-06-23T03:07:26.126894Z
jebediah 1983-06-23T03:07:26.127948Z
youngest is: gertrude 2016-06-23T03:07:26.126894Z
youngest is: gertrude 2016-06-23T03:07:26.126894Z

正如其他人所建议的那样,使用LocalDateTime LocalDateTime具有非常方便的功能,完全适合您的问题。 您可以直接将您的字符串解析到其中,只需从您的 ISO 字符串中删除“Z”就可以了:

LocalDateTime someBirthday = LocalDateTime.parse("2018-12-10T13:49:51.141");

下一步,编写一个比较器:

public class BirthdayComparator implements Comparator<LocalDateTime> {

    @Override
    public int compare(LocalDateTime date1, LocalDateTime date2) {
        if(date1.isAfter(date2)) {
            return 1;
        } else if (date1.equals(date2)) {
            return 0;
        } else {
            return -1;
        }
    }
}

而已。 您现在可以按出生日期排序。

更新

最后一句警告:此代码忽略时区。 准确地说,它隐含地假设,所有给定的生日都在同一个时区。 如果您必须与全球各地的人打交道,这可能会出现问题......

这是一种方法。

record Person(String name, String id, String dateOfBirth,
                int salary) {}
        
List<Person> list = new ArrayList<>(List.of(
        new Person("Jim", "10", "2018-12-10T13:49:51.141Z",
                20),
        new Person("Mary", "11", "2018-12-15T13:49:51.141Z",
                20),
        new Person("Bob", "12", "2018-12-12T13:49:51.141Z",
                20)));
  • 使用maxBy ,因为最年轻的人会在离时代更远的地方出生。
  • 解析时间的Instant 您无法使用LocalDateTime解析给定的格式示例
Person youngest = list.stream().collect(Collectors.maxBy(Comparator
        .comparing(p -> Instant.parse(p.dateOfBirth)))).get();

System.out.println(youngest);

印刷

Person[name=Mary, id=11, dateOfBirth=2018-12-15T13:49:51.141Z, salary=20]

请注意, maxBy返回一个Optional ,因此最后调用get() 您可能希望返回Optional<Person>然后根据查找的成功提取日期。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM