diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java index c08e05cc..27574a3f 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java @@ -87,7 +87,7 @@ public class VehicleRoutingProblem { private FleetSize fleetSize = FleetSize.INFINITE; - private Collection vehicleTypes = new ArrayList(); + private Map vehicleTypes = new HashMap<>(); private Collection initialRoutes = new ArrayList(); @@ -395,8 +395,13 @@ public class VehicleRoutingProblem { incVehicleTypeIdIndexCounter(); } uniqueVehicles.add(vehicle); - if (!vehicleTypes.contains(vehicle.getType())) { - vehicleTypes.add(vehicle.getType()); + if (!vehicleTypes.containsKey(vehicle.getType().getTypeId())) { + vehicleTypes.put(vehicle.getType().getTypeId(), vehicle.getType()); + } else { + VehicleType existingType = vehicleTypes.get(vehicle.getType().getTypeId()); + if (!vehicle.getType().equals(existingType)) { + throw new IllegalArgumentException("A type with type id " + vehicle.getType().getTypeId() + " already exists. However, types are different. Please use unique vehicle types only."); + } } String startLocationId = vehicle.getStartLocation().getId(); addLocationToTentativeLocations(vehicle.getStartLocation()); @@ -495,7 +500,7 @@ public class VehicleRoutingProblem { * @return collection of vehicle-types */ public Collection getAddedVehicleTypes() { - return Collections.unmodifiableCollection(vehicleTypes); + return Collections.unmodifiableCollection(vehicleTypes.values()); } /** @@ -587,7 +592,7 @@ public class VehicleRoutingProblem { this.jobs = builder.jobs; this.fleetSize = builder.fleetSize; this.vehicles = builder.uniqueVehicles; - this.vehicleTypes = builder.vehicleTypes; + this.vehicleTypes = builder.vehicleTypes.values(); this.initialVehicleRoutes = builder.initialRoutes; this.transportCosts = builder.transportCosts; this.activityCosts = builder.activityCosts; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java index 112c137c..931d6187 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java @@ -81,6 +81,37 @@ public class VehicleTypeImpl implements VehicleType { public String toString() { return "[fixed=" + fix + "][perTime=" + perTransportTimeUnit + "][perDistance=" + perDistanceUnit + "][perWaitingTimeUnit=" + perWaitingTimeUnit + "]"; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VehicleCostParams)) return false; + + VehicleCostParams that = (VehicleCostParams) o; + + if (Double.compare(that.fix, fix) != 0) return false; + if (Double.compare(that.perTransportTimeUnit, perTransportTimeUnit) != 0) return false; + if (Double.compare(that.perDistanceUnit, perDistanceUnit) != 0) return false; + if (Double.compare(that.perWaitingTimeUnit, perWaitingTimeUnit) != 0) return false; + return Double.compare(that.perServiceTimeUnit, perServiceTimeUnit) == 0; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(fix); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perTransportTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perDistanceUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perWaitingTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perServiceTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } } /** @@ -299,32 +330,30 @@ public class VehicleTypeImpl implements VehicleType { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((typeId == null) ? 0 : typeId.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VehicleTypeImpl)) return false; + + VehicleTypeImpl that = (VehicleTypeImpl) o; + + if (Double.compare(that.maxVelocity, maxVelocity) != 0) return false; + if (!typeId.equals(that.typeId)) return false; + if (profile != null ? !profile.equals(that.profile) : that.profile != null) return false; + if (!vehicleCostParams.equals(that.vehicleCostParams)) return false; + return capacityDimensions.equals(that.capacityDimensions); } - /** - * Two vehicle-types are equal if they have the same vehicleId. - */ @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - VehicleTypeImpl other = (VehicleTypeImpl) obj; - if (typeId == null) { - if (other.typeId != null) - return false; - } else if (!typeId.equals(other.typeId)) - return false; - return true; + public int hashCode() { + int result; + long temp; + result = typeId.hashCode(); + result = 31 * result + (profile != null ? profile.hashCode() : 0); + result = 31 * result + vehicleCostParams.hashCode(); + result = 31 * result + capacityDimensions.hashCode(); + temp = Double.doubleToLongBits(maxVelocity); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; } private final String typeId; diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java index 7e4dbec2..046f1934 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java @@ -316,6 +316,17 @@ public class VehicleRoutingProblemTest { builder.addVehicle(vehicle2); } + @Test(expected = IllegalArgumentException.class) + public void whenAddingVehicleTypesWithSameIdButDifferentCosts_itShouldThrowException() { + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type1 = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleType type2 = VehicleTypeImpl.Builder.newInstance("type").setCostPerServiceTime(2d).build(); + VehicleImpl vehicle1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(Location.newInstance("loc")).setType(type1).build(); + VehicleImpl vehicle2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(Location.newInstance("loc")).setType(type2).build(); + builder.addVehicle(vehicle1); + builder.addVehicle(vehicle2); + } + @Test(expected = IllegalArgumentException.class) public void whenBuildingProblemWithSameBreakId_itShouldThrowException(){ VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java index 0c85b96a..bd145479 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java @@ -124,6 +124,7 @@ public class VehicleTypeImplTest { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").setCostPerDistance(-10).build(); } + @Test public void whenSettingPerDistanceCosts_itShouldBeSetCorrectly() { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").setCostPerDistance(10).build(); assertEquals(10.0, type.getVehicleCostParams().perDistanceUnit, 0.0); @@ -166,4 +167,32 @@ public class VehicleTypeImplTest { assertEquals(42, two.getUserData()); assertNull(three.getUserData()); } + + @Test + public void typesShouldBeEqual() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + assertTrue(one.equals(two)); + } + + @Test + public void typesShouldBeNotEqual() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + assertFalse(one.equals(two)); + } + + @Test + public void typesShouldBeNotEqual2() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 20).build(); + assertFalse(one.equals(two)); + } + + @Test + public void typesShouldBeEqual2() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + assertTrue(one.equals(two)); + } }