ICU-21349 Enhance Supporting Mixed Unit (such as "inch-and-foot")

See #1363
diff --git a/icu4c/source/i18n/measunit_extra.cpp b/icu4c/source/i18n/measunit_extra.cpp
index 03aefcd..9c6d4bd 100644
--- a/icu4c/source/i18n/measunit_extra.cpp
+++ b/icu4c/source/i18n/measunit_extra.cpp
@@ -774,16 +774,20 @@ bool MeasureUnitImpl::appendSingleUnit(const SingleUnitImpl &singleUnit, UErrorC
     return true;
 }
 
-MaybeStackVector<MeasureUnitImpl> MeasureUnitImpl::extractIndividualUnits(UErrorCode &status) const {
-    MaybeStackVector<MeasureUnitImpl> result;
+MaybeStackVector<MeasureUnitImplWithIndex>
+MeasureUnitImpl::extractIndividualUnitsWithIndices(UErrorCode &status) const {
+    MaybeStackVector<MeasureUnitImplWithIndex> result;
 
     if (this->complexity != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
-        result.emplaceBackAndCheckErrorCode(status, *this, status);
+        result.emplaceBackAndCheckErrorCode(status, 0, new MeasureUnitImpl(*this, status));
         return result;
     }
 
-    for (int32_t i = 0; i < singleUnits.length(); i++) {
-        result.emplaceBackAndCheckErrorCode(status, *singleUnits[i], status);
+    for (int32_t i = 0; i < singleUnits.length(); ++i) {
+        result.emplaceBackAndCheckErrorCode(status, i, new MeasureUnitImpl(*singleUnits[i], status));
+        if (U_FAILURE(status)) {
+            return result;
+        }
     }
 
     return result;
diff --git a/icu4c/source/i18n/measunit_impl.h b/icu4c/source/i18n/measunit_impl.h
index ef027ec..137d0ac 100644
--- a/icu4c/source/i18n/measunit_impl.h
+++ b/icu4c/source/i18n/measunit_impl.h
@@ -14,10 +14,32 @@
 
 U_NAMESPACE_BEGIN
 
+// Export an explicit template instantiation of the LocalPointer that is used as a
+// data member of MeasureUnitImpl.
+// (When building DLLs for Windows this is required.)
+#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
+#if defined(_MSC_VER)
+// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!=
+#pragma warning(push)
+#pragma warning(disable : 4661)
+#endif
+template class U_I18N_API LocalPointerBase<MeasureUnitImpl>;
+template class U_I18N_API LocalPointer<MeasureUnitImpl>;
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+#endif
 
 static const char16_t kDefaultCurrency[] = u"XXX";
 static const char kDefaultCurrency8[] = "XXX";
 
+struct U_I18N_API MeasureUnitImplWithIndex : public UMemory {
+    const int32_t index;
+    LocalPointer<MeasureUnitImpl> unitImpl;
+    // Takes ownership of unitImpl.
+    MeasureUnitImplWithIndex(int32_t index, MeasureUnitImpl *unitImpl)
+        : index(index), unitImpl(unitImpl) {}
+};
 
 /**
  * A struct representing a single unit (optional SI prefix and dimensionality).
@@ -130,9 +152,12 @@ struct U_I18N_API SingleUnitImpl : public UMemory {
 // MaybeStackVector. This is required when building DLLs for Windows. (See
 // datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
-template class U_I18N_API MaybeStackArray<SingleUnitImpl*, 8>;
+template class U_I18N_API MaybeStackArray<SingleUnitImpl *, 8>;
 template class U_I18N_API MemoryPool<SingleUnitImpl, 8>;
 template class U_I18N_API MaybeStackVector<SingleUnitImpl, 8>;
+template class U_I18N_API MaybeStackArray<MeasureUnitImplWithIndex *, 8>;
+template class U_I18N_API MemoryPool<MeasureUnitImplWithIndex, 8>;
+template class U_I18N_API MaybeStackVector<MeasureUnitImplWithIndex, 8>;
 #endif
 
 /**
@@ -149,7 +174,7 @@ class U_I18N_API MeasureUnitImpl : public UMemory {
     MeasureUnitImpl &operator=(MeasureUnitImpl &&other) noexcept = default;
 
     /** Extract the MeasureUnitImpl from a MeasureUnit. */
-    static inline const MeasureUnitImpl* get(const MeasureUnit& measureUnit) {
+    static inline const MeasureUnitImpl *get(const MeasureUnit &measureUnit) {
         return measureUnit.fImpl;
     }
 
@@ -204,14 +229,15 @@ class U_I18N_API MeasureUnitImpl : public UMemory {
     MeasureUnitImpl copy(UErrorCode& status) const;
 
     /**
-     * Extracts the list of all the individual units inside the `MeasureUnitImpl`.
+     * Extracts the list of all the individual units inside the `MeasureUnitImpl` with their indices.
      *      For example:    
      *          -   if the `MeasureUnitImpl` is `foot-per-hour`
-     *                  it will return a list of 1 {`foot-per-hour`} 
+     *                  it will return a list of 1 {(0, `foot-per-hour`)} 
      *          -   if the `MeasureUnitImpl` is `foot-and-inch` 
-     *                  it will return a list of 2 { `foot`, `inch`}
+     *                  it will return a list of 2 {(0, `foot`), (1, `inch`)}
      */
-    MaybeStackVector<MeasureUnitImpl> extractIndividualUnits(UErrorCode &status) const;
+    MaybeStackVector<MeasureUnitImplWithIndex>
+    extractIndividualUnitsWithIndices(UErrorCode &status) const;
 
     /** Mutates this MeasureUnitImpl to take the reciprocal. */
     void takeReciprocal(UErrorCode& status);
diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp
index 1409131..4661c27 100644
--- a/icu4c/source/i18n/number_formatimpl.cpp
+++ b/icu4c/source/i18n/number_formatimpl.cpp
@@ -250,10 +250,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
         fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);
         chain = fUsagePrefsHandler.getAlias();
     } else if (isMixedUnit) {
-        MeasureUnitImpl temp;
-        const MeasureUnitImpl &outputUnit = MeasureUnitImpl::forMeasureUnit(macros.unit, temp, status);
-        auto unitConversionHandler = new UnitConversionHandler(outputUnit.singleUnits[0]->build(status),
-                                                               macros.unit, chain, status);
+        auto unitConversionHandler = new UnitConversionHandler(macros.unit, chain, status);
         fUnitConversionHandler.adoptInsteadAndCheckErrorCode(unitConversionHandler, status);
         chain = fUnitConversionHandler.getAlias();
     }
diff --git a/icu4c/source/i18n/number_longnames.cpp b/icu4c/source/i18n/number_longnames.cpp
index bf89a7a..fc2baa2 100644
--- a/icu4c/source/i18n/number_longnames.cpp
+++ b/icu4c/source/i18n/number_longnames.cpp
@@ -440,9 +440,9 @@ void MixedUnitLongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUn
     fillIn->rules = rules;
     fillIn->parent = parent;
 
-    // We need a localised NumberFormatter for the integers of the bigger units
+    // We need a localised NumberFormatter for the numbers of the bigger units
     // (providing Arabic numerals, for example).
-    fillIn->fIntegerFormatter = NumberFormatter::withLocale(loc);
+    fillIn->fNumberFormatter = NumberFormatter::withLocale(loc);
 }
 
 void MixedUnitLongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
@@ -462,12 +462,6 @@ const Modifier *MixedUnitLongNameHandler::getMixedUnitModifier(DecimalQuantity &
         status = U_UNSUPPORTED_ERROR;
         return &micros.helpers.emptyWeakModifier;
     }
-    // If we don't have at least one mixedMeasure, the LongNameHandler would be
-    // sufficient and we shouldn't be running MixedUnitLongNameHandler code:
-    U_ASSERT(micros.mixedMeasuresCount > 0);
-    // mixedMeasures does not contain the last value:
-    U_ASSERT(fMixedUnitCount == micros.mixedMeasuresCount + 1);
-    U_ASSERT(fListFormatter.isValid());
 
     // Algorithm:
     //
@@ -492,39 +486,41 @@ const Modifier *MixedUnitLongNameHandler::getMixedUnitModifier(DecimalQuantity &
         return &micros.helpers.emptyWeakModifier;
     }
 
+    StandardPlural::Form quantityPlural = StandardPlural::Form::OTHER;
     for (int32_t i = 0; i < micros.mixedMeasuresCount; i++) {
         DecimalQuantity fdec;
-        fdec.setToLong(micros.mixedMeasures[i]);
-        if (i > 0 && fdec.isNegative()) {
-            // If numbers are negative, only the first number needs to have its
-            // negative sign formatted.
-            fdec.negate();
+
+        // If numbers are negative, only the first number needs to have its
+        // negative sign formatted.
+        int64_t number = i > 0 ? std::abs(micros.mixedMeasures[i]) : micros.mixedMeasures[i];
+
+        if (micros.indexOfQuantity == i) { // Insert placeholder for `quantity`
+            // If quantity is not the first value and quantity is negative
+            if (micros.indexOfQuantity > 0 && quantity.isNegative()) {
+                quantity.negate();
+            }
+
+            StandardPlural::Form quantityPlural =
+                utils::getPluralSafe(micros.rounder, rules, quantity, status);
+            UnicodeString quantityFormatWithPlural =
+                getWithPlural(&fMixedUnitData[i * ARRAY_LENGTH], quantityPlural, status);
+            SimpleFormatter quantityFormatter(quantityFormatWithPlural, 0, 1, status);
+            quantityFormatter.format(UnicodeString(u"{0}"), outputMeasuresList[i], status);
+        } else {
+            fdec.setToLong(number);
+            StandardPlural::Form pluralForm = utils::getStandardPlural(rules, fdec);
+            UnicodeString simpleFormat =
+                getWithPlural(&fMixedUnitData[i * ARRAY_LENGTH], pluralForm, status);
+            SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status);
+            UnicodeString num;
+            auto appendable = UnicodeStringAppendable(num);
+
+            fNumberFormatter.formatDecimalQuantity(fdec, status).appendTo(appendable, status);
+            compiledFormatter.format(num, outputMeasuresList[i], status);
         }
-        StandardPlural::Form pluralForm = utils::getStandardPlural(rules, fdec);
-
-        UnicodeString simpleFormat =
-            getWithPlural(&fMixedUnitData[i * ARRAY_LENGTH], pluralForm, status);
-        SimpleFormatter compiledFormatter(simpleFormat, 0, 1, status);
-
-        UnicodeString num;
-        auto appendable = UnicodeStringAppendable(num);
-        fIntegerFormatter.formatDecimalQuantity(fdec, status).appendTo(appendable, status);
-        compiledFormatter.format(num, outputMeasuresList[i], status);
-        // TODO(icu-units#67): fix field positions
     }
 
-    // Reiterated: we have at least one mixedMeasure:
-    U_ASSERT(micros.mixedMeasuresCount > 0);
-    // Thus if negative, a negative has already been formatted:
-    if (quantity.isNegative()) {
-        quantity.negate();
-    }
 
-    UnicodeString *finalSimpleFormats = &fMixedUnitData[(fMixedUnitCount - 1) * ARRAY_LENGTH];
-    StandardPlural::Form finalPlural = utils::getPluralSafe(micros.rounder, rules, quantity, status);
-    UnicodeString finalSimpleFormat = getWithPlural(finalSimpleFormats, finalPlural, status);
-    SimpleFormatter finalFormatter(finalSimpleFormat, 0, 1, status);
-    finalFormatter.format(UnicodeString(u"{0}"), outputMeasuresList[fMixedUnitCount - 1], status);
 
     // Combine list into a "premixed" pattern
     UnicodeString premixedFormatPattern;
@@ -535,10 +531,8 @@ const Modifier *MixedUnitLongNameHandler::getMixedUnitModifier(DecimalQuantity &
         return &micros.helpers.emptyWeakModifier;
     }
 
-    // TODO(icu-units#67): fix field positions
-    // Return a SimpleModifier for the "premixed" pattern
     micros.helpers.mixedUnitModifier =
-        SimpleModifier(premixedCompiled, kUndefinedField, false, {this, SIGNUM_POS_ZERO, finalPlural});
+        SimpleModifier(premixedCompiled, kUndefinedField, false, {this, SIGNUM_POS_ZERO, quantityPlural});
     return &micros.helpers.mixedUnitModifier;
 }
 
diff --git a/icu4c/source/i18n/number_longnames.h b/icu4c/source/i18n/number_longnames.h
index 66eb9a9..9d34c3c 100644
--- a/icu4c/source/i18n/number_longnames.h
+++ b/icu4c/source/i18n/number_longnames.h
@@ -151,21 +151,24 @@ class MixedUnitLongNameHandler : public MicroPropsGenerator, public ModifierStor
   private:
     // Not owned
     const PluralRules *rules;
+
     // Not owned
     const MicroPropsGenerator *parent;
 
     // Total number of units in the MeasureUnit this handler was configured for:
     // for "foot-and-inch", this will be 2.
     int32_t fMixedUnitCount = 1;
+
     // Stores unit data for each of the individual units. For each unit, it
     // stores ARRAY_LENGTH strings, as returned by getMeasureData. (Each unit
     // with index `i` has ARRAY_LENGTH strings starting at index
     // `i*ARRAY_LENGTH` in this array.)
     LocalArray<UnicodeString> fMixedUnitData;
-    // A localized NumberFormatter used to format the integer-valued bigger
-    // units of Mixed Unit measurements.
-    LocalizedNumberFormatter fIntegerFormatter;
-    // A localised list formatter for joining mixed units together.
+
+    // Formats the larger units of Mixed Unit measurements.
+    LocalizedNumberFormatter fNumberFormatter;
+
+    // Joins mixed units together.
     LocalPointer<ListFormatter> fListFormatter;
 
     MixedUnitLongNameHandler(const PluralRules *rules, const MicroPropsGenerator *parent)
diff --git a/icu4c/source/i18n/number_microprops.h b/icu4c/source/i18n/number_microprops.h
index 058c592..98bfa40 100644
--- a/icu4c/source/i18n/number_microprops.h
+++ b/icu4c/source/i18n/number_microprops.h
@@ -36,8 +36,7 @@ class IntMeasures : public MaybeStackArray<int64_t, 2> {
      * Stack Capacity: most mixed units are expected to consist of two or three
      * subunits, so one or two integer measures should be enough.
      */
-    IntMeasures() : MaybeStackArray<int64_t, 2>() {
-    }
+    IntMeasures() : MaybeStackArray<int64_t, 2>() {}
 
     /**
      * Copy constructor.
@@ -122,9 +121,14 @@ struct MicroProps : public MicroPropsGenerator {
     // play.
     MeasureUnit outputUnit;
 
-    // In the case of mixed units, this is the set of integer-only units
-    // *preceding* the final unit.
+    // Contains all the values of each unit in mixed units. For quantity (which is the floating value of
+    // the smallest unit in the mixed unit), the value stores in `quantity`.
+    // NOTE: the value of quantity in `mixedMeasures` will be left unset.
     IntMeasures mixedMeasures;
+
+    // Points to quantity position, -1 if the position is not set yet.
+    int32_t indexOfQuantity = -1;
+
     // Number of mixedMeasures that have been populated
     int32_t mixedMeasuresCount = 0;
 
diff --git a/icu4c/source/i18n/number_usageprefs.cpp b/icu4c/source/i18n/number_usageprefs.cpp
index ea684ba..a0f265d 100644
--- a/icu4c/source/i18n/number_usageprefs.cpp
+++ b/icu4c/source/i18n/number_usageprefs.cpp
@@ -107,37 +107,42 @@ void Usage::set(StringPiece value) {
 // measures.
 void mixedMeasuresToMicros(const MaybeStackVector<Measure> &measures, DecimalQuantity *quantity,
                            MicroProps *micros, UErrorCode status) {
-    micros->mixedMeasuresCount = measures.length() - 1;
-    if (micros->mixedMeasuresCount > 0) {
-#ifdef U_DEBUG
-        U_ASSERT(micros->outputUnit.getComplexity(status) == UMEASURE_UNIT_MIXED);
-        U_ASSERT(U_SUCCESS(status));
-        // Check that we received measurements with the expected MeasureUnits:
-        MeasureUnitImpl temp;
-        const MeasureUnitImpl& impl = MeasureUnitImpl::forMeasureUnit(micros->outputUnit, temp, status);
-        U_ASSERT(U_SUCCESS(status));
-        U_ASSERT(measures.length() == impl.singleUnits.length());
-        for (int32_t i = 0; i < measures.length(); i++) {
-            U_ASSERT(measures[i]->getUnit() == impl.singleUnits[i]->build(status));
+    micros->mixedMeasuresCount = measures.length();
+
+    if (micros->mixedMeasures.getCapacity() < micros->mixedMeasuresCount) {
+        if (micros->mixedMeasures.resize(micros->mixedMeasuresCount) == nullptr) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return;
         }
-        (void)impl;
-#endif
-        // Mixed units: except for the last value, we pass all values to the
-        // LongNameHandler via micros->mixedMeasures.
-        if (micros->mixedMeasures.getCapacity() < micros->mixedMeasuresCount) {
-            if (micros->mixedMeasures.resize(micros->mixedMeasuresCount) == nullptr) {
-                status = U_MEMORY_ALLOCATION_ERROR;
-                return;
-            }
-        }
-        for (int32_t i = 0; i < micros->mixedMeasuresCount; i++) {
-            micros->mixedMeasures[i] = measures[i]->getNumber().getInt64();
-        }
-    } else {
-        micros->mixedMeasuresCount = 0;
     }
-    // The last value (potentially the only value) gets passed on via quantity.
-    quantity->setToDouble(measures[measures.length() - 1]->getNumber().getDouble());
+
+    for (int32_t i = 0; i < micros->mixedMeasuresCount; i++) {
+        switch (measures[i]->getNumber().getType()) {
+        case Formattable::kInt64:
+            micros->mixedMeasures[i] = measures[i]->getNumber().getInt64();
+            break;
+
+        case Formattable::kDouble:
+            U_ASSERT(micros->indexOfQuantity < 0);
+            quantity->setToDouble(measures[i]->getNumber().getDouble());
+            micros->indexOfQuantity = i;
+            break;
+
+        default:
+            U_ASSERT(0 == "Found a Measure Number which is neither a double nor an int");
+            UPRV_UNREACHABLE;
+            break;
+        }
+
+        if (U_FAILURE(status)) {
+            return;
+        }
+    }
+
+    if (micros->indexOfQuantity < 0) {
+        // There is no quantity.
+        status = U_INTERNAL_PROGRAM_ERROR;
+    }
 }
 
 UsagePrefsHandler::UsagePrefsHandler(const Locale &locale,
@@ -170,22 +175,20 @@ void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps &m
     mixedMeasuresToMicros(routedMeasures, &quantity, &micros, status);
 }
 
-UnitConversionHandler::UnitConversionHandler(const MeasureUnit &inputUnit, const MeasureUnit &outputUnit,
+UnitConversionHandler::UnitConversionHandler(const MeasureUnit &targetUnit,
                                              const MicroPropsGenerator *parent, UErrorCode &status)
-    : fOutputUnit(outputUnit), fParent(parent) {
+    : fOutputUnit(targetUnit), fParent(parent) {
     MeasureUnitImpl tempInput, tempOutput;
-    const MeasureUnitImpl &inputUnitImpl = MeasureUnitImpl::forMeasureUnit(inputUnit, tempInput, status);
-    const MeasureUnitImpl &outputUnitImpl =
-        MeasureUnitImpl::forMeasureUnit(outputUnit, tempOutput, status);
 
-    // TODO: this should become an initOnce thing? Review with other
-    // ConversionRates usages.
     ConversionRates conversionRates(status);
     if (U_FAILURE(status)) {
         return;
     }
+
+    const MeasureUnitImpl &targetUnitImpl =
+        MeasureUnitImpl::forMeasureUnit(targetUnit, tempOutput, status);
     fUnitConverter.adoptInsteadAndCheckErrorCode(
-        new ComplexUnitsConverter(inputUnitImpl, outputUnitImpl, conversionRates, status), status);
+        new ComplexUnitsConverter(targetUnitImpl, conversionRates, status), status);
 }
 
 void UnitConversionHandler::processQuantity(DecimalQuantity &quantity, MicroProps &micros,
diff --git a/icu4c/source/i18n/number_usageprefs.h b/icu4c/source/i18n/number_usageprefs.h
index 9e8bd93..7054722 100644
--- a/icu4c/source/i18n/number_usageprefs.h
+++ b/icu4c/source/i18n/number_usageprefs.h
@@ -97,14 +97,15 @@ class U_I18N_API UnitConversionHandler : public MicroPropsGenerator, public UMem
     /**
      * Constructor.
      *
-     * @param inputUnit Specifies the input MeasureUnit. Mixed units are not
-     *     supported as input (because input is just a single decimal quantity).
-     * @param outputUnit Specifies the output MeasureUnit.
+     * @param targetUnit Specifies the output MeasureUnit. The input MeasureUnit
+     *     is derived from it: in case of a mixed unit, the biggest unit is
+     *     taken as the input unit. If not a mixed unit, the input unit will be
+     *     the same as the output unit and no unit conversion takes place.
      * @param parent The parent MicroPropsGenerator.
      * @param status Receives status.
      */
-    UnitConversionHandler(const MeasureUnit &inputUnit, const MeasureUnit &outputUnit,
-                          const MicroPropsGenerator *parent, UErrorCode &status);
+    UnitConversionHandler(const MeasureUnit &targetUnit, const MicroPropsGenerator *parent,
+                          UErrorCode &status);
 
     /**
      * Obtains the appropriate output values from the Unit Converter.
diff --git a/icu4c/source/i18n/units_complexconverter.cpp b/icu4c/source/i18n/units_complexconverter.cpp
index 7abe89f..7e9c713 100644
--- a/icu4c/source/i18n/units_complexconverter.cpp
+++ b/icu4c/source/i18n/units_complexconverter.cpp
@@ -21,34 +21,58 @@
 
 U_NAMESPACE_BEGIN
 namespace units {
+ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &targetUnit,
+                                             const ConversionRates &ratesInfo, UErrorCode &status)
+    : units_(targetUnit.extractIndividualUnitsWithIndices(status)) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    U_ASSERT(units_.length() != 0);
+
+    // Just borrowing a pointer to the instance
+    MeasureUnitImpl *biggestUnit = units_[0]->unitImpl.getAlias();
+    for (int32_t i = 1; i < units_.length(); i++) {
+        if (UnitConverter::compareTwoUnits(*units_[i]->unitImpl, *biggestUnit, ratesInfo, status) > 0 &&
+            U_SUCCESS(status)) {
+            biggestUnit = units_[i]->unitImpl.getAlias();
+        }
+
+        if (U_FAILURE(status)) {
+            return;
+        }
+    }
+    this->init(*biggestUnit, ratesInfo, status);
+}
 
 ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &inputUnit,
                                              const MeasureUnitImpl &outputUnits,
                                              const ConversionRates &ratesInfo, UErrorCode &status)
-    : units_(outputUnits.extractIndividualUnits(status)) {
+    : units_(outputUnits.extractIndividualUnitsWithIndices(status)) {
     if (U_FAILURE(status)) {
         return;
     }
 
     U_ASSERT(units_.length() != 0);
 
-    // Save the desired order of output units before we sort units_
-    for (int32_t i = 0; i < units_.length(); i++) {
-        outputUnits_.emplaceBackAndCheckErrorCode(status, units_[i]->copy(status).build(status));
-    }
+    this->init(inputUnit, ratesInfo, status);
+}
 
+void ComplexUnitsConverter::init(const MeasureUnitImpl &inputUnit,
+                                 const ConversionRates &ratesInfo,
+                                 UErrorCode &status) {
     // Sorts units in descending order. Therefore, we return -1 if
     // the left is bigger than right and so on.
     auto descendingCompareUnits = [](const void *context, const void *left, const void *right) {
         UErrorCode status = U_ZERO_ERROR;
 
-        const auto *leftPointer = static_cast<const MeasureUnitImpl *const *>(left);
-        const auto *rightPointer = static_cast<const MeasureUnitImpl *const *>(right);
+        const auto *leftPointer = static_cast<const MeasureUnitImplWithIndex *const *>(left);
+        const auto *rightPointer = static_cast<const MeasureUnitImplWithIndex *const *>(right);
 
-        return -1 * UnitConverter::compareTwoUnits(**leftPointer,                                  //
-                                                   **rightPointer,                                 //
-                                                   *static_cast<const ConversionRates *>(context), //
-                                                   status);
+        // Multiply by -1 to sort in descending order
+        return (-1) * UnitConverter::compareTwoUnits(*((**leftPointer).unitImpl) /* left unit*/,     //
+                                                     *((**rightPointer).unitImpl) /* right unit */,  //
+                                                     *static_cast<const ConversionRates *>(context), //
+                                                     status);
     };
 
     uprv_sortArray(units_.getAlias(),                                                                  //
@@ -76,11 +100,11 @@ ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &inputUnit,
     //              3. then, the final result will be (6 feet and 6.74016 inches)
     for (int i = 0, n = units_.length(); i < n; i++) {
         if (i == 0) { // first element
-            unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, *units_[i], ratesInfo,
-                                                         status);
+            unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, *(units_[i]->unitImpl),
+                                                         ratesInfo, status);
         } else {
-            unitConverters_.emplaceBackAndCheckErrorCode(status, *units_[i - 1], *units_[i], ratesInfo,
-                                                         status);
+            unitConverters_.emplaceBackAndCheckErrorCode(status, *(units_[i - 1]->unitImpl),
+                                                         *(units_[i]->unitImpl), ratesInfo, status);
         }
 
         if (U_FAILURE(status)) {
@@ -100,7 +124,7 @@ UBool ComplexUnitsConverter::greaterThanOrEqual(double quantity, double limit) c
 MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity,
                                                          icu::number::impl::RoundingImpl *rounder,
                                                          UErrorCode &status) const {
-    // TODO(hugovdm): return an error for "foot-and-foot"?
+    // TODO: return an error for "foot-and-foot"?
     MaybeStackVector<Measure> result;
     int sign = 1;
     if (quantity < 0) {
@@ -110,7 +134,7 @@ MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity,
 
     // For N converters:
     // - the first converter converts from the input unit to the largest unit,
-    // - N-1 converters convert to bigger units for which we want integers,
+    // - the following N-2 converters convert to bigger units for which we want integers,
     // - the Nth converter (index N-1) converts to the smallest unit, for which
     //   we keep a double.
     MaybeStackArray<int64_t, 5> intValues(unitConverters_.length() - 1, status);
@@ -137,102 +161,85 @@ MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity,
             } else {
                 quantity = remainder;
             }
-        } else { // LAST ELEMENT
-            if (rounder == nullptr) {
-                // Nothing to do for the last element.
-                break;
-            }
-
-            // Round the last value
-            // TODO(ICU-21288): get smarter about precision for mixed units.
-            number::impl::DecimalQuantity quant;
-            quant.setToDouble(quantity);
-            rounder->apply(quant, status);
-            if (U_FAILURE(status)) {
-                return result;
-            }
-            quantity = quant.toDouble();
-            if (i == 0) {
-                // Last element is also the first element, so we're done
-                break;
-            }
-
-            // Check if there's a carry, and bubble it back up the resulting intValues.
-            int64_t carry = floor(unitConverters_[i]->convertInverse(quantity) * (1 + DBL_EPSILON));
-            if (carry <= 0) {
-                break;
-            }
-            quantity -= unitConverters_[i]->convert(carry);
-            intValues[i - 1] += carry;
-
-            // We don't use the first converter: that one is for the input unit
-            for (int32_t j = i - 1; j > 0; j--) {
-                carry = floor(unitConverters_[j]->convertInverse(intValues[j]) * (1 + DBL_EPSILON));
-                if (carry <= 0) {
-                    break;
-                }
-                intValues[j] -= round(unitConverters_[j]->convert(carry));
-                intValues[j - 1] += carry;
-            }
-        }
+        }   
     }
 
