compare two arrays values
190126
function sameValues(a, b) {
  var
    i = a.length,
    j = b.length,
    k = i === j
  ;
  if (k) {
    while (i--) {
      if (a[i] !== b[i]) {
        return !k;
      }
    }
  }
  return k;
}
764305
function compareArrays(array1, array2, dontConvert) {
        var len = Math.min(array1.length, array2.length),
            lengthDiff = Math.abs(array1.length - array2.length),
            diffs = 0,
            i;
        for (i = 0; i < len; i++) {
            if ((dontConvert && array1[i] !== array2[i]) ||
                (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
                diffs++;
            }
        }
        return diffs + lengthDiff;
    }
1066438
def difference(array, k=1):
    
    if (len(array) - k) < 1:
        raise ValueError()
    if k < 0:
        raise ValueError("k has to be greater or equal than zero!")
    elif k == 0:
        return [i - i for i in array]
    else:
        return [j - i for i, j in zip(array[:-k], array[k:])]
1232442
static boolean arraysEqual(byte[] array1, byte[] array2) {
    if (array1.length != array2.length) {
      return false;
    }
    
    boolean isEqual = true;
    for (int i = 0; i < array1.length; i++) {
      if (array1[i] != array2[i]) {
        isEqual = false;
      }
    }
    return isEqual;
  }
492221
function (value, array) {
            if (util.isFn(array.indexOf)) {
                return array.indexOf(value);
            } else {
                return $.inArray(value, array);
            }
        }
1331344
function ( array1, array2 ) {
		var a = array1.length > array2.length ? array1 : array2,
		    b = ( a === array1 ? array2 : array1 );

		return a.filter( function ( key ) {
			return array.contains( b, key );
		});
	}
729530
function isEqual (array1, array2) {
    return array1.length === array2.length && array1.every(function (element, index) {
      return element === array2[index];
    });
  }
403721
public static boolean equal(Object obj1, Object obj2) {
        if (obj1 == obj2) {
            return true;
        }
        if (obj1 == null || obj2 == null) {
            return false;
        }
        if (obj1.getClass().isArray()) {
            return equalsArray(obj1, obj2);
        }
        // this does not handle arrays embedded in objects, such as in lists/maps
        // but you shouldn't use arrays like that, should you?
        return obj1.equals(obj2);
    }
1021061
public static boolean areEqual(byte[] array1, byte[] array2) {
		if (array1.length != array2.length) return false;

		for (int i=0; i<array1.length; ++i)
			if (array1[i] != array2[i]) return false;

		return true;
	}
878595
public static function equal(array $array1, array $array2)
    {
        if (static::count($array1) != static::count($array2)) {
            return false;
        } else {
            for ($i = 0; $i < static::count($array1); $i++) {
                if ($array1[$i] != $array2[$i]) {
                    return false;
                }
            }
        }

        return true;
    }
140377
function compare(buf1, buf2){
    if (buf1.length !== buf2.length)
        return buf1.length - buf2.length;

    for (var i = 0; i < buf1.length; i++)
        if (buf1[i] !== buf2[i])
            return buf1[i] - buf2[i];

    return 0;
}
32152
protected function diff($array1, $array2)
    {
        $difference = [];
        foreach ($array1 as $key => $value) {
            if (is_array($value)) {
                if (!isset($array2[$key]) || !is_array($array2[$key])) {
                    $difference[$key] = $value;
                } else {
                    $new_diff = $this->diff($value, $array2[$key]);
                    if (!empty($new_diff)) {
                        $difference[$key] = $new_diff;
                    }
                }
            } else {
                if (!array_key_exists($key, $array2) || $array2[$key] !== $value) {
                    $difference[$key] = $value;
                }
            }
        }

        return $difference;
    }
958383
function contains(array, value) {
  for (let i = 0; i < array.length; i++) {
    if (array[i] == value) { // non-strict equality check!
      return true;
    }
  }
  return false;
}
1248701
function compareBufferArrays(buffArray1, buffArray2) {
  assert(Array.isArray(buffArray1), 'We only accept arrays.');
  assert(Array.isArray(buffArray2), 'We only accept arrays.');
  if (buffArray1.length !== buffArray2.length) {
    return false;
  }
  for (var i = 0; i < buffArray1.length; ++i) {
    var buff1 = buffArray1[i];
    assert(Buffer.isBuffer(buff1), 'Only buffers allowed in 1');
    var buff2 = buffArray2[i];
    assert(Buffer.isBuffer(buff2), 'Only buffers allowed in 2');
    if (Buffer.compare(buff1, buff2) !== 0) {
      return false;
    }
  }
  return true;
}
898831
protected boolean compareValues(Object previous, Object value) {

        if ((previous == null) && (value != null)) {
            return (true);
        } else if ((previous != null) && (value == null)) {
            return (true);
        } else if ((previous == null)) {
            return (false);
        }

        boolean valueChanged = false;
        Object oldarray[];
        Object newarray[];

        // The arrays may be arrays of primitives;  for simplicity,
        // perform the boxing here.
        if (!(previous instanceof Object[])) {
            previous = toObjectArray(previous);
        }

        if (!(value instanceof Object[])) {
            value = toObjectArray(value);
        }

        // If values are still not of the type Object[], it is perhaps a
        // mistake by the renderers, so return false, so that
        // ValueChangedEvent is not queued in this case.
        if (!(previous instanceof Object[]) || 
              !(value instanceof Object[])) {
              return false;
        }
        oldarray = (Object[]) previous;
        newarray = (Object[])value;
       
        // If we got here then both the arrays cannot be null
        // if their lengths vary, return false.
        if ( oldarray.length != newarray.length) {
            return true;
        }
        
        // make sure every element in the previous array occurs the same
        // number of times in the current array. This should help us
        // to find out the values changed are not. Since we cannot assume
        // the browser will send the elements in the same order everytime,
        // it will not suffice to just compare the element position and position.
        int count1;
        int count2;
        for ( int i= 0; i < oldarray.length; ++i ) {
            count1 = countElementOccurrence(oldarray[i], oldarray);
            count2 = countElementOccurrence(oldarray[i], newarray);
            if ( count1 != count2 ) {
                valueChanged = true;
                break;
            }     
        }    
        return valueChanged;

    }
1057724
public static function getArrayDiffRecursively($array1, $array2)
    {
        $diff_array = [];

        // value 비교
        if (!is_array($array1)) {
            if ($array1 != $array2) {
                return $array1;
            } else {
                return [];
            }
        }

        // array 비교
        foreach ($array1 as $key => $value) {
            if (is_array($value)) {
                if (!is_array($array2[$key])) {
                    $diff_array[$key] = $value;
                } else {
                    $sub_diff_array = ArrayUtils::getArrayDiffRecursively($value, $array2[$key]);
                    if (count($sub_diff_array)) {
//                        $diff_array[$key] = $sub_diff_array;
                        $diff_array[$key] = $value;
                    }
                }
            } elseif ($array2[$key] != $value) {
                $diff_array[$key] = $value;
            }
        }
        return $diff_array;
    }
78117
private static boolean equalsArray(Object obj1, Object obj2) {
        if (obj1 instanceof Object[] && obj2 instanceof Object[] && obj1.getClass() == obj2.getClass()) {
            return Arrays.deepEquals((Object[]) obj1, (Object[]) obj2);
        } else if (obj1 instanceof int[] && obj2 instanceof int[]) {
            return Arrays.equals((int[]) obj1, (int[]) obj2);
        } else if (obj1 instanceof long[] && obj2 instanceof long[]) {
            return Arrays.equals((long[]) obj1, (long[]) obj2);
        } else if (obj1 instanceof byte[] && obj2 instanceof byte[]) {
            return Arrays.equals((byte[]) obj1, (byte[]) obj2);
        } else if (obj1 instanceof double[] && obj2 instanceof double[]) {
            return Arrays.equals((double[]) obj1, (double[]) obj2);
        } else if (obj1 instanceof float[] && obj2 instanceof float[]) {
            return Arrays.equals((float[]) obj1, (float[]) obj2);
        } else if (obj1 instanceof char[] && obj2 instanceof char[]) {
            return Arrays.equals((char[]) obj1, (char[]) obj2);
        } else if (obj1 instanceof short[] && obj2 instanceof short[]) {
            return Arrays.equals((short[]) obj1, (short[]) obj2);
        } else if (obj1 instanceof boolean[] && obj2 instanceof boolean[]) {
            return Arrays.equals((boolean[]) obj1, (boolean[]) obj2);
        }
        // reachable if obj1 is an array and obj2 is not
        return false;
    }

    /**
     * Checks if two floats are equal based on identity.
     * <p>
     * This performs the same check as {@link Float#equals(Object)}.
     * 
     * @param val1  the first value, may be null
     * @param val2  the second value, may be null
     * @return true if equal
     */
    public static boolean equal(float val1, float val2) {
        return Float.floatToIntBits(val1) == Float.floatToIntBits(val2);
    }

    /**
     * Checks if two floats are equal within the specified tolerance.
     * <p>
     * Two NaN values are equal. Positive and negative infinity are only equal with themselves.
     * Otherwise, the difference between the values is compared to the tolerance.
     * 
     * @param val1  the first value, may be null
     * @param val2  the second value, may be null
     * @param tolerance  the tolerance used to compare equal
     * @return true if equal
     */
    public static boolean equalWithTolerance(float val1, float val2, double tolerance) {
        return (Float.floatToIntBits(val1) == Float.floatToIntBits(val2)) || Math.abs(val1 - val2) <= tolerance;
    }

    /**
     * Checks if two doubles are equal based on identity.
     * <p>
     * This performs the same check as {@link Double#equals(Object)}.
     * 
     * @param val1  the first value, may be null
     * @param val2  the second value, may be null
     * @return true if equal
     */
    public static boolean equal(double val1, double val2) {
        return Double.doubleToLongBits(val1) == Double.doubleToLongBits(val2);
    }

    /**
     * Checks if two doubles are equal within the specified tolerance.
     * <p>
     * Two NaN values are equal. Positive and negative infinity are only equal with themselves.
     * Otherwise, the difference between the values is compared to the tolerance.
     * The tolerance is expected to be a finite value, not NaN or infinity.
     * 
     * @param val1  the first value, may be null
     * @param val2  the second value, may be null
     * @param tolerance  the tolerance used to compare equal
     * @return true if equal
     */
    public static boolean equalWithTolerance(double val1, double val2, double tolerance) {
        return (Double.doubleToLongBits(val1) == Double.doubleToLongBits(val2)) || Math.abs(val1 - val2) <= tolerance;
    }

    /**
     * Returns a hash code for an object handling null.
     * 
     * @param obj  the object, may be null
     * @return the hash code
     */
    public static int hashCode(Object obj) {
        if (obj == null) {
            return 0;
        }
        if (obj.getClass().isArray()) {
            return hashCodeArray(obj);
        }
        return obj.hashCode();
    }

    // extracted from hashCode(Object) to aid hotspot inlining
    private static int hashCodeArray(Object obj) {
        if (obj instanceof Object[]) {
            return Arrays.deepHashCode((Object[]) obj);
        } else if (obj instanceof int[]) {
            return Arrays.hashCode((int[]) obj);
        } else if (obj instanceof long[]) {
            return Arrays.hashCode((long[]) obj);
        } else if (obj instanceof byte[]) {
            return Arrays.hashCode((byte[]) obj);
        } else if (obj instanceof double[]) {
            return Arrays.hashCode((double[]) obj);
        } else if (obj instanceof float[]) {
            return Arrays.hashCode((float[]) obj);
        } else if (obj instanceof char[]) {
            return Arrays.hashCode((char[]) obj);
        } else if (obj instanceof short[]) {
            return Arrays.hashCode((short[]) obj);
        } else if (obj instanceof boolean[]) {
            return Arrays.hashCode((boolean[]) obj);
        }
        // unreachable?
        return obj.hashCode();
    }

    /**
     * Returns a hash code for a {@code boolean}.
     * 
     * @param value  the value to convert to a hash code
     * @return the hash code
     */
    public static int hashCode(boolean value) {
        return value ? 1231 : 1237;
    }

    /**
     * Returns a hash code for an {@code int}.
     * 
     * @param value  the value to convert to a hash code
     * @return the hash code
     */
    public static int hashCode(int value) {
        return value;
    }

    /**
     * Returns a hash code for a {@code long}.
     * 
     * @param value  the value to convert to a hash code
     * @return the hash code
     */
    public static int hashCode(long value) {
        return (int) (value ^ value >>> 32);
    }

    /**
     * Returns a hash code for a {@code float}.
     * 
     * @param value  the value to convert to a hash code
     * @return the hash code
     */
    public static int hashCode(float value) {
        return Float.floatToIntBits(value);
    }

    /**
     * Returns a hash code for a {@code double}.
     * 
     * @param value  the value to convert to a hash code
     * @return the hash code
     */
    public static int hashCode(double value) {
        return hashCode(Double.doubleToLongBits(value));
    }

    //-----------------------------------------------------------------------
    /**
     * Returns the {@code toString} value handling arrays.
     * 
     * @param obj  the object, may be null
     * @return the string, not null
     */
    public static String toString(Object obj) {
        if (obj == null) {
            return "null";
        }
        if (obj.getClass().isArray()) {
            return toStringArray(obj);
        }
        return obj.toString();
    }

    // extracted from toString(Object) to aid hotspot inlining
    private static String toStringArray(Object obj) {
        if (obj instanceof Object[]) {
            return Arrays.deepToString((Object[]) obj);
        } else if (obj instanceof int[]) {
            return Arrays.toString((int[]) obj);
        } else if (obj instanceof long[]) {
            return Arrays.toString((long[]) obj);
        } else if (obj instanceof byte[]) {
            return Arrays.toString((byte[]) obj);
        } else if (obj instanceof double[]) {
            return Arrays.toString((double[]) obj);
        } else if (obj instanceof float[]) {
            return Arrays.toString((float[]) obj);
        } else if (obj instanceof char[]) {
            return Arrays.toString((char[]) obj);
        } else if (obj instanceof short[]) {
            return Arrays.toString((short[]) obj);
        } else if (obj instanceof boolean[]) {
            return Arrays.toString((boolean[]) obj);
        }
        // unreachable?
        return obj.toString();
    }

    //-----------------------------------------------------------------------
    /**
     * Checks if the two beans have the same set of properties.
     * <p>
     * This comparison checks that both beans have the same set of property names
     * and that the value of each property name is also equal.
     * It does not check the bean type, thus a {@link FlexiBean} may be equal
     * to a {@link DirectBean}.
     * <p>
     * This comparison is usable with the {@link #propertiesHashCode} method.
     * The result is the same as that if each bean was converted to a {@code Map}
     * from name to value.
     * 
     * @param bean1  the first bean to compare, not null
     * @param bean2  the second bean to compare, not null
     * @return true if equal
     */
    public static boolean propertiesEqual(Bean bean1, Bean bean2) {
        Set<String> names = bean1.propertyNames();
        if (names.equals(bean2.propertyNames()) == false) {
            return false;
        }
        for (String name : names) {
            Object value1 = bean1.property(name).get();
            Object value2 = bean2.property(name).get();
            if (equal(value1, value2) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns a hash code based on the set of properties on a bean.
     * <p>
     * This hash code is usable with the {@link #propertiesEqual} method.
     * The result is the same as that if each bean was converted to a {@code Map}
     * from name to value.
     * 
     * @param bean  the bean to generate a hash code for, not null
     * @return the hash code
     */
    public static int propertiesHashCode(Bean bean) {
        int hash = 7;
        Set<String> names = bean.propertyNames();
        for (String name : names) {
            Object value = bean.property(name).get();
            hash += hashCode(value);
        }
        return hash;
    }

    /**
     * Returns a string describing the set of properties on a bean.
     * <p>
     * The result is the same as that if the bean was converted to a {@code Map}
     * from name to value.
     * 
     * @param bean  the bean to generate a string for, not null
     * @param prefix  the prefix to use, null ignored
     * @return the string form of the bean, not null
     */
    public static String propertiesToString(Bean bean, String prefix) {
        Set<String> names = bean.propertyNames();
        StringBuilder buf;
        if (prefix != null) {
            buf = new StringBuilder((names.size()) * 32 + prefix.length()).append(prefix);
        } else {
            buf = new StringBuilder((names.size()) * 32);
        }
        buf.append('{');
        if (names.size() > 0) {
            for (String name : names) {
                Object value = bean.property(name).get();
                buf.append(name).append('=').append(value).append(',').append(' ');
            }
            buf.setLength(buf.length() - 2);
        }
        buf.append('}');
        return buf.toString();
    }

    /**
     * Flattens a bean to a {@code Map}.
     * <p>
     * The returned map will contain all the properties from the bean with their actual values.
     * 
     * @param bean  the bean to generate a string for, not null
     * @return the bean as a map, not null
     */
    public static Map<String, Object> flatten(Bean bean) {
        Map<String, MetaProperty<?>> propertyMap = bean.metaBean().metaPropertyMap();
        Map<String, Object> map = new LinkedHashMap<>(propertyMap.size());
        for (Entry<String, MetaProperty<?>> entry : propertyMap.entrySet()) {
            map.put(entry.getKey(), entry.getValue().get(bean));
        }
        return Collections.unmodifiableMap(map);
    }

    //-----------------------------------------------------------------------
    /**
     * Clones a bean.
     * <p>
     * This performs a deep clone. There is no protection against cycles in
     * the object graph beyond {@code StackOverflowError}.
     * 
     * @param <T>  the type of the bean
     * @param original  the original bean to clone, null returns null
     * @return the cloned bean, null if null input
     */
    public static <T extends Bean> T clone(T original) {
        if (original == null || original instanceof ImmutableBean) {
            return original;
        }
        return cloneAlways(original);
    }

    /**
     * Clones a bean always.
     * <p>
     * This performs a deep clone. There is no protection against cycles in
     * the object graph beyond {@code StackOverflowError}.
     * This differs from {@link #clone()} in that immutable beans are also cloned.
     * 
     * @param <T>  the type of the bean
     * @param original  the original bean to clone, not null
     * @return the cloned bean, not null
     */
    public static <T extends Bean> T cloneAlways(T original) {
        @SuppressWarnings("unchecked")
        BeanBuilder<T> builder = (BeanBuilder<T>) original.metaBean().builder();
        for (MetaProperty<?> mp : original.metaBean().metaPropertyIterable()) {
            if (mp.style().isBuildable()) {
                Object value = mp.get(original);
                builder.set(mp.name(), Cloner.INSTANCE.clone(value));
            }
        }
        return builder.build();
    }

    //-----------------------------------------------------------------------
    /**
     * Checks if the value is not null, throwing an exception if it is.
     * 
     * @param value  the value to check, may be null
     * @param propertyName  the property name, should not be null
     * @throws IllegalArgumentException if the value is null
     */
    public static void notNull(Object value, String propertyName) {
        if (value == null) {
            throw new IllegalArgumentException(notNullMsg(propertyName));
        }
    }

    // extracted from notNull(Object,String) to aid hotspot inlining
    private static String notNullMsg(String propertyName) {
        return "Argument '" + propertyName + "' must not be null";
    }

    /**
     * Checks if the value is not blank, throwing an exception if it is.
     * <p>
     * Validate that the specified argument is not null and has at least one non-whitespace character.
     * 
     * @param value  the value to check, may be null
     * @param propertyName  the property name, should not be null
     * @throws IllegalArgumentException if the value is null or empty
     */
    public static void notBlank(String value, String propertyName) {
        if (isBlank(value)) {
            throw new IllegalArgumentException(notBlank(propertyName));
        }
    }

    @SuppressWarnings("null")
    private static boolean isBlank(String str) {
        int strLen = (str != null ? str.length() : 0);
        if (strLen != 0) {
            for (int i = 0; i < strLen; i++) {
                if (!Character.isWhitespace(str.charAt(i))) {
                    return false;
                }
            }
        }
        return true;
    }

    private static String notBlank(String propertyName) {
        return "Argument '" + propertyName + "' must not be empty";
    }

    /**
     * Checks if the value is not empty, throwing an exception if it is.
     * 
     * @param value  the value to check, may be null
     * @param propertyName  the property name, should not be null
     * @throws IllegalArgumentException if the value is null or empty
     */
    public static void notEmpty(String value, String propertyName) {
        if (value == null || value.length() == 0) {
            throw new IllegalArgumentException(notEmpty(propertyName));
        }
    }

    // extracted from notEmpty(?,String) to aid hotspot inlining
    private static String notEmpty(String propertyName) {
        return "Argument '" + propertyName + "' must not be empty";
    }

    /**
     * Checks if the collection value is not empty, throwing an exception if it is.
     * 
     * @param value  the value to check, may be null
     * @param propertyName  the property name, should not be null
     * @throws IllegalArgumentException if the value is null or empty
     */
    public static void notEmpty(Collection<?> value, String propertyName) {
        if (value == null || value.isEmpty()) {
            throw new IllegalArgumentException(notEmpty(propertyName));
        }
    }

    /**
     * Checks if the map value is not empty, throwing an exception if it is.
     * 
     * @param value  the value to check, may be null
     * @param propertyName  the property name, should not be null
     * @throws IllegalArgumentException if the value is null or empty
     */
    public static void notEmpty(Map<?, ?> value, String propertyName) {
        if (value == null || value.isEmpty()) {
            throw new IllegalArgumentException(notEmpty(propertyName));
        }
    }

    //-----------------------------------------------------------------------
    /**
     * Extracts the collection content type as a {@code Class} from a property.
     * <p>
     * This method allows the resolution of generics in certain cases.
     * 
     * @param prop  the property to examine, not null
     * @return the collection content type, null if unable to determine or type has no generic parameters
     */
    public static Class<?> collectionType(Property<?> prop) {
        return collectionType(prop.metaProperty(), prop.bean().getClass());
    }

    /**
     * Extracts the collection content type as a {@code Class} from a meta-property.
     * <p>
     * The target type is the type of the object, not the declaring type of the meta-property.
     * 
     * @param prop  the property to examine, not null
     * @param targetClass  the target type to evaluate against, not null
     * @return the collection content type, null if unable to determine or type has no generic parameters
     */
    public static Class<?> collectionType(MetaProperty<?> prop, Class<?> targetClass) {
        return extractTypeClass(prop, targetClass, 1, 0);
    }

    /**
     * Extracts the map value type generic type parameters as a {@code Class} from a meta-property.
     * <p>
     * The target type is the type of the object, not the declaring type of the meta-property.
     * <p>
     * This is used when the collection generic parameter is a map or collection.
     * 
     * @param prop  the property to examine, not null
     * @param targetClass  the target type to evaluate against, not null
     * @return the collection content type generic parameters, empty if unable to determine, no nulls
     */
    public static List<Class<?>> collectionTypeTypes(MetaProperty<?> prop, Class<?> targetClass) {
        Type type = extractType(targetClass, prop, 1, 0);
        return extractTypeClasses(targetClass, type);
    }

    /**
     * Extracts the map key type as a {@code Class} from a meta-property.
     * 
     * @param prop  the property to examine, not null
     * @return the map key type, null if unable to determine or type has no generic parameters
     */
    public static Class<?> mapKeyType(Property<?> prop) {
        return mapKeyType(prop.metaProperty(), prop.bean().getClass());
    }

    /**
     * Extracts the map key type as a {@code Class} from a meta-property.
     * <p>
     * The target type is the type of the object, not the declaring type of the meta-property.
     * 
     * @param prop  the property to examine, not null
     * @param targetClass  the target type to evaluate against, not null
     * @return the map key type, null if unable to determine or type has no generic parameters
     */
    public static Class<?> mapKeyType(MetaProperty<?> prop, Class<?> targetClass) {
        return extractTypeClass(prop, targetClass, 2, 0);
    }

    /**
     * Extracts the map value type as a {@code Class} from a meta-property.
     * 
     * @param prop  the property to examine, not null
     * @return the map value type, null if unable to determine or type has no generic parameters
     */
    public static Class<?> mapValueType(Property<?> prop) {
        return mapValueType(prop.metaProperty(), prop.bean().getClass());
    }

    /**
     * Extracts the map value type as a {@code Class} from a meta-property.
     * <p>
     * The target type is the type of the object, not the declaring type of the meta-property.
     * 
     * @param prop  the property to examine, not null
     * @param targetClass  the target type to evaluate against, not null
     * @return the map value type, null if unable to determine or type has no generic parameters
     */
    public static Class<?> mapValueType(MetaProperty<?> prop, Class<?> targetClass) {
        return extractTypeClass(prop, targetClass, 2, 1);
    }

    /**
     * Extracts the map value type generic type parameters as a {@code Class} from a meta-property.
     * <p>
     * The target type is the type of the object, not the declaring type of the meta-property.
     * <p>
     * This is used when the map value generic parameter is a map or collection.
     * 
     * @param prop  the property to examine, not null
     * @param targetClass  the target type to evaluate against, not null
     * @return the map value type generic parameters, empty if unable to determine, no nulls
     */
    public static List<Class<?>> mapValueTypeTypes(MetaProperty<?> prop, Class<?> targetClass) {
        Type type = extractType(targetClass, prop, 2, 1);
        return extractTypeClasses(targetClass, type);
    }

    /**
     * Low-level method to extract generic type information.
     * 
     * @param prop  the property to examine, not null
     * @param targetClass  the target type to evaluate against, not null
     * @param size  the number of generic parameters expected
     * @param index  the index of the generic parameter
     * @return the type, null if unable to determine or type has no generic parameters
     */
    public static Class<?> extractTypeClass(MetaProperty<?> prop, Class<?> targetClass, int size, int index) {
        return eraseToClass(extractType(targetClass, prop, size, index));
    }

    private static Type extractType(Class<?> targetClass, MetaProperty<?> prop, int size, int index) {
        Type genType = prop.propertyGenericType();
        if (genType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genType;
            Type[] types = pt.getActualTypeArguments();
            if (types.length == size) {
                Type type = types[index];
                if (type instanceof WildcardType) {
                    WildcardType wtype = (WildcardType) type;
                    if (wtype.getLowerBounds().length == 0 && wtype.getUpperBounds().length > 0) {
                        type = wtype.getUpperBounds()[0];
                    }
                }
                if (type instanceof TypeVariable) {
                    type = resolveGenerics(targetClass, (TypeVariable<?>) type);
                }
                return type;
            }
        }
        return null;
    }

    private static List<Class<?>> extractTypeClasses(Class<?> targetClass, Type type) {
        List<Class<?>> result = new ArrayList<>();
        if (type != null) {
            if (type instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) type;
                Type[] actualTypes = pt.getActualTypeArguments();
                for (Type actualType : actualTypes) {
                    if (actualType instanceof TypeVariable) {
                        actualType = resolveGenerics(targetClass, (TypeVariable<?>) actualType);
                    }
                    Class<?> cls = eraseToClass(actualType);
                    result.add(cls != null ? cls : Object.class);
                }