这个问题最初是要求日期的。在答案发布后,问题被更改为要求LocalDateTime
。我将保留此答案,因为它(a)回答了最初发布的问题,(b)可能对其他人有帮助。
其他答案看起来很有趣,也可能是正确的。但我发现以下代码更容易理解和验证/调试。
注意: 我不断言这段代码是最好的、最简洁的或最快的。坦白地说,我在这里的尝试只是为了推动我的Java流和lambda使用理解的极限。
不要发明自己的类来保存开始/结束日期。 ThreeTen-Extra库提供了一个LocalDateRange
类,用于表示作为一对java.time.LocalDate
对象附加到时间线上的时间跨度。 LocalDateRange
提供了几种方法,如:
- 比较,如
abuts
和overlaps
。
- 工厂方法,如
union
和intersection
。
我们可以使用Java 9及更高版本中方便的List.of
方法定义输入,以创建一个不可修改的LocalDateRange
列表。
List < LocalDateRange > dateRanges =
List.of(
LocalDateRange.of( LocalDate.of( 2019 , 1 , 1 ) , LocalDate.of( 2019 , 5 , 1 ) ) ,
LocalDateRange.of( LocalDate.of( 2019 , 3 , 1 ) , LocalDate.of( 2019 , 6 , 1 ) ) ,
LocalDateRange.of( LocalDate.of( 2019 , 2 , 1 ) , LocalDate.of( 2019 , 7 , 1 ) ) ,
LocalDateRange.of( LocalDate.of( 2019 , 8 , 1 ) , LocalDate.of( 2019 , 12 , 1 ) ) ,
LocalDateRange.of( LocalDate.of( 2018 , 12 , 1 ) , LocalDate.of( 2019 , 1 , 31 ) )
);
确定涉及的日期总范围,最早开始和最晚结束日期。
请注意,我们正在处理日期范围列表(LocalDateRange),每个日期范围包含一对日期对象(LocalDate)。比较器正在比较存储在每个LocalDateRange中的起始/结束LocalDate对象,以获取最小值或最大值。这里看到的get方法正在获取一个LocalDateRange,因此我们随后调用getStart/getEnd来检索存储在其中的起始/结束LocalDate。
LocalDate start = dateRanges.stream().min( Comparator.comparing( localDateRange -> localDateRange.getStart() ) ).get().getStart();
LocalDate end = dateRanges.stream().max( Comparator.comparing( localDateRange -> localDateRange.getEnd() ) ).get().getEnd();
列出该时间间隔内的所有日期。 LocalDate#datesUntil
方法生成一个 LocalDate
对象流,该流包含在开始和结束日期对之间找到的对象。开始日期是包含的,而结束日期是不包含的。
List < LocalDate > dates =
start
.datesUntil( end )
.collect( Collectors.toList() );
对于这些日期中的每一个,获取包含该日期的日期范围列表。
Map < LocalDate, List < LocalDateRange > > mapDateToListOfDateRanges = new TreeMap <>();
for ( LocalDate date : dates )
{
List < LocalDateRange > hits = dateRanges.stream().filter( range -> range.contains( date ) ).collect( Collectors.toList() );
System.out.println( date + " ➡ " + hits );
mapDateToListOfDateRanges.put( date , hits );
}
对于这些日期中的每一个,获取包含该日期的日期范围的计数。我们想要上面放入映射的每个
List
的计数。生成一个新映射,其值是原始映射中集合计数的计数,在我的问题
Report on a multimap by producing a new map of each key mapped to the count of elements in its collection value中讨论,我从
Syco的答案中提取了代码,请参考。
Map < LocalDate, Integer > mapDateToCountOfDateRanges =
mapDateToListOfDateRanges
.entrySet()
.stream()
.collect(
Collectors.toMap(
( Map.Entry < LocalDate, List < LocalDateRange > > e ) -> { return e.getKey(); } ,
( Map.Entry < LocalDate, List < LocalDateRange > > e ) -> { return e.getValue().size(); } ,
( o1 , o2 ) -> o1 ,
TreeMap :: new
)
);
很遗憾,似乎没有办法通过流过滤地图中多个条目的最大值。请参见:使用Java8 Stream从地图中查找最高值。
因此,首先我们找到映射的一个或多个条目中值的最大数。
Integer max = mapDateToCountOfDateRanges.values().stream().max( Comparator.naturalOrder() ).get();
然后,我们筛选只有该数字值的条目,并将这些条目移动到一个新地图中。
Map < LocalDate, Integer > mapDateToCountOfDateRangesFilteredByHighestCount =
mapDateToCountOfDateRanges
.entrySet()
.stream()
.filter( e -> e.getValue() == max )
.collect(
Collectors.toMap(
Map.Entry :: getKey ,
Map.Entry :: getValue ,
( o1 , o2 ) -> o1 ,
TreeMap :: new
)
);
输出到控制台。
System.out.println( "dateRanges = " + dateRanges );
System.out.println( "start/end = " + LocalDateRange.of( start , end ).toString() );
System.out.println( "mapDateToListOfDateRanges = " + mapDateToListOfDateRanges );
System.out.println( "mapDateToCountOfDateRanges = " + mapDateToCountOfDateRanges );
System.out.println( "mapDateToCountOfDateRangesFilteredByHighestCount = " + mapDateToCountOfDateRangesFilteredByHighestCount );
简短的结果。
[注意: 我没有手动验证这些结果。使用此代码需自行风险评估,并进行自己的验证。]
mapDateToCountOfDateRangesFilteredByHighestCount = {2019-03-01=3, 2019-03-02=3, 2019-03-03=3, 2019-03-04=3, 2019-03-05=3, 2019-03-06=3, 2019-03-07=3, 2019-03-08=3, 2019-03-09=3, 2019-03-10=3, 2019-03-11=3, 2019-03-12=3, 2019-03-13=3, 2019-03-14=3, 2019-03-15=3, 2019-03-16=3, 2019-03-17=3, 2019-03-18=3, 2019-03-19=3, 2019-03-20=3, 2019-03-21=3, 2019-03-22=3, 2019-03-23=3, 2019-03-24=3, 2019-03-25=3, 2019-03-26=3, 2019-03-27=3, 2019-03-28=3, 2019-03-29=3, 2019-03-30=3, 2019-03-31=3, 2019-04-01=3, 2019-04-02=3, 2019-04-03=3, 2019-04-04=3, 2019-04-05=3, 2019-04-06=3, 2019-04-07=3, 2019-04-08=3, 2019-04-09=3, 2019-04-10=3, 2019-04-11=3, 2019-04-12=3, 2019-04-13=3, 2019-04-14=3, 2019-04-15=3, 2019-04-16=3, 2019-04-17=3, 2019-04-18=3, 2019-04-19=3, 2019-04-20=3, 2019-04-21=3, 2019-04-22=3, 2019-04-23=3, 2019-04-24=3, 2019-04-25=3, 2019-04-26=3, 2019-04-27=3, 2019-04-28=3, 2019-04-29=3, 2019-04-30=3}
完整代码
为了方便复制粘贴,这里提供一个完整的类来运行这个示例代码。
package work.basil.example;
import org.threeten.extra.LocalDateRange;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
public class DateRanger
{
public static void main ( String[] args )
{
DateRanger app = new DateRanger();
app.demo();
}
private void demo ( )
{
List < LocalDateRange > dateRanges =
List.of(
LocalDateRange.of( LocalDate.of( 2019 , 1 , 1 ) , LocalDate.of( 2019 , 5 , 1 ) ) ,
LocalDateRange.of( LocalDate.of( 2019 , 3 , 1 ) , LocalDate.of( 2019 , 6 , 1 ) ) ,
LocalDateRange.of( LocalDate.of( 2019 , 2 , 1 ) , LocalDate.of( 2019 , 7 , 1 ) ) ,
LocalDateRange.of( LocalDate.of( 2019 , 8 , 1 ) , LocalDate.of( 2019 , 12 , 1 ) ) ,
LocalDateRange.of( LocalDate.of( 2018 , 12 , 1 ) , LocalDate.of( 2019 , 1 , 31 ) )
);
LocalDate start = dateRanges.stream().min( Comparator.comparing( localDateRange -> localDateRange.getStart() ) ).get().getStart();
LocalDate end = dateRanges.stream().max( Comparator.comparing( localDateRange -> localDateRange.getEnd() ) ).get().getEnd();
List < LocalDate > dates =
start
.datesUntil( end )
.collect( Collectors.toList() );
Map < LocalDate, List < LocalDateRange > > mapDateToListOfDateRanges = new TreeMap <>();
for ( LocalDate date : dates )
{
List < LocalDateRange > hits = dateRanges.stream().filter( range -> range.contains( date ) ).collect( Collectors.toList() );
System.out.println( date + " ➡ " + hits );
mapDateToListOfDateRanges.put( date , hits );
}
Map < LocalDate, Integer > mapDateToCountOfDateRanges =
mapDateToListOfDateRanges
.entrySet()
.stream()
.collect(
Collectors.toMap(
( Map.Entry < LocalDate, List < LocalDateRange > > e ) -> { return e.getKey(); } ,
( Map.Entry < LocalDate, List < LocalDateRange > > e ) -> { return e.getValue().size(); } ,
( o1 , o2 ) -> o1 ,
TreeMap :: new
)
);
Integer max = mapDateToCountOfDateRanges.values().stream().max( Comparator.naturalOrder() ).get();
Map < LocalDate, Integer > mapDateToCountOfDateRangesFilteredByHighestCount =
mapDateToCountOfDateRanges
.entrySet()
.stream()
.filter( e -> e.getValue() == max )
.collect(
Collectors.toMap(
Map.Entry :: getKey ,
Map.Entry :: getValue ,
( o1 , o2 ) -> o1 ,
TreeMap :: new
)
);
System.out.println( "dateRanges = " + dateRanges );
System.out.println( "start/end = " + LocalDateRange.of( start , end ).toString() );
System.out.println( "mapDateToListOfDateRanges = " + mapDateToListOfDateRanges );
System.out.println( "mapDateToCountOfDateRanges = " + mapDateToCountOfDateRanges );
System.out.println( "mapDateToCountOfDateRangesFilteredByHighestCount = " + mapDateToCountOfDateRangesFilteredByHighestCount );
}
}
r
,检查r
的起始点落在多少个范围内。找到最大值。 - Sweeper