/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.dolphinscheduler.spi.utils; import org.apache.commons.beanutils.BeanMap; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; /** * Provides utility methods and decorators for {@link Collection} instances. *

* Various utility methods might put the input objects into a Set/Map/Bag. In case * the input objects override {@link Object#equals(Object)}, it is mandatory that * the general contract of the {@link Object#hashCode()} method is maintained. *

* NOTE: From 4.0, method parameters will take {@link Iterable} objects when possible. * * @version $Id: CollectionUtils.java 1686855 2015-06-22 13:00:27Z tn $ * @since 1.0 */ public class CollectionUtils { private CollectionUtils() { throw new UnsupportedOperationException("Construct CollectionUtils"); } /** * The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * Returns a new {@link Collection} containing a minus a subset of * b. Only the elements of b that satisfy the predicate * condition, p are subtracted from a. * *

The cardinality of each element e in the returned {@link Collection} * that satisfies the predicate condition will be the cardinality of e in a * minus the cardinality of e in b, or zero, whichever is greater.

*

The cardinality of each element e in the returned {@link Collection} that does not * satisfy the predicate condition will be equal to the cardinality of e in a.

* * @param a the collection to subtract from, must not be null * @param b the collection to subtract, must not be null * @param T * @return a new collection with the results * @see Collection#removeAll */ public static Collection subtract(Set a, Set b) { return org.apache.commons.collections4.CollectionUtils.subtract(a, b); } public static boolean isNotEmpty(Collection coll) { return !isEmpty(coll); } public static boolean isEmpty(Collection coll) { return coll == null || coll.isEmpty(); } /** * String to map * * @param str string * @param separator separator * @return string to map */ public static Map stringToMap(String str, String separator) { return stringToMap(str, separator, ""); } /** * String to map * * @param str string * @param separator separator * @param keyPrefix prefix * @return string to map */ public static Map stringToMap(String str, String separator, String keyPrefix) { Map emptyMap = new HashMap<>(0); if (StringUtils.isEmpty(str)) { return emptyMap; } if (StringUtils.isEmpty(separator)) { return emptyMap; } String[] strings = str.split(separator); int initialCapacity = (int)(strings.length / DEFAULT_LOAD_FACTOR) + 1; Map map = new HashMap<>(initialCapacity); for (int i = 0; i < strings.length; i++) { String[] strArray = strings[i].split("="); if (strArray.length != 2) { return emptyMap; } //strArray[0] KEY strArray[1] VALUE if (StringUtils.isEmpty(keyPrefix)) { map.put(strArray[0], strArray[1]); } else { map.put(keyPrefix + strArray[0], strArray[1]); } } return map; } /** * Transform item in collection * * @param collection origin collection * @param transformFunc transform function * @param origin item type * @param target type * @return transform list */ public static List transformToList(Collection collection, Function transformFunc) { if (isEmpty(collection)) { return new ArrayList<>(); } return collection.stream().map(transformFunc).collect(Collectors.toList()); } /** * Collect collection to map * * @param collection origin collection * @param keyTransformFunction key transform function * @param target k type * @param value * @return map */ public static Map collectionToMap(Collection collection, Function keyTransformFunction) { if (isEmpty(collection)) { return new HashMap<>(); } return collection.stream().collect(Collectors.toMap(keyTransformFunction, Function.identity())); } /** * Helper class to easily access cardinality properties of two collections. * * @param the element type */ private static class CardinalityHelper { /** * Contains the cardinality for each object in collection A. */ final Map cardinalityA; /** * Contains the cardinality for each object in collection B. */ final Map cardinalityB; /** * Create a new CardinalityHelper for two collections. * * @param a the first collection * @param b the second collection */ public CardinalityHelper(final Iterable a, final Iterable b) { cardinalityA = CollectionUtils.getCardinalityMap(a); cardinalityB = CollectionUtils.getCardinalityMap(b); } /** * Returns the frequency of this object in collection A. * * @param obj the object * @return the frequency of the object in collection A */ public int freqA(final Object obj) { return getFreq(obj, cardinalityA); } /** * Returns the frequency of this object in collection B. * * @param obj the object * @return the frequency of the object in collection B */ public int freqB(final Object obj) { return getFreq(obj, cardinalityB); } private int getFreq(final Object obj, final Map freqMap) { final Integer count = freqMap.get(obj); if (count != null) { return count; } return 0; } } /** * returns {@code true} iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. * * @param a the first collection * @param b the second collection * @return Returns true iff the given Collections contain exactly the same elements with exactly the same cardinalities. * That is, iff the cardinality of e in a is equal to the cardinality of e in b, for each element e in a or b. */ public static boolean equalLists(Collection a, Collection b) { if (a == null && b == null) { return true; } if (a == null || b == null) { return false; } return isEqualCollection(a, b); } /** * Returns {@code true} iff the given {@link Collection}s contain * exactly the same elements with exactly the same cardinalities. *

* That is, iff the cardinality of e in a is * equal to the cardinality of e in b, * for each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @return true iff the collections contain the same elements with the same cardinalities. */ public static boolean isEqualCollection(final Collection a, final Collection b) { if (a.size() != b.size()) { return false; } final CardinalityHelper helper = new CardinalityHelper<>(a, b); if (helper.cardinalityA.size() != helper.cardinalityB.size()) { return false; } for (final Object obj : helper.cardinalityA.keySet()) { if (helper.freqA(obj) != helper.freqB(obj)) { return false; } } return true; } /** * Returns a {@link Map} mapping each unique element in the given * {@link Collection} to an {@link Integer} representing the number * of occurrences of that element in the {@link Collection}. *

* Only those elements present in the collection will appear as * keys in the map. * * @param the type of object in the returned {@link Map}. This is a super type of O * @param coll the collection to get the cardinality map for, must not be null * @return the populated cardinality map */ public static Map getCardinalityMap(final Iterable coll) { final Map count = new HashMap<>(); for (final O obj : coll) { count.put(obj, count.getOrDefault(obj, 0) + 1); } return count; } /** * Removes certain attributes of each object in the list * * @param originList origin list * @param exclusionSet exclusion set * @param T * @return removes certain attributes of each object in the list */ public static List> getListByExclusion(List originList, Set exclusionSet) { List> instanceList = new ArrayList<>(); if (exclusionSet == null) { exclusionSet = new HashSet<>(); } if (originList == null) { return instanceList; } Map instanceMap; for (T instance : originList) { BeanMap beanMap = new BeanMap(instance); instanceMap = new LinkedHashMap<>(16, 0.75f, true); for (Map.Entry entry : beanMap.entrySet()) { if (exclusionSet.contains(entry.getKey())) { continue; } instanceMap.put((String) entry.getKey(), entry.getValue()); } instanceList.add(instanceMap); } return instanceList; } }