Skip to content

Commit ae5b60b

Browse files
committed
[XMLBeans-438] improve performance of XmlComplexContentImpl.arraySetterHelper
git-svn-id: https://svn.apache.org/repos/asf/xmlbeans/trunk@1933220 13f79535-47bb-0310-9956-ffa450edef68
1 parent 9880164 commit ae5b60b

1 file changed

Lines changed: 67 additions & 21 deletions

File tree

src/main/java/org/apache/xmlbeans/impl/values/XmlComplexContentImpl.java

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import javax.xml.namespace.QName;
2323
import java.math.BigDecimal;
2424
import java.math.BigInteger;
25+
import java.util.ArrayList;
2526
import java.util.Calendar;
2627
import java.util.Date;
2728
import java.util.List;
@@ -342,7 +343,14 @@ protected void arraySetterHelper(XmlObject[] sources, QName elemName, QNameSet s
342343
}
343344
}
344345
if (i < sources.length) {
345-
TypeStoreUser current = (set == null) ? store.find_element_user(elemName, 0) : store.find_element_user(set, 0);
346+
// Get all existing elements upfront to avoid O(n^2) from repeated find_element_user calls
347+
List<XmlObject> existingList = new ArrayList<>(m);
348+
if (set == null) {
349+
store.find_all_element_users(elemName, existingList);
350+
} else {
351+
store.find_all_element_users(set, existingList);
352+
}
353+
TypeStoreUser current = existingList.isEmpty() ? null : (TypeStoreUser) existingList.get(0);
346354
if (current == sources[i]) {
347355
// The new object matches what already exists in the array
348356
// Heuristic: we optimize for the case where the new elements
@@ -351,16 +359,26 @@ protected void arraySetterHelper(XmlObject[] sources, QName elemName, QNameSet s
351359

352360
// First insert the new element in the array at position 0
353361
int j;
362+
int originalI = i; // capture for index arithmetic in the inner loop
354363
for (j = 0; j < i; j++) {
355364
TypeStoreUser user = (set == null) ? store.insert_element_user(elemName, j) : store.insert_element_user(set, elemName, j);
356365
((XmlObjectBase) user).set(sources[j]);
357366
}
367+
// Track inserts made inside this loop so we can compute the correct
368+
// index into existingList without calling find_element_user each time.
369+
int insertCount = 0;
358370
for (i++, j++; i < sources.length; i++, j++) {
359371
// Cursor is implicitly closed
360372
XmlCursor c = sources[i].isImmutable() ? null : sources[i].newCursor();
361373
if (c != null && c.toParent() && c.getObject() == this) {
362374
c.close();
363-
current = (set == null) ? store.find_element_user(elemName, j) : store.find_element_user(set, j);
375+
// Each of the originalI pre-inserts shifted existing elements right by
376+
// originalI positions; each subsequent insert in this loop shifts them
377+
// one more. So j (the current store position) maps to
378+
// existingList[j - originalI - insertCount], where originalI accounts
379+
// for pre-loop inserts and insertCount for in-loop inserts so far.
380+
int existingIdx = j - originalI - insertCount;
381+
current = (existingIdx < existingList.size()) ? (TypeStoreUser) existingList.get(existingIdx) : null;
364382
if (current != sources[i]) {
365383
// Fall back to the general case
366384
break;
@@ -372,11 +390,12 @@ protected void arraySetterHelper(XmlObject[] sources, QName elemName, QNameSet s
372390
// Insert before the current element
373391
TypeStoreUser user = (set == null) ? store.insert_element_user(elemName, j) : store.insert_element_user(set, elemName, j);
374392
((XmlObjectBase) user).set(sources[i]);
393+
insertCount++;
375394
}
376395
}
377396
startDest = j;
378397
startSrc = i;
379-
m = store.count_elements(elemName);
398+
m = (set == null) ? store.count_elements(elemName) : store.count_elements(set);
380399
}
381400
// Fall through
382401
} else {
@@ -407,19 +426,28 @@ protected void arraySetterHelper(XmlObject[] sources, QName elemName, QNameSet s
407426
}
408427
}
409428

429+
// Get existing elements upfront to avoid O(n^2) from repeated find_element_user calls.
430+
// The list is built lazily since it may not be needed when all remaining elements are new.
431+
List<XmlObject> finalList = null;
410432
int j;
411433
for (i = startSrc, j = startDest; i < n; i++, j++) {
412-
TypeStoreUser user;
434+
XmlObjectBase user;
413435

414436
if (j >= m) {
415-
user = store.add_element_user(elemName);
416-
} else if (set == null) {
417-
user = store.find_element_user(elemName, j);
437+
user = (XmlObjectBase) store.add_element_user(elemName);
418438
} else {
419-
user = store.find_element_user(set, j);
439+
if (finalList == null) {
440+
finalList = new ArrayList<>(m);
441+
if (set == null) {
442+
store.find_all_element_users(elemName, finalList);
443+
} else {
444+
store.find_all_element_users(set, finalList);
445+
}
446+
}
447+
user = (XmlObjectBase) finalList.get(j);
420448
}
421449

422-
((XmlObjectBase) user).set(sources[i]);
450+
user.set(sources[i]);
423451
}
424452

425453
// We can't just delegate to array_setter because we need
@@ -446,17 +474,26 @@ private void commonSetterHelper(QName elemName, QNameSet set, int n, BiConsumer<
446474
}
447475
}
448476

477+
// Get existing elements upfront to avoid O(n^2) from repeated find_element_user calls
478+
List<XmlObject> existing = new ArrayList<>(m);
479+
if (m > 0) {
480+
if (set == null) {
481+
store.find_all_element_users(elemName, existing);
482+
} else {
483+
store.find_all_element_users(set, existing);
484+
}
485+
}
486+
449487
for (int i = 0; i < n; i++) {
450-
TypeStoreUser user;
488+
XmlObjectBase user;
451489

452490
if (i >= m) {
453-
user = store.add_element_user(elemName);
454-
} else if (set == null) {
455-
user = store.find_element_user(elemName, i);
491+
user = (XmlObjectBase) store.add_element_user(elemName);
456492
} else {
457-
user = store.find_element_user(set, i);
493+
user = (XmlObjectBase) existing.get(i);
458494
}
459-
fun.accept((XmlObjectBase) user, i);
495+
496+
fun.accept(user, i);
460497
}
461498
}
462499

@@ -475,17 +512,26 @@ private <T> void commonSetterHelper2(QName elemName, QNameSet set, T[] sources,
475512
}
476513
}
477514

515+
// Get existing elements upfront to avoid O(n^2) from repeated find_element_user calls
516+
List<XmlObject> existing = new ArrayList<>(m);
517+
if (m > 0) {
518+
if (set == null) {
519+
store.find_all_element_users(elemName, existing);
520+
} else {
521+
store.find_all_element_users(set, existing);
522+
}
523+
}
524+
478525
for (int i = 0; i < n; i++) {
479-
TypeStoreUser user;
526+
XmlObjectBase user;
480527

481528
if (i >= m) {
482-
user = store.add_element_user(elemName);
483-
} else if (set == null) {
484-
user = store.find_element_user(elemName, i);
529+
user = (XmlObjectBase) store.add_element_user(elemName);
485530
} else {
486-
user = store.find_element_user(set, i);
531+
user = (XmlObjectBase) existing.get(i);
487532
}
488-
c.accept((XmlObjectBase) user, sources[i]);
533+
534+
c.accept(user, sources[i]);
489535
}
490536
}
491537
}

0 commit comments

Comments
 (0)