多地图
您可以使用multimap。 Map 是一对导致值的键。在 multimap 中,值实际上是值的集合,而不是单个值。
例如,想象一个包含三个时隙的层次结构,其中第一个和最后一个包含一个空列表(还没有订单),而中间的时隙有一个单一订单的列表。
- 2022-01-23T13:00-07:00[美国/埃德蒙顿]
- 2022-01-23T14:00-07:00[美国/埃德蒙顿]
- [订单[id=2d8e5cc2-26ac-474d-a081-2c71207fd6c5, customerName=Basil]]
- 2022-01-23T15:00-07:00[美国/埃德蒙顿]
(次要细节:2d8e5cc2-26ac-474d-a081-2c71207fd6c5 文本是一个 hexadecimal 字符串表示形式,用于标识该特定订单的 128 位 UUID 值。)
示例代码
这是一些示例代码。这段代码不完整,但会让你朝着正确的方向前进。
首先,定义我们的Order 类。我们将使用 Java 16 中的新功能将其设为record。您也可以定义一个常规类,但记录要简单得多。编译器隐式创建构造函数、getter、equals & hashCode 和 toString。
package work.basil.example.orders;
import java.util.UUID;
public record Order(UUID id , String customerName)
{
}
定义DayManager 类。
构造函数是为跟踪订单设置数据结构的地方。你在你的 addOrder 方法中做这项工作,这是没有意义的。
构造函数的目标是填充一个NavigableMap,一个Map,它维护它的键是一个排序的顺序。我们使用TreeMap作为NavigableMap接口的具体实现。
我们地图的键是ZonedDateTime,这是一个在时区范围内带有时间的日期。每个ZonedDateTime 对象是每个时隙的开始。我使用这种方法而不是你仅仅使用整数来识别每个插槽。
我们地图的值是Order 对象的列表。当我们将订单添加到我们的跟踪系统时,它们会出现在这些列表之一中。每个列表都绑定到一个ZonedDateTime 对象,作为我们地图中的时隙。使用List 的问题在于该类是一个可调整大小的集合。所以我们的代码在这里管理大小限制,检查当前大小以计算列表中已经存在多少订单。我们将该计数与我们在DayManager 类中作为成员字段保留的每时隙容量数进行比较。
- 如果我们的产能不足,我们将订单添加到列表中,并返回一个包含
ZonedDateTime 的Optional 来标识时间段。
- 如果我们有足够的能力,我们需要进入下一个时间段 - 我把这个留给读者作为练习。
- 提示:我们手头有一个
ZonedDateTime,这是用于访问我们地图的当前密钥。因此,如果我们添加存储为成员字段的Duration,我们将获得映射中的下一个键。使用该键获取下一个订单列表。 Lather, rinse, repeat,直到超过一天的最后一个时间段。
顺便说一句,在实际工作中,我会寻找第三方库,例如 Eclipse Collections 或 Google Guava 以获得固定大小的列表类。最好依赖已经编写和测试过的代码,而不是依赖我们这里的大小检查代码。
我们在构造函数中使用时隙和空列表预先填充我们的地图。因此,当我们开始添加订单时,我们就有了一个数据结构。
addOrder 方法在映射中查找请求的时隙作为键。在地图上执行get 会返回List 的订单供我们检查。
package work.basil.example.orders;
import java.time.*;
import java.util.*;
public class DayManager
{
// Member fields.
final ZoneId zoneId;
final LocalDate workDate;
final LocalTime startTime;
final Duration timeSlice;
final int numberOfTimeSlots, capacityPerTimeSlot;
private final NavigableMap < ZonedDateTime, List < Order > > ordersPerTimeSlot;
// Constructor
public DayManager ( final ZoneId zoneId , final LocalDate localDate , final LocalTime startTime , final Duration timeSlice , final int numberOfTimeSlots , final int capacityPerSlot )
{
this.zoneId = zoneId;
this.workDate = localDate;
this.startTime = startTime;
this.timeSlice = timeSlice;
this.numberOfTimeSlots = numberOfTimeSlots;
this.capacityPerTimeSlot = capacityPerSlot;
this.ordersPerTimeSlot = new TreeMap <>();
this.populateMap();
}
// Subroutine.
private void populateMap ( )
{
ZonedDateTime start = ZonedDateTime.of( this.workDate , this.startTime , this.zoneId );
for ( int i = 0 ; i < this.numberOfTimeSlots ; i++ )
{
Duration d = this.timeSlice.multipliedBy( i );
ZonedDateTime zdt = start.plus( d );
List < Order > list = new ArrayList <>( this.capacityPerTimeSlot );
this.ordersPerTimeSlot.put( zdt , list );
}
System.out.println( "this.ordersPerTimeSlot = " + this.ordersPerTimeSlot );
}
// Business logic.
public Optional < ZonedDateTime > addOrder ( final Order order , final ZonedDateTime zdt )
{
List < Order > orders = this.ordersPerTimeSlot.get( zdt );
if ( Objects.isNull( orders ) ) { return Optional.empty(); }
if ( orders.size() > this.capacityPerTimeSlot )
{
String msg = "ERROR - Capacity per time slot exceeded. ";
System.out.println( msg );
throw new IllegalStateException( msg );
} else if ( orders.size() == this.capacityPerTimeSlot )
{
String msg = "INFO - This time slot filled.";
System.out.println( msg );
throw new IllegalStateException( msg );
} else if ( orders.size() < this.capacityPerTimeSlot )
{
// Room in this time slot to place order.
orders.add( order );
return Optional.of( zdt );
} else
{
String msg = "ERROR - Should never reach this point. Error in IF-THEN logic of adding orders.";
System.out.println( msg );
throw new IllegalStateException( msg );
}
}
// Debugging
public String dumpOrders ( )
{
return this.ordersPerTimeSlot.toString();
}
}
这是一个App 课程,用于练习我们的DayManager 和Order 课程。
package work.basil.example.orders;
import java.time.*;
import java.util.Optional;
import java.util.UUID;
public class App
{
public static void main ( String[] args )
{
ZoneId z = ZoneId.of( "America/Edmonton" );
LocalDate ld = LocalDate.of( 2022 , Month.JANUARY , 23 );
DayManager dm = new DayManager( z , ld , LocalTime.of( 13 , 0 ) , Duration.ofHours( 1 ) , 3 , 3 );
Order order = new Order( UUID.fromString( "2d8e5cc2-26ac-474d-a081-2c71207fd6c5" ) , "Basil" );
Optional < ZonedDateTime > optionalTimeSlot = dm.addOrder( order , ZonedDateTime.of( ld , LocalTime.of( 14 , 0 ) , z ) );
System.out.println( "order = " + order );
System.out.println( "optionalTimeSlot.toString() = " + optionalTimeSlot );
System.out.println( dm.dumpOrders() );
}
}
运行时。
this.ordersPerTimeSlot = {2022-01-23T13:00-07:00[America/Edmonton]=[], 2022-01-23T14:00-07:00[America/Edmonton]=[], 2022-01-23T15:00-07:00[America/Edmonton]=[]}
order = Order[id=2d8e5cc2-26ac-474d-a081-2c71207fd6c5, customerName=Basil]
optionalTimeSlot.toString() = Optional[2022-01-23T14:00-07:00[America/Edmonton]]
{2022-01-23T13:00-07:00[America/Edmonton]=[], 2022-01-23T14:00-07:00[America/Edmonton]=[Order[id=2d8e5cc2-26ac-474d-a081-2c71207fd6c5, customerName=Basil]], 2022-01-23T15:00-07:00[America/Edmonton]=[]}