提问人:codrp 提问时间:10/5/2023 最后编辑:Arvind Kumar Avinashcodrp 更新时间:10/11/2023 访问量:152
比较 Java 中数组列表中的日期后返回最新日期
Return the most recent date after comparing dates from array list in Java
问:
在比较数组列表中的所有元素后,如何返回最近的日期?预期是返回值,因为它是所有元素中列表中的最新日期。谁能帮忙?2023-01-03T02:00:00.800Z
我使用 JDoodle 创建了一个工作示例
法典
import java.util.ArrayList;
class Car {
String name;
String sold;
Car(String name, String sold) {
this.name = name;
this.sold = sold;
}
}
public class MyClass {
public static void main(String args[]) {
ArrayList <Car> carsList = new ArrayList <Car> ();
carsList.add(new Car("BMW", "2023-01-01T02:00:00.800Z"));
carsList.add(new Car("Jeep", "2023-01-02T02:00:00.800Z"));
carsList.add(new Car("Benz", "2023-01-03T02:00:00.800Z"));
for (int i = 0; i < carsList.size(); i++) {
for (int j = i + 1; j < carsList.size(); j++) {
int res = carsList.get(i).sold.compareTo(carsList.get(j).sold);
System.out.println(res);
}
}
}
}
答:
您可以通过以下方式替换循环:for
String mostRecentDate = carsList.get(0).sold;
for (int i = 1; i < carsList.size(); i++) {
String currentDate = carsList.get(i).sold;
if (currentDate.compareTo(mostRecentDate) > 0) {
mostRecentDate = currentDate;
}
}
但老实说,最好的方法是使用以下方法比较日期(因为它是一个日期):sold
LocalDateTime
if (currentDate.isAfter(mostRecentDate)) {
mostRecentDate = currentDate;
}
评论
ZonedDateTime
LocalDateTime
ZonedDateTime
String
ZonedDateTime
Car
Car
String
String
LocalDateTime
这里是错误的类型。输入字符串表示从零小时-分钟-秒的 UTC 偏移量中看到的时间日期。A只有这三个部分中的两个:日期和时间,但缺少偏移量(或时区)的概念。 这里其实是合适的类型。LocalDateTime
Instant
toInstant
。仅当问题域需要了解该特定时区时才使用。显然,问题域并不关心特定的时区,因为示例数据都是 UTC 格式(注意输入字符串末尾的 )。如果对特定时区不感兴趣,👉🏼则应将某个时刻表示为 。ZonedDateTime
Instant
ZonedDateTime
Z
Instant
您可以流式传输汽车,提取日期并找到最大日期,如下所示:
String maxDate = carsList.stream()
.map(car -> car.sold)
.max(String::compareTo)
.orElse(null); // return null if no "sold" timestamps found
观看现场演示。
您的日期(实际上是时间戳)只能与字符串进行有意义的比较,因为它们是 ISO 格式。使用更合适的类型来存储它们会更好、更清晰,例如 LocalDateTime
评论
get()
orElse(null)
orElse(null)
我建议您声明为 .但是,如果出于某种原因想将其保留为 a,则可以将其解析为 .请注意,日期时间字符串已采用 ISO 8601 格式;因此,您不需要使用 a 来解析它们。sold
Instant
String
Instant
DateTimeFormatter
carsList.stream()
.max(Comparator.comparing(car -> Instant.parse(car.sold)))
.get()
.sold
从 Trail 了解有关新式日期时间 API 的更多信息:日期时间
评论
Instant
ZonedDateTime
sold
ZonedDateTime
ZonedDateTime
Instant
Instant
Instant::toString
Instant
其他答案是正确的。但这里有一些替代语法,以及使用正确的日期时间类型。我们看到了 Java 21+ 中的新特性。SequencedCollection
TL的;博士
将类定义为实现 .Comparable
record CarSale( String name , Instant whenSold ) implements Comparable < CarSale >
{
private static final Comparator < CarSale > COMPARATOR =
Comparator
.comparing ( CarSale :: whenSold )
.thenComparing ( CarSale :: name );
@Override
public int compareTo ( final CarSale other )
{
return CarSale.COMPARATOR.compare ( this , other );
}
}
将生成的排序集合定义为其实现为 .通过获取该系列的最后一个元素来获得最新的汽车销售。SequencedCollection
TreeSet
CarSale latest =
new TreeSet <> (
List
.of (
new CarSale ( "BMW" , Instant.parse ( "2023-01-01T02:00:00.800Z" ) ) ,
new CarSale ( "Benz" , Instant.parse ( "2023-01-03T02:00:00.800Z" ) ) ,
new CarSale ( "Jeep" , Instant.parse ( "2023-01-02T02:00:00.800Z" ) )
)
)
.getLast ( ); // Java 21+.
CarSale[名称=奔驰,何时售出=2023-01-03T02:00:00.800Z]
详
命名
你的名字有点含糊不清。可以更清楚,例如.上课实际上是关于汽车何时出售的。所以会更精确。sold
whenSold
Car
CarSale
记录
如果类的主要目的是透明地传达浅层不可变数据,请将类定义为记录。编译器隐式创建构造函数 getter 和 。Car
equals
hashCode
toString
record CarSale ( String name , String whenSold ) {}
java.time.Instant
第二个参数 表示时间轴上的一个时刻和点。Java 包含用于此类日期时间工作的 java.time 类。使用 java.time.Instant
表示时间轴上的一个点,如 UTC 所示(与 UTC 的偏移量为 0 小时-分钟-秒)。whenSold
record CarSale ( String name , Instant whenSold ) {}
输入字符串使用标准 ISO 8601 格式来表示该时刻。默认情况下,java.time 类在解析/生成文本时使用 ISO 8601 格式。因此,我们可以直接解析您的输入。
CarSale car = new CarSale ( "BMW" , Instant.parse( "2023-01-01T02:00:00.800Z" ) ) ;
List.of
您可以使用 List.of 更紧凑地收集实例化对象。这将生成一个不可修改的 List
对象。CarSale
示例数据已按所需的排序顺序排列。因此,我更改了您的原始顺序,以便我们可以验证是否正在进行排序。
List < CarSale > carSales =
List.of (
new CarSale ( "BMW" , Instant.parse ( "2023-01-01T02:00:00.800Z" ) ) ,
new CarSale ( "Benz" , Instant.parse ( "2023-01-03T02:00:00.800Z" ) ),
new CarSale ( "Jeep" , Instant.parse ( "2023-01-02T02:00:00.800Z" ) )
);
Comparable
其他答案应用比较器
。这是一种有效的方法。但是,如果对这些记录进行排序的主要默认方式是 ,则将其作为功能构建到您的类中。若要生成它,请使用其方法实现 Comparable
接口。CarSale
whenSold
compareTo
为了实现,我们可以定义一个 contained 来做这项工作。使其具有单例常量。compareTo
Comparator
static
在排序时,当两辆或更多汽车同时售出时,我们可能会打成平局。为了打破这种联系,我们可以看看唯一的其他属性,.whenSold
name
private static final Comparator < CarSale > COMPARATOR =
Comparator
.comparing ( CarSale :: whenSold )
.thenComparing ( CarSale :: name );
完成该方法。compareTo
@Override
public int compareTo ( final CarSale other )
{
return CarSale.COMPARATOR.compare ( this , other );
}
排序
最后,我们进行排序。
我们有一个不可修改的列表,无法排序。因此,我们需要创建一个可以排序的可修改集合。我们可以通过将不可修改的列表传递给可修改实现的构造函数来做到这一点。List
new ArrayList <> ( carSales )
Stream
排序
从那里,我们可以通过创建流进行排序,要求流使用我们对象的“自然顺序”进行自我排序。自然秩序是由它的存在所决定的秩序。我们将生成的排序元素收集到另一个 .CarSale
Comparable
List
List < CarSale > carSalesSorted = new ArrayList <> ( carSales ).stream ( ).sorted ( ).toList ( );
SequencedCollection
在 Java 21 中,我们可以使用更通用的接口 SequencedCollection
而不是 .请参阅 JEP 431:序列化集合。List
SequencedCollection < CarSale > carSalesSorted = new ArrayList <> ( carSales ).stream ( ).sorted ( ).toList ( );
TreeSet
代替ArrayList
此时,我们意识到可以使用 SequencedCollection
的不同实现。我们可以使用 TreeSet
,它会自动将其元素按排序顺序排列。这只有在我们不关心跟踪重复对象时才有效,因为 Set
实现就像禁止重复一样。我们可以简单地将不可修改项传递给 TreeSet
的构造函数。TreeSet
List
SequencedCollection < CarSale > carSalesSorted = new TreeSet <> ( carSales );
若要访问元素,请进行迭代。例如,调用 java.lang.Iterable#forEach
。这是有效的,因为每个对象也是一个 Iterable
对象。SequencedCollection
carSalesSorted.forEach ( System.out::println );
运行时:
CarSale[name=BMW, whenSold=2023-01-01T02:00:00.800Z]
CarSale[name=Jeep, whenSold=2023-01-02T02:00:00.800Z]
CarSale[name=Benz, whenSold=2023-01-03T02:00:00.800Z]
getLast
通过从我们的 .只需调用 getLast
。SequencedCollection
CarSale latest = carSalesSorted.getLast ( );
CarSale[名称=奔驰,何时售出=2023-01-03T02:00:00.800Z]
完整的代码示例:
package work.basil.example;
import java.time.Instant;
import java.util.*;
public class CarsLedger
{
public static void main ( String[] args )
{
record CarSale( String name ,
Instant whenSold ) implements Comparable < CarSale >
{
private static final Comparator < CarSale > COMPARATOR =
Comparator
.comparing ( CarSale :: whenSold )
.thenComparing ( CarSale :: name );
@Override
public int compareTo ( final CarSale other )
{
return CarSale.COMPARATOR.compare ( this , other );
}
}
List < CarSale > carSales =
List.of (
new CarSale ( "BMW" , Instant.parse ( "2023-01-01T02:00:00.800Z" ) ) ,
new CarSale ( "Benz" , Instant.parse ( "2023-01-03T02:00:00.800Z" ) ) ,
new CarSale ( "Jeep" , Instant.parse ( "2023-01-02T02:00:00.800Z" ) )
);
//SequencedCollection < CarSale > carSalesSorted = new ArrayList <> ( carSales ).stream ( ).sorted ( ).toList ( );
SequencedCollection < CarSale > carSalesSorted = new TreeSet <> (
List.of (
new CarSale ( "BMW" , Instant.parse ( "2023-01-01T02:00:00.800Z" ) ) ,
new CarSale ( "Benz" , Instant.parse ( "2023-01-03T02:00:00.800Z" ) ) ,
new CarSale ( "Jeep" , Instant.parse ( "2023-01-02T02:00:00.800Z" ) )
)
);
carSalesSorted.forEach ( System.out :: println );
CarSale latest = carSalesSorted.getLast ( );
System.out.println ( "latest = " + latest );
}
}
"...在比较数组列表中的所有元素后,如何返回最近的日期?..."
您可以使用 ZonedDateTime#parse 方法。
将 b 与 a 进行比较,首先按最近日期排序。
import static java.time.ZonedDateTime.parse;
carsList.sort((a, b) -> parse(b.sold).compareTo(parse(a.sold)));
或者,在一行中,有一个流。
Car c = carsList.stream()
.sorted((a, b) -> parse(b.sold).compareTo(parse(a.sold)))
.findFirst()
.get();
输出
Benz 2023-01-03T02:00:00.800Z
如果不想使用流或闭包,可以使用匿名类。
carsList.sort(new Comparator<>() {
@Override
public int compare(Car a, Car b) {
return parse(b.sold).compareTo(parse(a.sold));
}
});
评论
SequencedCollection
java.time
Car latestSold = carsList.stream().max(Comparator.comparing(Car::getSold)).orElseThrow();
sold
Instant
Collcetions.max(carList, Comparator.comparing(Car::getSold))