-    // Package values into Measure instances in result:
+    applyRounder(intValues, quantity, rounder, status);
+
+    // Initialize empty result. We use a MaybeStackArray directly so we can
+    // assign pointers - for this privilege we have to take care of cleanup.
+    MaybeStackArray<Measure *, 4> tmpResult(unitConverters_.length(), status);
+    if (U_FAILURE(status)) {
+        return result;
+    }
+
+    // Package values into temporary Measure instances in tmpResult:
     for (int i = 0, n = unitConverters_.length(); i < n; ++i) {
         if (i < n - 1) {
             Formattable formattableQuantity(intValues[i] * sign);
             // Measure takes ownership of the MeasureUnit*
-            MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status));
-            if (result.emplaceBackAndCheckErrorCode(status, formattableQuantity, type, status) ==
-                nullptr) {
-                // Ownership wasn't taken
-                U_ASSERT(U_FAILURE(status));
-                delete type;
-            }
-            if (U_FAILURE(status)) {
-                return result;
-            }
+            MeasureUnit *type = new MeasureUnit(units_[i]->unitImpl->copy(status).build(status));
+            tmpResult[units_[i]->index] = new Measure(formattableQuantity, type, status);
         } else { // LAST ELEMENT
-            // Add the last element, not an integer:
             Formattable formattableQuantity(quantity * sign);
             // Measure takes ownership of the MeasureUnit*
-            MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status));
-            if (result.emplaceBackAndCheckErrorCode(status, formattableQuantity, type, status) ==
-                nullptr) {
-                // Ownership wasn't taken
-                U_ASSERT(U_FAILURE(status));
-                delete type;
-            }
-            if (U_FAILURE(status)) {
-                return result;
-            }
-            U_ASSERT(result.length() == i + 1);
-            U_ASSERT(result[i] != nullptr);
+            MeasureUnit *type = new MeasureUnit(units_[i]->unitImpl->copy(status).build(status));
+            tmpResult[units_[i]->index] = new Measure(formattableQuantity, type, status);
         }
     }
 
