Skip to content

Commit 138422a

Browse files
committed
optimize
1 parent d2576be commit 138422a

File tree

4 files changed

+280
-247
lines changed

4 files changed

+280
-247
lines changed

jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Inserter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route)
7070
if (job instanceof Service) {
7171
route.setVehicleAndDepartureTime(iData.getSelectedVehicle(), iData.getVehicleDepartureTime());
7272
if (!iData.getSelectedVehicle().isReturnToDepot()) {
73-
if (iData.getDeliveryInsertionIndex() >= route.getTourActivities().getActivities().size()) {
73+
if (iData.getDeliveryInsertionIndex() >= route.getTourActivities().size()) {
7474
setEndLocation(route, (Service) job);
7575
}
7676
}

jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,12 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job
128128
boolean tourEnd = false;
129129
//pickupShipmentLoop
130130
List<TourActivity> activities = currentRoute.getTourActivities().getActivities();
131+
int activitiesSize = activities.size();
131132

132133
List<HardConstraint> failedActivityConstraints = new ArrayList<>();
133134
while (!tourEnd) {
134135
TourActivity nextAct;
135-
if (i < activities.size()) {
136+
if (i < activitiesSize) {
136137
nextAct = activities.get(i);
137138
} else {
138139
nextAct = end;
@@ -182,7 +183,7 @@ else if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) {
182183
boolean tourEnd_deliveryLoop = false;
183184
while (!tourEnd_deliveryLoop) {
184185
TourActivity nextAct_deliveryLoop;
185-
if (j < activities.size()) {
186+
if (j < activitiesSize) {
186187
nextAct_deliveryLoop = activities.get(j);
187188
} else {
188189
nextAct_deliveryLoop = end;

jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/Capacity.java

Lines changed: 152 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
*/
2929
public class Capacity {
3030

31+
// Pre-allocate a common instance for performance
32+
private static final Capacity ZERO = new Capacity(new int[1]);
33+
3134
/**
3235
* Adds up two capacities, i.e. sums up each and every capacity dimension, and returns the resulting Capacity.
3336
* <p>
@@ -40,11 +43,20 @@ public class Capacity {
4043
*/
4144
public static Capacity addup(Capacity cap1, Capacity cap2) {
4245
if (cap1 == null || cap2 == null) throw new NullPointerException("arguments must not be null");
43-
Capacity.Builder capacityBuilder = Capacity.Builder.newInstance();
44-
for (int i = 0; i < Math.max(cap1.getNuOfDimensions(), cap2.getNuOfDimensions()); i++) {
45-
capacityBuilder.addDimension(i, cap1.get(i) + cap2.get(i));
46+
47+
// Special case handling for better performance
48+
if (cap1.isZero()) return copyOf(cap2);
49+
if (cap2.isZero()) return copyOf(cap1);
50+
51+
int maxDimension = Math.max(cap1.getNuOfDimensions(), cap2.getNuOfDimensions());
52+
int[] newDimensions = new int[maxDimension];
53+
54+
// Process all dimensions in one loop
55+
for (int i = 0; i < maxDimension; i++) {
56+
newDimensions[i] = cap1.get(i) + cap2.get(i);
4657
}
47-
return capacityBuilder.build();
58+
59+
return new Capacity(newDimensions);
4860
}
4961

5062
/**
@@ -54,16 +66,22 @@ public static Capacity addup(Capacity cap1, Capacity cap2) {
5466
* @param cap2subtract capacity to subtract
5567
* @return new capacity
5668
* @throws NullPointerException if one of the args is null
57-
* @throws IllegalStateException if number of capacityDimensions of cap1 and cap2 are different (i.e. <code>cap1.getNuOfDimension() != cap2.getNuOfDimension()</code>).
5869
*/
5970
public static Capacity subtract(Capacity cap, Capacity cap2subtract) {
6071
if (cap == null || cap2subtract == null) throw new NullPointerException("arguments must not be null");
61-
Capacity.Builder capacityBuilder = Capacity.Builder.newInstance();
62-
for (int i = 0; i < Math.max(cap.getNuOfDimensions(), cap2subtract.getNuOfDimensions()); i++) {
63-
int dimValue = cap.get(i) - cap2subtract.get(i);
64-
capacityBuilder.addDimension(i, dimValue);
72+
73+
// Special case handling for better performance
74+
if (cap2subtract.isZero()) return copyOf(cap);
75+
76+
int maxDimension = Math.max(cap.getNuOfDimensions(), cap2subtract.getNuOfDimensions());
77+
int[] newDimensions = new int[maxDimension];
78+
79+
// Process all dimensions in one loop
80+
for (int i = 0; i < maxDimension; i++) {
81+
newDimensions[i] = cap.get(i) - cap2subtract.get(i);
6582
}
66-
return capacityBuilder.build();
83+
84+
return new Capacity(newDimensions);
6785
}
6886

6987
/**
@@ -75,12 +93,18 @@ public static Capacity subtract(Capacity cap, Capacity cap2subtract) {
7593
*/
7694
public static Capacity invert(Capacity cap2invert) {
7795
if (cap2invert == null) throw new NullPointerException("arguments must not be null");
78-
Capacity.Builder capacityBuilder = Capacity.Builder.newInstance();
96+
97+
// Special case handling for better performance
98+
if (cap2invert.isZero()) return ZERO;
99+
100+
int[] newDimensions = new int[cap2invert.getNuOfDimensions()];
101+
102+
// Process all dimensions in one loop
79103
for (int i = 0; i < cap2invert.getNuOfDimensions(); i++) {
80-
int dimValue = cap2invert.get(i) * -1;
81-
capacityBuilder.addDimension(i, dimValue);
104+
newDimensions[i] = -cap2invert.get(i);
82105
}
83-
return capacityBuilder.build();
106+
107+
return new Capacity(newDimensions);
84108
}
85109

86110
/**
@@ -98,14 +122,19 @@ public static Capacity invert(Capacity cap2invert) {
98122
public static double divide(Capacity numerator, Capacity denominator) {
99123
int nuOfDimensions = 0;
100124
double sumQuotients = 0.0;
101-
for (int index = 0; index < Math.max(numerator.getNuOfDimensions(), denominator.getNuOfDimensions()); index++) {
102-
if (numerator.get(index) != 0 && denominator.get(index) == 0) {
125+
int maxDim = Math.max(numerator.getNuOfDimensions(), denominator.getNuOfDimensions());
126+
127+
for (int index = 0; index < maxDim; index++) {
128+
int num = numerator.get(index);
129+
int denom = denominator.get(index);
130+
131+
if (num != 0 && denom == 0) {
103132
throw new IllegalArgumentException("numerator > 0 and denominator = 0. cannot divide by 0");
104-
} else if (numerator.get(index) == 0 && denominator.get(index) == 0) {
133+
} else if (num == 0 && denom == 0) {
105134
continue;
106135
} else {
107136
nuOfDimensions++;
108-
sumQuotients += (double) numerator.get(index) / (double) denominator.get(index);
137+
sumQuotients += (double) num / (double) denom;
109138
}
110139
}
111140
if (nuOfDimensions > 0) return sumQuotients / (double) nuOfDimensions;
@@ -120,7 +149,11 @@ public static double divide(Capacity numerator, Capacity denominator) {
120149
*/
121150
public static Capacity copyOf(Capacity capacity) {
122151
if (capacity == null) return null;
123-
return new Capacity(capacity);
152+
if (capacity.isZero()) return ZERO;
153+
154+
int[] newDimensions = new int[capacity.getNuOfDimensions()];
155+
System.arraycopy(capacity.dimensions, 0, newDimensions, 0, capacity.getNuOfDimensions());
156+
return new Capacity(newDimensions);
124157
}
125158

126159
/**
@@ -129,11 +162,9 @@ public static Capacity copyOf(Capacity capacity) {
129162
* @author schroeder
130163
*/
131164
public static class Builder {
132-
133-
/**
134-
* default is 1 dimension with size of zero
135-
*/
136-
private int[] dimensions = new int[1];
165+
private static final int DEFAULT_CAPACITY = 10;
166+
private int[] dimensions;
167+
private int maxIndex = -1;
137168

138169
/**
139170
* Returns a new instance of Capacity with one dimension and a value/size of 0
@@ -145,6 +176,18 @@ public static Builder newInstance() {
145176
}
146177

147178
Builder() {
179+
dimensions = new int[DEFAULT_CAPACITY];
180+
}
181+
182+
/**
183+
* Sets initial capacity for more efficient building when dimension count is known
184+
*
185+
* @param capacity the initial capacity
186+
* @return this builder
187+
*/
188+
public Builder withCapacity(int capacity) {
189+
this.dimensions = new int[capacity];
190+
return this;
148191
}
149192

150193
/**
@@ -158,20 +201,25 @@ public static Builder newInstance() {
158201
* @return this builder
159202
*/
160203
public Builder addDimension(int index, int dimValue) {
161-
if (index < dimensions.length) {
162-
dimensions[index] = dimValue;
163-
} else {
164-
int requiredSize = index + 1;
165-
int[] newDimensions = new int[requiredSize];
166-
copy(dimensions, newDimensions);
167-
newDimensions[index] = dimValue;
168-
this.dimensions = newDimensions;
204+
ensureCapacity(index + 1);
205+
dimensions[index] = dimValue;
206+
if (index > maxIndex) {
207+
maxIndex = index;
169208
}
170209
return this;
171210
}
172211

173-
private void copy(int[] from, int[] to) {
174-
System.arraycopy(from, 0, to, 0, dimensions.length);
212+
/**
213+
* Ensures the dimensions array has sufficient capacity
214+
* Grows by factor 1.5x for better amortized performance
215+
*/
216+
private void ensureCapacity(int requiredSize) {
217+
if (requiredSize > dimensions.length) {
218+
int newSize = Math.max(requiredSize, dimensions.length + (dimensions.length >> 1));
219+
int[] newDimensions = new int[newSize];
220+
System.arraycopy(dimensions, 0, newDimensions, 0, dimensions.length);
221+
dimensions = newDimensions;
222+
}
175223
}
176224

177225
/**
@@ -180,28 +228,44 @@ private void copy(int[] from, int[] to) {
180228
* @return Capacity
181229
*/
182230
public Capacity build() {
183-
return new Capacity(this);
184-
}
231+
// Special case for empty or zero-only capacity
232+
boolean isZero = true;
233+
for (int i = 0; i <= maxIndex; i++) {
234+
if (dimensions[i] != 0) {
235+
isZero = false;
236+
break;
237+
}
238+
}
185239

240+
if (isZero && maxIndex < 0) {
241+
return ZERO;
242+
}
186243

244+
// Create right-sized array for the final capacity
245+
int[] rightSizedDimensions = new int[maxIndex + 1];
246+
System.arraycopy(dimensions, 0, rightSizedDimensions, 0, maxIndex + 1);
247+
return new Capacity(rightSizedDimensions);
248+
}
187249
}
188250

189-
private int[] dimensions;
251+
private final int[] dimensions;
252+
private final boolean isZero; // Cache for quick zero checks
190253

191254
/**
192-
* copy constructor
193-
*
194-
* @param capacity capacity to be copied
255+
* Private constructor that takes ownership of the provided array
195256
*/
196-
private Capacity(Capacity capacity) {
197-
this.dimensions = new int[capacity.getNuOfDimensions()];
198-
for (int i = 0; i < capacity.getNuOfDimensions(); i++) {
199-
this.dimensions[i] = capacity.get(i);
200-
}
201-
}
257+
private Capacity(int[] dimensions) {
258+
this.dimensions = dimensions;
202259

203-
private Capacity(Builder builder) {
204-
dimensions = builder.dimensions;
260+
// Precompute if this capacity is all zeros
261+
boolean allZeros = true;
262+
for (int dim : dimensions) {
263+
if (dim != 0) {
264+
allZeros = false;
265+
break;
266+
}
267+
}
268+
this.isZero = allZeros;
205269
}
206270

207271
/**
@@ -213,7 +277,6 @@ public int getNuOfDimensions() {
213277
return dimensions.length;
214278
}
215279

216-
217280
/**
218281
* Returns value of capacity-dimension with specified index.
219282
* <p>
@@ -236,7 +299,10 @@ public int get(int index) {
236299
*/
237300
public boolean isLessOrEqual(Capacity toCompare) {
238301
if (toCompare == null) throw new NullPointerException();
239-
for (int i = 0; i < this.getNuOfDimensions(); i++) {
302+
303+
// We can't use isZero as a fast path since dimensions can be negative
304+
int maxDim = Math.max(this.getNuOfDimensions(), toCompare.getNuOfDimensions());
305+
for (int i = 0; i < maxDim; i++) {
240306
if (this.get(i) > toCompare.get(i)) return false;
241307
}
242308
return true;
@@ -251,12 +317,24 @@ public boolean isLessOrEqual(Capacity toCompare) {
251317
*/
252318
public boolean isGreaterOrEqual(Capacity toCompare) {
253319
if (toCompare == null) throw new NullPointerException();
254-
for (int i = 0; i < Math.max(this.getNuOfDimensions(), toCompare.getNuOfDimensions()); i++) {
320+
321+
// We can't use isZero as a fast path since dimensions can be negative
322+
int maxDim = Math.max(this.getNuOfDimensions(), toCompare.getNuOfDimensions());
323+
for (int i = 0; i < maxDim; i++) {
255324
if (this.get(i) < toCompare.get(i)) return false;
256325
}
257326
return true;
258327
}
259328

329+
/**
330+
* Check if this is a zero capacity (all dimensions are zero)
331+
*
332+
* @return true if all dimensions are zero
333+
*/
334+
public boolean isZero() {
335+
return isZero;
336+
}
337+
260338
@Override
261339
public String toString() {
262340
StringBuilder string = new StringBuilder("[noDimensions=" + getNuOfDimensions() + "]");
@@ -275,20 +353,35 @@ public String toString() {
275353
*/
276354
public static Capacity max(Capacity cap1, Capacity cap2) {
277355
if (cap1 == null || cap2 == null) throw new IllegalArgumentException("arg must not be null");
278-
Capacity.Builder toReturnBuilder = Capacity.Builder.newInstance();
279-
for (int i = 0; i < Math.max(cap1.getNuOfDimensions(), cap2.getNuOfDimensions()); i++) {
280-
toReturnBuilder.addDimension(i, Math.max(cap1.get(i), cap2.get(i)));
356+
357+
int maxDim = Math.max(cap1.getNuOfDimensions(), cap2.getNuOfDimensions());
358+
int[] newDimensions = new int[maxDim];
359+
360+
for (int i = 0; i < maxDim; i++) {
361+
newDimensions[i] = Math.max(cap1.get(i), cap2.get(i));
281362
}
282-
return toReturnBuilder.build();
363+
364+
return new Capacity(newDimensions);
283365
}
284366

367+
/**
368+
* Return the minimum, i.e. the minimum of each capacity dimension.
369+
*
370+
* @param cap1 first capacity to compare
371+
* @param cap2 second capacity to compare
372+
* @return capacity minimum of each capacity dimension
373+
*/
285374
public static Capacity min(Capacity cap1, Capacity cap2) {
286375
if (cap1 == null || cap2 == null) throw new IllegalArgumentException("arg must not be null");
287-
Capacity.Builder toReturnBuilder = Capacity.Builder.newInstance();
288-
for (int i = 0; i < Math.max(cap1.getNuOfDimensions(), cap2.getNuOfDimensions()); i++) {
289-
toReturnBuilder.addDimension(i, Math.min(cap1.get(i), cap2.get(i)));
376+
377+
int maxDim = Math.max(cap1.getNuOfDimensions(), cap2.getNuOfDimensions());
378+
int[] newDimensions = new int[maxDim];
379+
380+
for (int i = 0; i < maxDim; i++) {
381+
newDimensions[i] = Math.min(cap1.get(i), cap2.get(i));
290382
}
291-
return toReturnBuilder.build();
383+
384+
return new Capacity(newDimensions);
292385
}
293386

294387
@Override

0 commit comments

Comments
 (0)