@@ -1433,7 +1433,8 @@ public static <T> T arraycopy(final T source, final int sourcePos, final T dest,
1433
1433
}
1434
1434
1435
1435
/**
1436
- * Searches element in array sorted by key.
1436
+ * Searches element in array sorted by key. If there are multiple elements matching, it returns first occurrence.
1437
+ * If the array is not sorted, the result is undefined.
1437
1438
*
1438
1439
* @param array
1439
1440
* array sorted by key field
@@ -1445,25 +1446,26 @@ public static <T> T arraycopy(final T source, final int sourcePos, final T dest,
1445
1446
* comparator for keys
1446
1447
*
1447
1448
* @return
1448
- * index of the search key, if it is contained in the array; otherwise, (-first_greater - 1).
1449
- * The first_greater is the index of lowest greater element in the list - if all elements are lower, the
1450
- * first_greater is defined as array.length.
1449
+ * index of the first occurrence of search key, if it is contained in the array; otherwise,
1450
+ * (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements
1451
+ * are lower, the first_greater is defined as array.length.
1451
1452
*
1452
1453
* @param <T>
1453
1454
* type of array element
1454
1455
* @param <K>
1455
1456
* type of key
1456
1457
*/
1457
- public static <K , T > int binarySearch (
1458
+ public static <K , T > int binarySearchFirst (
1458
1459
T [] array ,
1459
1460
K key ,
1460
1461
Function <T , K > keyExtractor , Comparator <? super K > comparator
1461
1462
) {
1462
- return binarySearch0 (array , 0 , array .length , key , keyExtractor , comparator );
1463
+ return binarySearchFirst0 (array , 0 , array .length , key , keyExtractor , comparator );
1463
1464
}
1464
1465
1465
1466
/**
1466
- * Searches element in array sorted by key, within range fromIndex (inclusive) - toIndex (exclusive).
1467
+ * Searches element in array sorted by key, within range fromIndex (inclusive) - toIndex (exclusive). If there are
1468
+ * multiple elements matching, it returns first occurrence. If the array is not sorted, the result is undefined.
1467
1469
*
1468
1470
* @param array
1469
1471
* array sorted by key field
@@ -1479,9 +1481,9 @@ public static <K, T> int binarySearch(
1479
1481
* comparator for keys
1480
1482
*
1481
1483
* @return
1482
- * index of the search key, if it is contained in the array within specified range; otherwise,
1483
- * (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements
1484
- * are lower, the first_greater is defined as toIndex.
1484
+ * index of the first occurrence of search key, if it is contained in the array within specified range;
1485
+ * otherwise, (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if
1486
+ * all elements are lower, the first_greater is defined as toIndex.
1485
1487
*
1486
1488
* @throws ArrayIndexOutOfBoundsException
1487
1489
* when fromIndex or toIndex is out of array range
@@ -1493,28 +1495,124 @@ public static <K, T> int binarySearch(
1493
1495
* @param <K>
1494
1496
* type of key
1495
1497
*/
1496
- public static <T , K > int binarySearch (
1498
+ public static <T , K > int binarySearchFirst (
1497
1499
T [] array ,
1498
1500
int fromIndex , int toIndex ,
1499
1501
K key ,
1500
1502
Function <T , K > keyExtractor , Comparator <? super K > comparator
1501
1503
) {
1502
- if (fromIndex > toIndex ) {
1503
- throw new IllegalArgumentException (
1504
- "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")" );
1505
- }
1506
- if (fromIndex < 0 ) {
1507
- throw new ArrayIndexOutOfBoundsException (fromIndex );
1508
- }
1509
- if (toIndex > array .length ) {
1510
- throw new ArrayIndexOutOfBoundsException (toIndex );
1504
+ checkRange (array .length , fromIndex , toIndex );
1505
+
1506
+ return binarySearchFirst0 (array , fromIndex , toIndex , key , keyExtractor , comparator );
1507
+ }
1508
+
1509
+ // common implementation for binarySearch methods, with same semantics:
1510
+ private static <T , K > int binarySearchFirst0 (
1511
+ T [] array ,
1512
+ int fromIndex , int toIndex ,
1513
+ K key ,
1514
+ Function <T , K > keyExtractor , Comparator <? super K > comparator
1515
+ ) {
1516
+ int l = fromIndex ;
1517
+ int h = toIndex - 1 ;
1518
+
1519
+ while (l <= h ) {
1520
+ final int m = (l + h ) >>> 1 ; // unsigned shift to avoid overflow
1521
+ final K value = keyExtractor .apply (array [m ]);
1522
+ final int c = comparator .compare (value , key );
1523
+ if (c < 0 ) {
1524
+ l = m + 1 ;
1525
+ } else if (c > 0 ) {
1526
+ h = m - 1 ;
1527
+ } else if (l < h ) {
1528
+ // possibly multiple matching items remaining:
1529
+ h = m ;
1530
+ } else {
1531
+ // single matching item remaining:
1532
+ return m ;
1533
+ }
1511
1534
}
1512
1535
1513
- return binarySearch0 (array , fromIndex , toIndex , key , keyExtractor , comparator );
1536
+ // not found, the l points to the lowest higher match:
1537
+ return -l - 1 ;
1538
+ }
1539
+
1540
+ /**
1541
+ * Searches element in array sorted by key. If there are multiple elements matching, it returns last occurrence.
1542
+ * If the array is not sorted, the result is undefined.
1543
+ *
1544
+ * @param array
1545
+ * array sorted by key field
1546
+ * @param key
1547
+ * key to search for
1548
+ * @param keyExtractor
1549
+ * function to extract key from element
1550
+ * @param comparator
1551
+ * comparator for keys
1552
+ *
1553
+ * @return
1554
+ * index of the last occurrence of search key, if it is contained in the array; otherwise,
1555
+ * (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if all elements
1556
+ * are lower, the first_greater is defined as array.length.
1557
+ *
1558
+ * @param <T>
1559
+ * type of array element
1560
+ * @param <K>
1561
+ * type of key
1562
+ */
1563
+ public static <K , T > int binarySearchLast (
1564
+ T [] array ,
1565
+ K key ,
1566
+ Function <T , K > keyExtractor , Comparator <? super K > comparator
1567
+ ) {
1568
+ return binarySearchLast0 (array , 0 , array .length , key , keyExtractor , comparator );
1569
+ }
1570
+
1571
+ /**
1572
+ * Searches element in array sorted by key, within range fromIndex (inclusive) - toIndex (exclusive). If there are
1573
+ * multiple elements matching, it returns last occurrence. If the array is not sorted, the result is undefined.
1574
+ *
1575
+ * @param array
1576
+ * array sorted by key field
1577
+ * @param fromIndex
1578
+ * start index (inclusive)
1579
+ * @param toIndex
1580
+ * end index (exclusive)
1581
+ * @param key
1582
+ * key to search for
1583
+ * @param keyExtractor
1584
+ * function to extract key from element
1585
+ * @param comparator
1586
+ * comparator for keys
1587
+ *
1588
+ * @return
1589
+ * index of the last occurrence of search key, if it is contained in the array within specified range;
1590
+ * otherwise, (-first_greater - 1). The first_greater is the index of lowest greater element in the list - if
1591
+ * all elements are lower, the first_greater is defined as toIndex.
1592
+ *
1593
+ * @throws ArrayIndexOutOfBoundsException
1594
+ * when fromIndex or toIndex is out of array range
1595
+ * @throws IllegalArgumentException
1596
+ * when fromIndex is greater than toIndex
1597
+ *
1598
+ * @param <T>
1599
+ * type of array element
1600
+ * @param <K>
1601
+ * type of key
1602
+ */
1603
+ public static <T , K > int binarySearchLast (
1604
+ T [] array ,
1605
+ int fromIndex , int toIndex ,
1606
+ K key ,
1607
+ Function <T , K > keyExtractor , Comparator <? super K > comparator
1608
+ ) {
1609
+ checkRange (array .length , fromIndex , toIndex );
1610
+
1611
+ return binarySearchLast0 (array , fromIndex , toIndex , key , keyExtractor , comparator );
1514
1612
}
1515
1613
1516
1614
// common implementation for binarySearch methods, with same semantics:
1517
- private static <T , K > int binarySearch0 (
1615
+ private static <T , K > int binarySearchLast0 (
1518
1616
T [] array ,
1519
1617
int fromIndex , int toIndex ,
1520
1618
K key ,
@@ -1531,8 +1629,16 @@ private static <T, K> int binarySearch0(
1531
1629
l = m + 1 ;
1532
1630
} else if (c > 0 ) {
1533
1631
h = m - 1 ;
1632
+ } else if (m + 1 < h ) {
1633
+ // matching, more than two items remaining:
1634
+ l = m ;
1635
+ } else if (m + 1 == h ) {
1636
+ // two items remaining, next loops would result in unchanged l and h, we have to choose m or h:
1637
+ final K valueH = keyExtractor .apply (array [h ]);
1638
+ final int cH = comparator .compare (valueH , key );
1639
+ return cH == 0 ? h : m ;
1534
1640
} else {
1535
- // 0, found
1641
+ // one item remaining, single match:
1536
1642
return m ;
1537
1643
}
1538
1644
}
@@ -9573,4 +9679,18 @@ public static String[] toStringArray(final Object[] array, final String valueFor
9573
9679
public ArrayUtils () {
9574
9680
// empty
9575
9681
}
9682
+
9683
+ static void checkRange (int length , int fromIndex , int toIndex ) {
9684
+ if (fromIndex > toIndex ) {
9685
+ throw new IllegalArgumentException (
9686
+ "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")" );
9687
+ }
9688
+ if (fromIndex < 0 ) {
9689
+ throw new ArrayIndexOutOfBoundsException (fromIndex );
9690
+ }
9691
+ if (toIndex > length ) {
9692
+ throw new ArrayIndexOutOfBoundsException (toIndex );
9693
+ }
9694
+
9695
+ }
9576
9696
}
0 commit comments