-    MaybeStackVector<Measure> orderedResult;
-    int32_t unitsCount = outputUnits_.length();
-    U_ASSERT(unitsCount == units_.length());
-    Measure **arr = result.getAlias();
-    // O(N^2) is fine: mixed units' unitsCount is usually 2 or 3.
-    for (int32_t i = 0; i < unitsCount; i++) {
-        for (int32_t j = i; j < unitsCount; j++) {
-            // Find the next expected unit, and swap it into place.
-            U_ASSERT(result[j] != nullptr);
-            if (result[j]->getUnit() == *outputUnits_[i]) {
-                if (j != i) {
-                    Measure *tmp = arr[j];
-                    arr[j] = arr[i];
-                    arr[i] = tmp;
-                }
-            }
-        }
+
+    // Transfer values into result and return:
+    for(int32_t i = 0, n = unitConverters_.length(); i < n; ++i) {
+        U_ASSERT(tmpResult[i] != nullptr);
+        result.emplaceBackAndCheckErrorCode(status, *tmpResult[i]);
+        delete tmpResult[i];
     }
 
     return result;
 }
 
+void ComplexUnitsConverter::applyRounder(MaybeStackArray<int64_t, 5> &intValues, double &quantity,
+                                         icu::number::impl::RoundingImpl *rounder,
+                                         UErrorCode &status) const {
+    if (rounder == nullptr) {
+        // Nothing to do for the quantity.
+        return;
+    }
+
+    number::impl::DecimalQuantity decimalQuantity;
+    decimalQuantity.setToDouble(quantity);
+    rounder->apply(decimalQuantity, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+    quantity = decimalQuantity.toDouble();
+
+    int32_t lastIndex = unitConverters_.length() - 1;
+    if (lastIndex == 0) {
+        // Only one element, no need to bubble up the carry
+        return;
+    }
+
+    // Check if there's a carry, and bubble it back up the resulting intValues.
+    int64_t carry = floor(unitConverters_[lastIndex]->convertInverse(quantity) * (1 + DBL_EPSILON));
+    if (carry <= 0) {
+        return;
+    }
+    quantity -= unitConverters_[lastIndex]->convert(carry);
+    intValues[lastIndex - 1] += carry;
+
+    // We don't use the first converter: that one is for the input unit
+    for (int32_t j = lastIndex - 1; j > 0; j--) {
+        carry = floor(unitConverters_[j]->convertInverse(intValues[j]) * (1 + DBL_EPSILON));
+        if (carry <= 0) {
+            return;
+        }
+        intValues[j] -= round(unitConverters_[j]->convert(carry));
+        intValues[j - 1] += carry;
+    }
+}
+
 } // namespace units
 U_NAMESPACE_END
 
diff --git a/icu4c/source/i18n/units_complexconverter.h b/icu4c/source/i18n/units_complexconverter.h
index 83c5b94..a727ea9 100644
--- a/icu4c/source/i18n/units_complexconverter.h
+++ b/icu4c/source/i18n/units_complexconverter.h
@@ -49,6 +49,22 @@ namespace units {
 class U_I18N_API ComplexUnitsConverter : public UMemory {
   public:
     /**
+     * Constructs `ComplexUnitsConverter` for an `targetUnit` that could be Single, Compound or Mixed.
+     * In case of:
+     * 1- Single and Compound units,
+     * the conversion will not perform anything, the input will be equal to the output.
+     * 2- Mixed Unit
+     * the conversion will consider the input is the biggest unit. And will convert it to be spread
+     * through the target units. For example: if target unit is "inch-and-foot", and the input is 2.5. The
+     * converter will consider the input value in "foot", because foot is the biggest unit. Then, it
+     * will convert 2.5 feet to "inch-and-foot".
+     *
+     * @param targetUnit could be any type. (single, compound or mixed).
+     * @param status
+     */
+    ComplexUnitsConverter(const MeasureUnitImpl &targetUnit, const ConversionRates &ratesInfo,
+                          UErrorCode &status);
+    /**
      * Constructor of `ComplexUnitsConverter`.
      * NOTE:
      *   - inputUnit and outputUnits must be under the same category
@@ -79,10 +95,20 @@ class U_I18N_API ComplexUnitsConverter : public UMemory {
 
   private:
     MaybeStackVector<UnitConverter> unitConverters_;
-    // Individual units of mixed units, sorted big to small
-    MaybeStackVector<MeasureUnitImpl> units_;
-    // Individual units of mixed units, sorted in desired output order
-    MaybeStackVector<MeasureUnit> outputUnits_;
+
+    // Individual units of mixed units, sorted big to small, with indices
+    // indicating the requested output mixed unit order.
+    MaybeStackVector<MeasureUnitImplWithIndex> units_;
+
+    // Sorts units_, which must be populated before calling this, and populates
+    // unitConverters_.
+    void init(const MeasureUnitImpl &inputUnit, const ConversionRates &ratesInfo, UErrorCode &status);
+
+    // Applies the rounder to the quantity (last element) and bubble up any carried value to all the
+    // intValues.
+    // TODO(ICU-21288): get smarter about precision for mixed units.
+    void applyRounder(MaybeStackArray<int64_t, 5> &intValues, double &quantity,
+                      icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const;
 };
 
 } // namespace units
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index a57cf3d..ccc3360 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -769,6 +769,67 @@ void NumberFormatterApiTest::unitMeasure() {
             4.28571,
             u"4 metric tons, 285 kilograms, 710 grams");
 
+    assertFormatSingle(u"Mixed Unit (Not Sorted) [metric]",                               //
+                       u"unit/gram-and-kilogram unit-width-full-name",                    //
+                       u"unit/gram-and-kilogram unit-width-full-name",                    //
+                       NumberFormatter::with()                                            //
+                           .unit(MeasureUnit::forIdentifier("gram-and-kilogram", status)) //
+                           .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),                         //
+                       Locale("en-US"),                                                   //
+                       4.28571,                                                           //
+                       u"285.71 grams, 4 kilograms");                                     //
+
+    assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial]",                                  //
+                       u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
+                       u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
+                       NumberFormatter::with()                                                 //
+                           .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
+                           .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),                              //
+                       Locale("en-US"),                                                        //
+                       4.28571,                                                                //
+                       u"10.28556 inches, 4 yards, 0 feet");                                   //
+
+    assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full]",                             //
+                       u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
+                       u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
+                       NumberFormatter::with()                                                 //
+                           .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
+                           .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),                              //
+                       Locale("en-US"),                                                        //
+                       4.38571,                                                                //
+                       u"1.88556 inches, 4 yards, 1 foot");                                    //
+
+    assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full integers]",                    //
+                       u"unit/inch-and-yard-and-foot @# unit-width-full-name",                 //
+                       u"unit/inch-and-yard-and-foot @# unit-width-full-name",                 //
+                       NumberFormatter::with()                                                 //
+                           .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
+                           .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)                               //
+                           .precision(Precision::maxSignificantDigits(2)),                     //
+                       Locale("en-US"),                                                        //
+                       4.36112,                                                                //
+                       u"1 inch, 4 yards, 1 foot");                                            //
+
+    assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full] with `And` in the end",       //
+                       u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
+                       u"unit/inch-and-yard-and-foot unit-width-full-name",                    //
+                       NumberFormatter::with()                                                 //
+                           .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
+                           .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),                              //
+                       Locale("fr-FR"),                                                        //
+                       4.38571,                                                                //
+                       u"1,88556\u00A0pouce, 4\u00A0yards et 1\u00A0pied");                    //
+
+    assertFormatSingle(u"Mixed unit, Scientific [Not in Order]",                               //
+                       u"unit/foot-and-inch-and-yard E0",                                      //
+                       u"unit/foot-and-inch-and-yard E0",                                      //
+                       NumberFormatter::with()                                                 //
+                           .unit(MeasureUnit::forIdentifier("foot-and-inch-and-yard", status)) //
+                           .notation(Notation::scientific()),                                  //
+                       Locale("en-US"),                                                        //
+                       3.65,                                                                   //
+                       "1 ft, 1.14E1 in, 3 yd");                                               //
+
     assertFormatSingle(
             u"Testing  \"1 foot 12 inches\"",
             u"unit/foot-and-inch @### unit-width-full-name",
diff --git a/icu4c/source/test/intltest/units_test.cpp b/icu4c/source/test/intltest/units_test.cpp
index fb67503..a72fff3 100644
--- a/icu4c/source/test/intltest/units_test.cpp
+++ b/icu4c/source/test/intltest/units_test.cpp
@@ -549,10 +549,65 @@ void UnitsTest::testComplexUnitsConverter() {
 
 void UnitsTest::testComplexUnitConverterSorting() {
     IcuTestErrorCode status(*this, "UnitsTest::testComplexUnitConverterSorting");
+    ConversionRates conversionRates(status);
+
+    status.assertSuccess();
+
+    struct TestCase {
+        const char *msg;
+        const char *input;
+        const char *output;
+        double inputValue;
+        Measure expected[3];
+        int32_t expectedCount;
+        // For mixed units, accuracy of the smallest unit
+        double accuracy;
+    } testCases[]{{"inch-and-foot",
+                   "meter",
+                   "inch-and-foot",
+                   10.0,
+                   {
+                       Measure(9.70079, MeasureUnit::createInch(status), status),
+                       Measure(32, MeasureUnit::createFoot(status), status),
+                       Measure(0, MeasureUnit::createBit(status), status),
+                   },
+                   2,
+                   0.00001},
+                  {"inch-and-yard-and-foot",
+                   "meter",
+                   "inch-and-yard-and-foot",
+                   100.0,
+                   {
+                       Measure(1.0079, MeasureUnit::createInch(status), status),
+                       Measure(109, MeasureUnit::createYard(status), status),
+                       Measure(1, MeasureUnit::createFoot(status), status),
+                   },
+                   3,
+                   0.0001}};
+
+    for (const auto &testCase : testCases) {
+        MeasureUnitImpl inputImpl = MeasureUnitImpl::forIdentifier(testCase.input, status);
+        MeasureUnitImpl outputImpl = MeasureUnitImpl::forIdentifier(testCase.output, status);
+        ComplexUnitsConverter converter(inputImpl, outputImpl, conversionRates, status);
+
+        auto actual = converter.convert(testCase.inputValue, nullptr, status);
+
+        for (int i = 0; i < testCase.expectedCount; i++) {
+            assertEquals(testCase.msg, testCase.expected[i].getUnit().getIdentifier(),
+                         actual[i]->getUnit().getIdentifier());
+
+            if (testCase.expected[i].getNumber().getType() == Formattable::Type::kInt64) {
+                assertEquals(testCase.msg, testCase.expected[i].getNumber().getInt64(),
+                             actual[i]->getNumber().getInt64());
+            } else {
+                assertEqualsNear(testCase.msg, testCase.expected[i].getNumber().getDouble(),
+                                 actual[i]->getNumber().getDouble(), testCase.accuracy);
+            }
+        }
+    }
 
     MeasureUnitImpl source = MeasureUnitImpl::forIdentifier("meter", status);
     MeasureUnitImpl target = MeasureUnitImpl::forIdentifier("inch-and-foot", status);
-    ConversionRates conversionRates(status);
 
     ComplexUnitsConverter complexConverter(source, target, conversionRates, status);
     auto measures = complexConverter.convert(10.0, nullptr, status);
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MicroProps.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MicroProps.java
index a75ac6b..dd16a58 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MicroProps.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MicroProps.java
@@ -53,10 +53,16 @@ public class MicroProps implements Cloneable, MicroPropsGenerator {
     // play.
     public MeasureUnit outputUnit;
 
-    // In the case of mixed units, this is the set of integer-only units
-    // *preceding* the final unit.
+    /**
+     * Contains all the measures.
+     */
     public List<Measure> mixedMeasures;
 
+    /**
+     * Points to quantity position, -1 if the position is not set yet.
+     */
+    public int indexOfQuantity = -1;
+
     private volatile boolean exhausted;
 
     /**
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MixedUnitLongNameHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MixedUnitLongNameHandler.java
index d1b7965..8487524 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MixedUnitLongNameHandler.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MixedUnitLongNameHandler.java
@@ -168,14 +168,33 @@ private Modifier getMixedUnitModifier(DecimalQuantity quantity, MicroProps micro
 
         List<String> outputMeasuresList = new ArrayList<>();
 
+        StandardPlural quantityPlural = StandardPlural.OTHER;
         for (int i = 0; i < micros.mixedMeasures.size(); i++) {
+
+            if ( i  == micros.indexOfQuantity) {
+                if (i > 0 && quantity.isNegative()) {
+                    // If numbers are negative, only the first number needs to have its
+                    // negative sign formatted.
+                    quantity.negate();
+                }
+
+                quantityPlural = RoundingUtils.getPluralSafe(micros.rounder, rules, quantity);
+                String quantitySimpleFormat = LongNameHandler.getWithPlural(this.fMixedUnitData.get(i), quantityPlural);
+                SimpleFormatter finalFormatter = SimpleFormatter.compileMinMaxArguments(quantitySimpleFormat, 0, 1);
+                outputMeasuresList.add(finalFormatter.format("{0}"));
+
+                continue;
+            }
+
+
             DecimalQuantity fdec = new DecimalQuantity_DualStorageBCD(micros.mixedMeasures.get(i).getNumber());
             if (i > 0 && fdec.isNegative()) {
                 // If numbers are negative, only the first number needs to have its
                 // negative sign formatted.
                 fdec.negate();
             }
-            StandardPlural pluralForm = fdec.getStandardPlural(rules);
+
+            StandardPlural pluralForm = RoundingUtils.getPluralSafe(micros.rounder, rules, fdec);
 
             String simpleFormat = LongNameHandler.getWithPlural(this.fMixedUnitData.get(i), pluralForm);
             SimpleFormatter compiledFormatter = SimpleFormatter.compileMinMaxArguments(simpleFormat, 0, 1);
@@ -186,18 +205,6 @@ private Modifier getMixedUnitModifier(DecimalQuantity quantity, MicroProps micro
             // TODO(icu-units#67): fix field positions
         }
 
-        // Reiterated: we have at least one mixedMeasure:
-        assert micros.mixedMeasures.size() > 0;
-        // Thus if negative, a negative has already been formatted:
-        if (quantity.isNegative()) {
-            quantity.negate();
-        }
-
-        String[] finalSimpleFormats = this.fMixedUnitData.get(this.fMixedUnitData.size() - 1);
-        StandardPlural finalPlural = RoundingUtils.getPluralSafe(micros.rounder, rules, quantity);
-        String finalSimpleFormat = LongNameHandler.getWithPlural(finalSimpleFormats, finalPlural);
-        SimpleFormatter finalFormatter = SimpleFormatter.compileMinMaxArguments(finalSimpleFormat, 0, 1);
-        outputMeasuresList.add(finalFormatter.format("{0}"));
 
         // Combine list into a "premixed" pattern
         String premixedFormatPattern = this.fListFormatter.format(outputMeasuresList);
@@ -209,7 +216,7 @@ private Modifier getMixedUnitModifier(DecimalQuantity quantity, MicroProps micro
         Modifier.Parameters params = new Modifier.Parameters();
         params.obj = this;
         params.signum = Modifier.Signum.POS_ZERO;
-        params.plural = finalPlural;
+        params.plural = quantityPlural;
         // Return a SimpleModifier for the "premixed" pattern
         return new SimpleModifier(premixedCompiled, null, false, params);
     }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/UnitConversionHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/UnitConversionHandler.java
index 424a58a..84a77ad 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/UnitConversionHandler.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/UnitConversionHandler.java
@@ -5,8 +5,8 @@
 import java.util.List;
 
 import com.ibm.icu.impl.units.ComplexUnitsConverter;
+import com.ibm.icu.impl.units.ConversionRates;
 import com.ibm.icu.impl.units.MeasureUnitImpl;
-import com.ibm.icu.impl.units.UnitsData;
 import com.ibm.icu.util.Measure;
 import com.ibm.icu.util.MeasureUnit;
 
@@ -22,22 +22,17 @@ public class UnitConversionHandler implements MicroPropsGenerator {
     private ComplexUnitsConverter fComplexUnitConverter;
 
     /**
-     * Constructor.
-     *
-     * @param inputUnit Specifies the input MeasureUnit. Mixed units are not
-     *     supported as input (because input is just a single decimal quantity).
-     * @param outputUnit Specifies the output MeasureUnit.
-     * @param parent The parent MicroPropsGenerator.
+     * @param targetUnit Specifies the output MeasureUnit. The input MeasureUnit
+     *     is derived from it: in case of a mixed unit, the biggest unit is
+     *     taken as the input unit. If not a mixed unit, the input unit will be
+     *     the same as the output unit and no unit conversion takes place.
+     * @param parent    The parent MicroPropsGenerator.
      */
-    public UnitConversionHandler(MeasureUnit inputUnit,
-                                 MeasureUnit outputUnit,
-                                 MicroPropsGenerator parent) {
-        this.fOutputUnit = outputUnit;
+    public UnitConversionHandler(MeasureUnit targetUnit, MicroPropsGenerator parent) {
+        this.fOutputUnit = targetUnit;
         this.fParent = parent;
-        MeasureUnitImpl inputUnitImpl = MeasureUnitImpl.forIdentifier(inputUnit.getIdentifier());
-        MeasureUnitImpl outputUnitImpl = MeasureUnitImpl.forIdentifier(outputUnit.getIdentifier());
-        this.fComplexUnitConverter = new ComplexUnitsConverter(inputUnitImpl, outputUnitImpl,
-                                                               new UnitsData().getConversionRates());
+        MeasureUnitImpl targetUnitImpl = MeasureUnitImpl.forIdentifier(targetUnit.getIdentifier());
+        this.fComplexUnitConverter = new ComplexUnitsConverter(targetUnitImpl, new ConversionRates());
     }
 
     /**
@@ -48,10 +43,11 @@ public MicroProps processQuantity(DecimalQuantity quantity) {
         MicroProps result = this.fParent.processQuantity(quantity);
 
         quantity.roundToInfinity(); // Enables toDouble
-        List<Measure> measures = this.fComplexUnitConverter.convert(quantity.toBigDecimal(), result.rounder);
+        ComplexUnitsConverter.ComplexConverterResult complexConverterResult
+                = this.fComplexUnitConverter.convert(quantity.toBigDecimal(), result.rounder);
 
         result.outputUnit = this.fOutputUnit;
-        UsagePrefsHandler.mixedMeasuresToMicros(measures, quantity, result);
+        UsagePrefsHandler.mixedMeasuresToMicros(complexConverterResult, quantity, result);
 
         return result;
     }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/UsagePrefsHandler.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/UsagePrefsHandler.java
index b490204..b2c7ec8 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/UsagePrefsHandler.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/UsagePrefsHandler.java
@@ -6,6 +6,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import com.ibm.icu.impl.units.ComplexUnitsConverter;
 import com.ibm.icu.impl.units.MeasureUnitImpl;
 import com.ibm.icu.impl.units.UnitsRouter;
 import com.ibm.icu.util.Measure;
@@ -30,24 +31,10 @@ public UsagePrefsHandler(ULocale locale, MeasureUnit inputUnit, String usage, Mi
      * in measures.
      */
     protected static void
-    mixedMeasuresToMicros(List<Measure> measures, DecimalQuantity outQuantity, MicroProps outMicros) {
-        outMicros.mixedMeasures = new ArrayList<>();
-        if (measures.size() > 1) {
-            // For debugging
-            assert (outMicros.outputUnit.getComplexity() == MeasureUnit.Complexity.MIXED);
-
-            // Check that we received the expected number of measurements:
-            assert measures.size() == outMicros.outputUnit.splitToSingleUnits().size();
-
-            // Mixed units: except for the last value, we pass all values to the
-            // LongNameHandler via micros->mixedMeasures.
-            for (int i = 0, n = measures.size() - 1; i < n; i++) {
-                outMicros.mixedMeasures.add(measures.get(i));
-            }
-        }
-
-        // The last value (potentially the only value) gets passed on via quantity.
-        outQuantity.setToBigDecimal((BigDecimal) measures.get(measures.size()- 1).getNumber());
+    mixedMeasuresToMicros(ComplexUnitsConverter.ComplexConverterResult complexConverterResult, DecimalQuantity quantity, MicroProps outMicros) {
+        outMicros.mixedMeasures = complexConverterResult.measures;
+        outMicros.indexOfQuantity = complexConverterResult.indexOfQuantity;
+        quantity.setToBigDecimal((BigDecimal) outMicros.mixedMeasures.get(outMicros.indexOfQuantity).getNumber());
     }
 
     /**
@@ -74,11 +61,8 @@ public MicroProps processQuantity(DecimalQuantity quantity) {
 
         quantity.roundToInfinity(); // Enables toDouble
         final UnitsRouter.RouteResult routed = fUnitsRouter.route(quantity.toBigDecimal(), micros);
-
-        final List<Measure> routedMeasures = routed.measures;
         micros.outputUnit = routed.outputUnit.build();
-
-        UsagePrefsHandler.mixedMeasuresToMicros(routedMeasures, quantity, micros);
+        UsagePrefsHandler.mixedMeasuresToMicros(routed.complexConverterResult, quantity, micros);
         return micros;
     }
 }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/ComplexUnitsConverter.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/ComplexUnitsConverter.java
index cf52263..3ea9030 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/ComplexUnitsConverter.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/ComplexUnitsConverter.java
@@ -3,6 +3,7 @@
 package com.ibm.icu.impl.units;
 
 import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -12,49 +13,79 @@
 import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
 import com.ibm.icu.number.Precision;
 import com.ibm.icu.util.Measure;
-import com.ibm.icu.util.MeasureUnit;
 
 /**
- * Converts from single or compound unit to single, compound or mixed units.
- * For example, from `meter` to `foot+inch`.
+ * Converts from single or compound unit to single, compound or mixed units. For example, from `meter` to `foot+inch`.
  * <p>
- * DESIGN:
- * This class uses `UnitConverter` in order to perform the single converter (i.e. converters from a
- * single unit to another single unit). Therefore, `ComplexUnitsConverter` class contains multiple
- * instances of the `UnitConverter` to perform the conversion.
+ * DESIGN: This class uses `UnitConverter` in order to perform the single converter (i.e. converters from a single unit
+ * to another single unit). Therefore, `ComplexUnitsConverter` class contains multiple instances of the `UnitConverter`
+ * to perform the conversion.
  */
 public class ComplexUnitsConverter {
     public static final BigDecimal EPSILON = BigDecimal.valueOf(Math.ulp(1.0));
     public static final BigDecimal EPSILON_MULTIPLIER = BigDecimal.valueOf(1).add(EPSILON);
     private ArrayList<UnitConverter> unitConverters_;
-    // Individual units of mixed units, sorted big to small
-    private ArrayList<MeasureUnitImpl> units_;
-    // Individual units of mixed units, sorted in desired output order
-    private ArrayList<MeasureUnit> outputUnits_;
+    /**
+     * Individual units of mixed units, sorted big to small, with indices
+     * indicating the requested output mixed unit order.
+     */
+    private List<MeasureUnitImpl.MeasureUnitImplWithIndex> units_;
+    private MeasureUnitImpl inputUnit_;
 
     /**
-     * Constructor of `ComplexUnitsConverter`.
-     * NOTE:
-     * - inputUnit and outputUnits must be under the same category
-     * - e.g. meter to feet and inches --> all of them are length units.
+     * Constructs <code>ComplexUnitsConverter</code> for an <code>inputUnit</code> that could be Single, Compound or
+     * Mixed. In case of: 1- Single and Compound units, the conversion will not perform anything, the input will be
+     * equal to the output. 2- Mixed Unit the conversion will consider the input in the biggest unit. and will convert
+     * it to be spread throw the input units. For example: if input unit is "inch-and-foot", and the input is 2.5. The
+     * converter will consider the input value in "foot", because foot is the biggest unit. Then, it will convert 2.5
+     * feet to "inch-and-foot".
      *
-     * @param inputUnit   represents the source unit. (should be single or compound unit).
-     * @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
+     * @param targetUnit
+     *            represents the input unit. could be any type. (single, compound or mixed).
      */
-    public ComplexUnitsConverter(MeasureUnitImpl inputUnit, MeasureUnitImpl outputUnits,
-                                 ConversionRates conversionRates) {
-        units_ = outputUnits.extractIndividualUnits();
-        outputUnits_ = new ArrayList<>(units_.size());
-        for (MeasureUnitImpl itr : units_) {
-            outputUnits_.add(itr.build());
+    public ComplexUnitsConverter(MeasureUnitImpl targetUnit, ConversionRates conversionRates) {
+        this.units_ = targetUnit.extractIndividualUnitsWithIndices();
+        assert (!this.units_.isEmpty());
+
+        // Assign the biggest unit to inputUnit_.
+        this.inputUnit_ = this.units_.get(0).unitImpl;
+        MeasureUnitImpl.MeasureUnitImplComparator comparator = new MeasureUnitImpl.MeasureUnitImplComparator(
+                conversionRates);
+        for (MeasureUnitImpl.MeasureUnitImplWithIndex unitWithIndex : this.units_) {
+            if (comparator.compare(unitWithIndex.unitImpl, this.inputUnit_) > 0) {
+                this.inputUnit_ = unitWithIndex.unitImpl;
+            }
         }
-        assert (!units_.isEmpty());
 
+        this.init(conversionRates);
+    }
+
+    /**
+     * Constructs <code>ComplexUnitsConverter</code> NOTE: - inputUnit and outputUnits must be under the same category -
+     * e.g. meter to feet and inches --> all of them are length units.
+     *
+     * @param targetUnit
+     *            represents the source unit. (should be single or compound unit).
+     * @param outputUnits
+     *            represents the output unit. could be any type. (single, compound or mixed).
+     */
+    public ComplexUnitsConverter(MeasureUnitImpl targetUnit, MeasureUnitImpl outputUnits,
+            ConversionRates conversionRates) {
+        this.inputUnit_ = targetUnit;
+        this.units_ = outputUnits.extractIndividualUnitsWithIndices();
+        assert (!this.units_.isEmpty());
+
+        this.init(conversionRates);
+    }
+
+    /**
+     * Sorts units_, which must be populated before calling this, and populates
+     * unitConverters_.
+     */
+    private void init(ConversionRates conversionRates) {
         // Sort the units in a descending order.
-        Collections.sort(
-                this.units_,
-                Collections.reverseOrder(new MeasureUnitImpl.MeasureUnitImplComparator(conversionRates)));
-
+        Collections.sort(this.units_,
+                Collections.reverseOrder(new MeasureUnitImpl.MeasureUnitImplWithIndexComparator(conversionRates)));
 
         // If the `outputUnits` is `UMEASURE_UNIT_MIXED` such as `foot+inch`. Thus means there is more than one unit
         //  and In this case we need more converters to convert from the `inputUnit` to the first unit in the
@@ -73,20 +104,20 @@ public ComplexUnitsConverter(MeasureUnitImpl inputUnit, MeasureUnitImpl outputUn
         unitConverters_ = new ArrayList<>();
         for (int i = 0, n = units_.size(); i < n; i++) {
             if (i == 0) { // first element
-                unitConverters_.add(new UnitConverter(inputUnit, units_.get(i), conversionRates));
+                unitConverters_.add(new UnitConverter(this.inputUnit_, units_.get(i).unitImpl, conversionRates));
             } else {
-                unitConverters_.add(new UnitConverter(units_.get(i - 1), units_.get(i), conversionRates));
+                unitConverters_
+                        .add(new UnitConverter(units_.get(i - 1).unitImpl, units_.get(i).unitImpl, conversionRates));
             }
         }
     }
 
     /**
-     * Returns true if the specified `quantity` of the `inputUnit`, expressed in terms of the biggest
-     * unit in the MeasureUnit `outputUnit`, is greater than or equal to `limit`.
+     * Returns true if the specified `quantity` of the `inputUnit`, expressed in terms of the biggest unit in the
+     * MeasureUnit `outputUnit`, is greater than or equal to `limit`.
      * <p>
-     * For example, if the input unit is `meter` and the target unit is `foot+inch`. Therefore, this
-     * function will convert the `quantity` from `meter` to `foot`, then, it will compare the value in
-     * `foot` with the `limit`.
+     * For example, if the input unit is `meter` and the target unit is `foot+inch`. Therefore, this function will
+     * convert the `quantity` from `meter` to `foot`, then, it will compare the value in `foot` with the `limit`.
      */
     public boolean greaterThanOrEqual(BigDecimal quantity, BigDecimal limit) {
         assert !units_.isEmpty();
@@ -95,6 +126,16 @@ public boolean greaterThanOrEqual(BigDecimal quantity, BigDecimal limit) {
         return unitConverters_.get(0).convert(quantity).multiply(EPSILON_MULTIPLIER).compareTo(limit) >= 0;
     }
 
+    public static class ComplexConverterResult {
+        public final int indexOfQuantity;
+        public final List<Measure> measures;
+
+        ComplexConverterResult(int indexOfQuantity, List<Measure> measures) {
+            this.indexOfQuantity = indexOfQuantity;
+            this.measures = measures;
+        }
+    }
+
     /**
      * Returns outputMeasures which is an array with the corresponding values.
      * - E.g. converting meters to feet and inches.
@@ -103,9 +144,8 @@ public boolean greaterThanOrEqual(BigDecimal quantity, BigDecimal limit) {
      * the smallest element is the only element that could have fractional values. And all
      * other elements are floored to the nearest integer
      */
-    public List<Measure> convert(BigDecimal quantity, Precision rounder) {
-        List<Measure> result = new ArrayList<>(unitConverters_.size());
-        BigDecimal sign = BigDecimal.ONE;
+    public ComplexConverterResult convert(BigDecimal quantity, Precision rounder) {
+        BigInteger sign = BigInteger.ONE;
         if (quantity.compareTo(BigDecimal.ZERO) < 0) {
             quantity = quantity.abs();
             sign = sign.negate();
@@ -117,8 +157,7 @@ public List<Measure> convert(BigDecimal quantity, Precision rounder) {
         // - N-1 converters convert to bigger units for which we want integers,
         // - the Nth converter (index N-1) converts to the smallest unit, which
         //   isn't (necessarily) an integer.
-        List<BigDecimal> intValues = new ArrayList<>(unitConverters_.size() - 1);
-
+        List<BigInteger> intValues = new ArrayList<>(unitConverters_.size() - 1);
         for (int i = 0, n = unitConverters_.size(); i < n; ++i) {
             quantity = (unitConverters_.get(i)).convert(quantity);
 
@@ -129,83 +168,89 @@ public List<Measure> convert(BigDecimal quantity, Precision rounder) {
                 // decision is made. However after the thresholding, we use the
                 // original values to ensure unbiased accuracy (to the extent of
                 // double's capabilities).
-                BigDecimal flooredQuantity =
-                    quantity.multiply(EPSILON_MULTIPLIER).setScale(0, RoundingMode.FLOOR);
+                BigInteger flooredQuantity = quantity.multiply(EPSILON_MULTIPLIER).setScale(0, RoundingMode.FLOOR).toBigInteger();
                 intValues.add(flooredQuantity);
 
                 // Keep the residual of the quantity.
-                //   For example: `3.6 feet`, keep only `0.6 feet`
-                BigDecimal remainder = quantity.subtract(flooredQuantity);
+                // For example: `3.6 feet`, keep only `0.6 feet`
+                BigDecimal remainder = quantity.subtract(BigDecimal.valueOf(flooredQuantity.longValue()));
                 if (remainder.compareTo(BigDecimal.ZERO) == -1) {
                     quantity = BigDecimal.ZERO;
                 } else {
                     quantity = remainder;
                 }
-            } else { // LAST ELEMENT
-                if (rounder == null) {
-                    // Nothing to do for the last element.
-                    break;
-                }
-
-                // Round the last value
-                // TODO(ICU-21288): get smarter about precision for mixed units.
-                DecimalQuantity quant = new DecimalQuantity_DualStorageBCD(quantity);
-                rounder.apply(quant);
-                quantity = quant.toBigDecimal();
-                if (i == 0) {
-                    // Last element is also the first element, so we're done
-                    break;
-                }
-
-                // Check if there's a carry, and bubble it back up the resulting intValues.
-                BigDecimal carry = unitConverters_.get(i)
-                                       .convertInverse(quantity)
-                                       .multiply(EPSILON_MULTIPLIER)
-                                       .setScale(0, RoundingMode.FLOOR);
-                if (carry.compareTo(BigDecimal.ZERO) <= 0) { // carry is not greater than zero
-                    break;
-                }
-                quantity = quantity.subtract(unitConverters_.get(i).convert(carry));
-                intValues.set(i - 1, intValues.get(i - 1).add(carry));
-
-                // We don't use the first converter: that one is for the input unit
-                for (int j = i - 1; j > 0; j--) {
-                    carry = unitConverters_.get(j)
-                                .convertInverse(intValues.get(j))
-                                .multiply(EPSILON_MULTIPLIER)
-                                .setScale(0, RoundingMode.FLOOR);
-                    if (carry.compareTo(BigDecimal.ZERO) <= 0) { // carry is not greater than zero
-                        break;
-                    }
-                    intValues.set(j, intValues.get(j).subtract(unitConverters_.get(j).convert(carry)));
-                    intValues.set(j - 1, intValues.get(j - 1).add(carry));
-                }
             }
         }
 
-        // Package values into Measure instances in result:
+        quantity = applyRounder(intValues, quantity, rounder);
+
+        // Initialize empty measures.
+        List<Measure> measures = new ArrayList<>(unitConverters_.size());
+        for (int i = 0; i < unitConverters_.size(); i++) {
+            measures.add(null);
+        }
+
+        // Package values into Measure instances in measures:
+        int indexOfQuantity = -1;
         for (int i = 0, n = unitConverters_.size(); i < n; ++i) {
             if (i < n - 1) {
-                result.add(new Measure(intValues.get(i).multiply(sign), units_.get(i).build()));
+                Measure measure = new Measure(intValues.get(i).multiply(sign), units_.get(i).unitImpl.build());
+                measures.set(units_.get(i).index, measure);
             } else {
-                result.add(new Measure(quantity.multiply(sign), units_.get(i).build()));
+                indexOfQuantity = units_.get(i).index;
+                Measure measure =
+                        new Measure(quantity.multiply(BigDecimal.valueOf(sign.longValue())),
+                                units_.get(i).unitImpl.build());
+                measures.set(indexOfQuantity, measure);
             }
         }
 
-        for (int i = 0; i < result.size(); i++) {
-            for (int j = i; j < result.size(); j++) {
-                // Find the next expected unit, and swap it into place.
-                if (result.get(j).getUnit().equals(outputUnits_.get(i))) {
-                    if (j != i) {
-                        Measure tmp = result.get(j);
-                        result.set(j, result.get(i));
-                        result.set(i, tmp);
-                    }
-                }
-            }
+        return new ComplexConverterResult(indexOfQuantity , measures);
+    }
+
+    /**
+     * Applies the rounder to the quantity (last element) and bubble up any carried value to all the intValues.
+     *
+     * @return the rounded quantity
+     */
+    private BigDecimal applyRounder(List<BigInteger> intValues, BigDecimal quantity, Precision rounder) {
+        if (rounder == null) {
+            return quantity;
         }
 
-        return result;
+        DecimalQuantity quantityBCD = new DecimalQuantity_DualStorageBCD(quantity);
+        rounder.apply(quantityBCD);
+        quantity = quantityBCD.toBigDecimal();
+
+        if (intValues.size() == 0) {
+            // There is only one element, Therefore, nothing to be done
+            return quantity;
+        }
+
+        // Check if there's a carry, and bubble it back up the resulting intValues.
+        int lastIndex = unitConverters_.size() - 1;
+        BigDecimal carry = unitConverters_.get(lastIndex).convertInverse(quantity).multiply(EPSILON_MULTIPLIER)
+                .setScale(0, RoundingMode.FLOOR);
+        if (carry.compareTo(BigDecimal.ZERO) <= 0) { // carry is not greater than zero
+            return quantity;
+        }
+        quantity = quantity.subtract(unitConverters_.get(lastIndex).convert(carry));
+        intValues.set(lastIndex - 1, intValues.get(lastIndex - 1).add(carry.toBigInteger()));
+
+        // We don't use the first converter: that one is for the input unit
+        for (int j = lastIndex - 1; j > 0; j--) {
+            carry = unitConverters_.get(j)
+                    .convertInverse(BigDecimal.valueOf(intValues.get(j).longValue()))
+                    .multiply(EPSILON_MULTIPLIER)
+                    .setScale(0, RoundingMode.FLOOR);
+            if (carry.compareTo(BigDecimal.ZERO) <= 0) { // carry is not greater than zero
+                break;
+            }
+            intValues.set(j, intValues.get(j).subtract(unitConverters_.get(j).convert(carry).toBigInteger()));
+            intValues.set(j - 1, intValues.get(j - 1).add(carry.toBigInteger()));
+        }
+
+        return quantity;
     }
 
     @Override
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/MeasureUnitImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/MeasureUnitImpl.java
index e0aa3c5..5658019 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/MeasureUnitImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/MeasureUnitImpl.java
@@ -17,21 +17,18 @@
 public class MeasureUnitImpl {
 
     /**
-     * The full unit identifier.  Null if not computed.
+     * The full unit identifier. Null if not computed.
      */
     private String identifier = null;
-
     /**
      * The complexity, either SINGLE, COMPOUND, or MIXED.
      */
     private MeasureUnit.Complexity complexity = MeasureUnit.Complexity.SINGLE;
-
     /**
      * The list of single units. These may be summed or multiplied, based on the
      * value of the complexity field.
      * <p>
-     * The "dimensionless" unit (SingleUnitImpl default constructor) must not be
-     * added to this list.
+     * The "dimensionless" unit (SingleUnitImpl default constructor) must not be added to this list.
      * <p>
      * The "dimensionless" <code>MeasureUnitImpl</code> has an empty <code>singleUnits</code>.
      */
@@ -94,29 +91,20 @@ public void takeReciprocal() {
         }
     }
 
-    /**
-     * Extracts the list of all the individual units inside the `MeasureUnitImpl`.
-     * For example:
-     * -   if the <code>MeasureUnitImpl</code> is <code>foot-per-hour</code>
-     * it will return a list of 1 <code>{foot-per-hour}</code>
-     * -   if the <code>MeasureUnitImpl</code> is <code>foot-and-inch</code>
-     * it will return a list of 2 <code>{ foot, inch}</code>
-     *
-     * @return a list of <code>MeasureUnitImpl</code>
-     */
-    public ArrayList<MeasureUnitImpl> extractIndividualUnits() {
-        ArrayList<MeasureUnitImpl> result = new ArrayList<>();
+    public ArrayList<MeasureUnitImplWithIndex> extractIndividualUnitsWithIndices() {
+        ArrayList<MeasureUnitImplWithIndex> result = new ArrayList<>();
         if (this.getComplexity() == MeasureUnit.Complexity.MIXED) {
             // In case of mixed units, each single unit can be considered as a stand alone MeasureUnitImpl.
+            int i = 0;
             for (SingleUnitImpl singleUnit :
                     this.getSingleUnits()) {
-                result.add(new MeasureUnitImpl(singleUnit));
+                result.add(new MeasureUnitImplWithIndex(i++, new MeasureUnitImpl(singleUnit)));
             }
 
             return result;
         }
 
-        result.add(this.copy());
+        result.add(new MeasureUnitImplWithIndex(0, this.copy()));
         return result;
     }
 
@@ -198,7 +186,6 @@ public SingleUnitImpl getSingleUnitImpl() {
         throw new UnsupportedOperationException();
     }
 
-
     /**
      * Returns the CLDR unit identifier and null if not computed.
      */
@@ -266,6 +253,11 @@ public void serialize() {
         this.identifier = result.toString();
     }
 
+    @Override
+    public String toString() {
+        return "MeasureUnitImpl [" + build().getIdentifier() + "]";
+    }
+
     public enum CompoundPart {
         // Represents "-per-"
         PER(0),
@@ -369,6 +361,16 @@ public int getValue() {
 
     }
 
+    public static class MeasureUnitImplWithIndex {
+        int index;
+        MeasureUnitImpl unitImpl;
+
+        MeasureUnitImplWithIndex(int index, MeasureUnitImpl unitImpl) {
+            this.index = index;
+            this.unitImpl = unitImpl;
+        }
+    }
+
     public static class UnitsParser {
         // This used only to not build the trie each time we use the parser
         private volatile static CharsTrie savedTrie = null;
@@ -748,19 +750,28 @@ public MeasureUnitImplComparator(ConversionRates conversionRates) {
         public int compare(MeasureUnitImpl o1, MeasureUnitImpl o2) {
             BigDecimal factor1 = this.conversionRates.getFactorToBase(o1).getConversionRate();
             BigDecimal factor2 = this.conversionRates.getFactorToBase(o2).getConversionRate();
+
             return factor1.compareTo(factor2);
         }
     }
 
+    static class MeasureUnitImplWithIndexComparator implements Comparator<MeasureUnitImplWithIndex> {
+        private MeasureUnitImplComparator measureUnitImplComparator;
+
+        public MeasureUnitImplWithIndexComparator(ConversionRates conversionRates) {
+            this.measureUnitImplComparator = new MeasureUnitImplComparator(conversionRates);
+        }
+
+        @Override
+        public int compare(MeasureUnitImplWithIndex o1, MeasureUnitImplWithIndex o2) {
+            return this.measureUnitImplComparator.compare(o1.unitImpl, o2.unitImpl);
+        }
+    }
+
     static class SingleUnitComparator implements Comparator<SingleUnitImpl> {
         @Override
         public int compare(SingleUnitImpl o1, SingleUnitImpl o2) {
             return o1.compareTo(o2);
         }
     }
-
-    @Override
-    public String toString() {
-        return "MeasureUnitImpl [" + build().getIdentifier() + "]";
-    }
 }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsRouter.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsRouter.java
index d344f3e..b7c925c 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsRouter.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/units/UnitsRouter.java
@@ -176,19 +176,15 @@ public ConverterPreference(MeasureUnitImpl source, MeasureUnitImpl targetUnit,
     }
 
     public class RouteResult {
-        // A list of measures: a single measure for single units, multiple measures
-        // for mixed units.
-        //
-        // TODO(icu-units/icu#21): figure out the right mixed unit API.
-        public final List<Measure> measures;
+        public final ComplexUnitsConverter.ComplexConverterResult complexConverterResult;
 
         // The output unit for this RouteResult. This may be a MIXED unit - for
         // example: "yard-and-foot-and-inch", for which `measures` will have three
         // elements.
         public final MeasureUnitImpl outputUnit;
 
-        RouteResult(List<Measure> measures, MeasureUnitImpl outputUnit) {
-            this.measures = measures;
+        RouteResult(ComplexUnitsConverter.ComplexConverterResult complexConverterResult, MeasureUnitImpl outputUnit) {
+            this.complexConverterResult = complexConverterResult;
             this.outputUnit = outputUnit;
         }
     }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
index abcb141..3954c4f 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
@@ -36,6 +36,7 @@
 import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.MeasureUnit;
 
+
 /**
  * This is the "brain" of the number formatting pipeline. It ties all the pieces together, taking in a
  * MacroProps and a DecimalQuantity and outputting a properly formatted number string.
@@ -274,9 +275,7 @@ private static MicroPropsGenerator macrosToMicroGenerator(MacroProps macros, Mic
             }
             chain = usagePrefsHandler = new UsagePrefsHandler(macros.loc, macros.unit, macros.usage, chain);
         } else if (isMixedUnit) {
-            // TODO(icu-units#97): The input unit should be the largest unit, not the first unit, in the identifier.
-            MeasureUnit inputUnit = macros.unit.splitToSingleUnits().get(0);
-            chain = new UnitConversionHandler(inputUnit, macros.unit, chain);
+            chain = new UnitConversionHandler(macros.unit, chain);
         }
 
         // Multiplier
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
index d7ec361..99e21cc 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/UnitsTest.java
@@ -138,7 +138,7 @@ class TestCase {
             final MeasureUnitImpl inputImpl = MeasureUnitImpl.forIdentifier(input.getIdentifier());
             final MeasureUnitImpl outputImpl = MeasureUnitImpl.forIdentifier(output.getIdentifier());
             ComplexUnitsConverter converter = new ComplexUnitsConverter(inputImpl, outputImpl, rates);
-            measures = converter.convert(testCase.value, null);
+            measures = converter.convert(testCase.value, null).measures;
 
             assertEquals("measures length", testCase.expected.length, measures.size());
             int i = 0;
@@ -166,20 +166,67 @@ class TestCase {
 
     @Test
     public void testComplexUnitConverterSorting() {
+        class TestCase {
+            String message;
+            String inputUnit;
+            String outputUnit;
+            double inputValue;
+            Measure[] expectedMeasures;
+            double accuracy;
 
-        MeasureUnitImpl source = MeasureUnitImpl.forIdentifier("meter");
-        MeasureUnitImpl target = MeasureUnitImpl.forIdentifier("inch-and-foot");
+            public TestCase(String message, String inputUnit, String outputUnit, double inputValue, Measure[] expectedMeasures, double accuracy) {
+                this.message = message;
+                this.inputUnit = inputUnit;
+                this.outputUnit = outputUnit;
+                this.inputValue = inputValue;
+                this.expectedMeasures = expectedMeasures;
+                this.accuracy = accuracy;
+            }
+        }
+
+        TestCase[] testCases = new TestCase[]{
+                new TestCase(
+                        "inch-and-foot",
+                        "meter",
+                        "inch-and-foot",
+                        10.0,
+                        new Measure[]{
+                                new Measure(9.70079, MeasureUnit.INCH),
+                                new Measure(32, MeasureUnit.FOOT),
+                        },
+                        0.0001
+                ),
+                new TestCase(
+                        "inch-and-yard-and-foot",
+                        "meter",
+                        "inch-and-yard-and-foot",
+                        100.0,
+                        new Measure[]{
+                                new Measure(1.0079, MeasureUnit.INCH),
+                                new Measure(109, MeasureUnit.YARD),
+                                new Measure(1, MeasureUnit.FOOT),
+                        },
+                        0.0001
+                ),
+        };
+
         ConversionRates conversionRates = new ConversionRates();
+        for (TestCase testCase : testCases) {
+            MeasureUnitImpl input = MeasureUnitImpl.forIdentifier(testCase.inputUnit);
+            MeasureUnitImpl output = MeasureUnitImpl.forIdentifier(testCase.outputUnit);
 
-        ComplexUnitsConverter complexConverter = new ComplexUnitsConverter(source, target, conversionRates);
-        List<Measure> measures = complexConverter.convert(BigDecimal.valueOf(10.0), null);
+            ComplexUnitsConverter converter = new ComplexUnitsConverter(input, output, conversionRates);
+            List<Measure> actualMeasures = converter.convert(BigDecimal.valueOf(testCase.inputValue), null).measures;
 
-        assertEquals(measures.size(), 2);
-        assertEquals("inch-and-foot unit 0", "inch", measures.get(0).getUnit().getIdentifier());
-        assertEquals("inch-and-foot unit 1", "foot", measures.get(1).getUnit().getIdentifier());
-
-        assertEquals("inch-and-foot value 0", 9.7008, measures.get(0).getNumber().doubleValue(), 0.0001);
-        assertEquals("inch-and-foot value 1", 32, measures.get(1).getNumber().doubleValue(), 0.0001);
+            assertEquals(testCase.message, testCase.expectedMeasures.length, actualMeasures.size());
+            for (int i = 0; i < testCase.expectedMeasures.length; i++) {
+                assertEquals(testCase.message, testCase.expectedMeasures[i].getUnit(), actualMeasures.get(i).getUnit());
+                assertEquals(testCase.message,
+                        testCase.expectedMeasures[i].getNumber().doubleValue(),
+                        actualMeasures.get(i).getNumber().doubleValue(),
+                        testCase.accuracy);
+            }
+        }
     }
 
 
@@ -443,7 +490,7 @@ private void insertData(String category,
         for (TestCase testCase :
                 tests) {
             UnitsRouter router = new UnitsRouter(testCase.inputUnit.second, testCase.region, testCase.usage);
-            List<Measure> measures = router.route(testCase.input, null).measures;
+            List<Measure> measures = router.route(testCase.input, null).complexConverterResult.measures;
 
             assertEquals("Measures size must be the same as expected units",
                     measures.size(), testCase.expectedInOrder.size());
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
index 3e14dc0..dec74b9 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
@@ -728,6 +728,73 @@ public void unitMeasure() {
                 "4 metric tons, 285 kilograms, 710 grams");
 
         assertFormatSingle(
+                "Mixed Unit (Not Sorted) [metric]",
+                "unit/gram-and-kilogram unit-width-full-name",
+                "unit/gram-and-kilogram unit-width-full-name",
+                NumberFormatter.with()
+                        .unit(MeasureUnit.forIdentifier("gram-and-kilogram"))
+                        .unitWidth(UnitWidth.FULL_NAME),
+                new ULocale("en-US"),
+                4.28571,
+                "285.71 grams, 4 kilograms");
+
+        assertFormatSingle(
+                "Mixed Unit (Not Sorted) [imperial]",
+                "unit/inch-and-yard-and-foot unit-width-full-name",
+                "unit/inch-and-yard-and-foot unit-width-full-name",
+                NumberFormatter.with()
+                        .unit(MeasureUnit.forIdentifier("inch-and-yard-and-foot"))
+                        .unitWidth(UnitWidth.FULL_NAME),
+                new ULocale("en-US"),
+                4.28571,
+                "10.28556 inches, 4 yards, 0 feet");
+
+        assertFormatSingle(
+                "Mixed Unit (Not Sorted) [imperial full]",
+                "unit/inch-and-yard-and-foot unit-width-full-name",
+                "unit/inch-and-yard-and-foot unit-width-full-name",
+                NumberFormatter.with()
+                        .unit(MeasureUnit.forIdentifier("inch-and-yard-and-foot"))
+                        .unitWidth(UnitWidth.FULL_NAME),
+                new ULocale("en-US"),
+                4.38571,
+                "1.88556 inches, 4 yards, 1 foot");
+
+        assertFormatSingle(
+                "Mixed Unit (Not Sorted) [imperial full integers]",
+                "unit/inch-and-yard-and-foot @# unit-width-full-name",
+                "unit/inch-and-yard-and-foot @# unit-width-full-name",
+                NumberFormatter.with()
+                        .unit(MeasureUnit.forIdentifier("inch-and-yard-and-foot"))
+                        .unitWidth(UnitWidth.FULL_NAME)
+                        .precision(Precision.maxSignificantDigits(2)),
+                new ULocale("en-US"),
+                4.36112,
+                "1 inch, 4 yards, 1 foot");
+
+        assertFormatSingle(
+                "Mixed Unit (Not Sorted) [imperial full] with `And` in the end",
+                "unit/inch-and-yard-and-foot unit-width-full-name",
+                "unit/inch-and-yard-and-foot unit-width-full-name",
+                NumberFormatter.with()
+                        .unit(MeasureUnit.forIdentifier("inch-and-yard-and-foot"))
+                        .unitWidth(UnitWidth.FULL_NAME),
+                new ULocale("fr-FR"),
+                4.38571,
+                "1,88556\u00A0pouce, 4\u00A0yards et 1\u00A0pied");
+
+        assertFormatSingle(
+                "Mixed unit, Scientific [Not in Order]",
+                "unit/foot-and-inch-and-yard E0",
+                "unit/foot-and-inch-and-yard E0",
+                NumberFormatter.with()
+                        .unit(MeasureUnit.forIdentifier("foot-and-inch-and-yard"))
+                        .notation(Notation.scientific()),
+                new ULocale("en-US"),
+                3.65,
+                "1 ft, 1.14E1 in, 3 yd");
+
+        assertFormatSingle(
                 "Testing \"1 foot 12 inches\"",
                 "unit/foot-and-inch @### unit-width-full-name",
                 "unit/foot-and-inch @### unit-width-full-name",