Java tutorial
/* * Copyright (C) 2008 feilong * * Licensed 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 com.feilong.core.util; import java.math.BigDecimal; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.collections4.Predicate; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import com.feilong.core.bean.ConvertUtil; import com.feilong.core.bean.PropertyUtil; import com.feilong.core.lang.NumberUtil; import static com.feilong.core.Validator.isNullOrEmpty; import static com.feilong.core.bean.ConvertUtil.toArray; import static com.feilong.core.bean.ConvertUtil.toBigDecimal; import static com.feilong.core.util.MapUtil.newLinkedHashMap; /** * ??. * * <p> * sql? <a href="http://www.sqlcourse2.com/agg_functions.html">(Aggregate functions)</a> * </p> * * @author <a href="http://feitianbenyue.iteye.com/">feilong</a> * @see "java.util.stream.Collectors" * @since 1.8.0 */ //Aggregate Functions public final class AggregateUtil { /** Don't let anyone instantiate this class. */ private AggregateUtil() { //AssertionError?. ?????. ???. //see Effective Java 2nd throw new AssertionError("No " + getClass().getName() + " instances for you!"); } //***********************************avg************************************************************* /** * ?. * * <h3>:</h3> * <blockquote> * * <pre class="code"> * List{@code <User>} list = new ArrayList{@code <User>}(); * list.add(new User(2L)); * list.add(new User(5L)); * list.add(new User(5L)); * AggregateUtil.avg(list, "id", 2) * </pre> * * <b>:</b> 4.00 * </blockquote> * * @param <O> * the generic type * @param objectCollection * the object collection * @param propertyName * O??,Possibly indexed and/or nested name of the property to be modified,?? * <a href="../bean/BeanUtil.html#propertyName">propertyName</a> * @param scale * ? * @return <code>objectCollection</code> nullempty, {@link Collections#emptyMap()}<br> * <code>propertyName</code> null, {@link NullPointerException}<br> * <code>propertyName</code> blank, {@link IllegalArgumentException}<br> * @see #sum(Collection, String...) */ public static <O> BigDecimal avg(Collection<O> objectCollection, String propertyName, int scale) { return avg(objectCollection, ConvertUtil.toArray(propertyName), scale).get(propertyName); } /** * ?. * * <p> * {@link LinkedHashMap},key <code>propertyNames</code>,value???;key?? <code>propertyNames</code>? * </p> * * <h3>:</h3> * <blockquote> * * <pre class="code"> * User user1 = new User(2L); * user1.setAge(18); * * User user2 = new User(3L); * user2.setAge(30); * * List{@code <User>} list = toList(user1, user2); * Map{@code <String, BigDecimal>} map = AggregateUtil.avg(list, ConvertUtil.toArray("id", "age"), 2); * LOGGER.info(JsonUtil.format(map)); * </pre> * * <b>:</b> * * <pre class="code"> * { * "id": 2.5, * "age": 24 * } * </pre> * * </blockquote> * * @param <O> * the generic type * @param objectCollection * the object collection * @param propertyNames * O??,Possibly indexed and/or nested name of the property to be modified,?? * <a href="../bean/BeanUtil.html#propertyName">propertyName</a> * @param scale * ? * @return <code>objectCollection</code> nullempty, {@link Collections#emptyMap()}<br> * <code>propertyNames</code> null {@link NullPointerException} <br> * <code>propertyNames</code> null {@link IllegalArgumentException}<br> * @see #sum(Collection, String...) */ public static <O> Map<String, BigDecimal> avg(Collection<O> objectCollection, String[] propertyNames, int scale) { Map<String, BigDecimal> sumMap = sum(objectCollection, propertyNames); int size = objectCollection.size(); Map<String, BigDecimal> map = newLinkedHashMap(size); for (Map.Entry<String, BigDecimal> entry : sumMap.entrySet()) { map.put(entry.getKey(), NumberUtil.getDivideValue(toBigDecimal(entry.getValue()), size, scale)); } return map; } //***********************************sum************************************************************* /** * ,?<code>objectCollection</code> ?? <code>propertyNames</code> . * * <p> * ???null,0,? * </p> * * <h3>:</h3> * <blockquote> * * <pre class="code"> * User user1 = new User(2L); * user1.setAge(18); * * User user2 = new User(3L); * user2.setAge(30); * * List{@code <User>} list = new ArrayList{@code <User>}(); * list.add(user1); * list.add(user2); * * Map{@code <String, BigDecimal>} map = AggregateUtil.sum(list, "id", "age"); * LOGGER.info(JsonUtil.format(map)); * </pre> * * <b>:</b> * * <pre class="code"> * { * "id": 5, * "age": 48 * } * </pre> * * </blockquote> * * @param <O> * the generic type * @param objectCollection * the object collection * @param propertyNames * O??,Possibly indexed and/or nested name of the property to be modified,?? * <a href="../bean/BeanUtil.html#propertyName">propertyName</a> * @return <code>objectCollection</code> nullempty, {@link Collections#emptyMap()}<br> * <code>propertyNames</code> null {@link NullPointerException} <br> * <code>propertyNames</code> null {@link IllegalArgumentException}<br> * @see #sum(Collection, String[], Predicate) */ public static <O> Map<String, BigDecimal> sum(Collection<O> objectCollection, String... propertyNames) { return sum(objectCollection, propertyNames, null); } /** * <code>objectCollection</code>,???? <code>includePredicate</code> <code>propertyNames</code> ,. * * <p> * ???null,0,? * </p> * * * <h3>:</h3> * * <blockquote> * * <pre class="code"> * User user1 = new User(10L); * user1.setName(""); * user1.setAge(50); * * User user2 = new User(20L); * user1.setName(""); * user2.setAge(50); * * User user3 = new User(100L); * user3.setName(""); * user3.setAge(100); * * List{@code <User>} list = toList(user1, user2, user3); * Map{@code <String, BigDecimal>} map = AggregateUtil.sum(list, ConvertUtil.toArray("id", "age"), new Predicate{@code <User>}(){ * * {@code @Override} * public boolean evaluate(User user){ * return !"".equals(user.getName()); * } * }); * LOGGER.debug(JsonUtil.format(map)); * * </pre> * * <b>:</b> * * <pre class="code"> * { * "id": 30, * "age": 100 * } * </pre> * * </blockquote> * * @param <O> * the generic type * @param objectCollection * the object collection * @param propertyNames * O??,Possibly indexed and/or nested name of the property to be modified,?? * <a href="../bean/BeanUtil.html#propertyName">propertyName</a> * @param includePredicate * the include predicate * @return <code>objectCollection</code> nullempty, {@link Collections#emptyMap()}<br> * <code>propertyNames</code> null {@link NullPointerException} <br> * <code>propertyNames</code> null {@link IllegalArgumentException}<br> */ public static <O> Map<String, BigDecimal> sum(Collection<O> objectCollection, String[] propertyNames, Predicate<O> includePredicate) { if (isNullOrEmpty(objectCollection)) { return Collections.emptyMap(); } Validate.noNullElements(propertyNames, "propertyNames can't be null/empty!"); Map<String, BigDecimal> sumMap = newLinkedHashMap(objectCollection.size()); for (O obj : objectCollection) { if (null != includePredicate && !includePredicate.evaluate(obj)) { continue; } for (String propertyName : propertyNames) { //???null,0 BigDecimal addValue = NumberUtil.getAddValue(ObjectUtils.defaultIfNull(sumMap.get(propertyName), 0), ObjectUtils.defaultIfNull(PropertyUtil.<Number>getProperty(obj, propertyName), 0)); sumMap.put(propertyName, addValue); } } return sumMap; } /** * ,?<code>objectCollection</code> ?? <code>propertyName</code> . * * <p> * ???null,0,? * </p> * * <h3>:</h3> * <blockquote> * * <pre class="code"> * List{@code <User>} list = new ArrayList{@code <User>}(); * list.add(new User(2L)); * list.add(new User(5L)); * list.add(new User(5L)); * * LOGGER.info("" + AggregateUtil.sum(list, "id")); * </pre> * * <b>:</b> 12 * * </blockquote> * * <h3>:</h3> * ??, * * <pre class="code"> * * protected Integer getCookieShoppingCartLinesQty(List{@code <CookieShoppingCartLine>} cartLineList){ * Integer qty = 0; * //?cookie? * if ({@code null != cartLineList && cartLineList.size() > 0}){ * for (Iterator iterator = cartLineList.iterator(); iterator.hasNext();){ * CookieShoppingCartLine cookieShoppingCartLine = (CookieShoppingCartLine) iterator.next(); * qty += cookieShoppingCartLine.getQuantity(); * } * } * return qty; * } * </pre> * * ??: * * <pre class="code"> * * protected Integer getCookieShoppingCartLinesQty(List{@code <CookieShoppingCartLine>} cartLineList){ * return isNullOrEmpty(cartLineList) ? 0 : AggregateUtil.sum(cartLineList, "quantity").intValue(); * } * </pre> * * @param <O> * the generic type * @param objectCollection * the object collection * @param propertyName * O??,Possibly indexed and/or nested name of the property to be modified,?? * <a href="../bean/BeanUtil.html#propertyName">propertyName</a> * @return <code>objectCollection</code> null empty, null<br> * <code>propertyName</code> null, {@link NullPointerException}<br> * <code>propertyName</code> blank, {@link IllegalArgumentException} * @see #sum(Collection, String...) */ public static <O> BigDecimal sum(Collection<O> objectCollection, String propertyName) { return sum(objectCollection, propertyName, null); } /** * <code>objectCollection</code>,??? ? <code>includePredicate</code> <code>propertyName</code> ,.. * * <p> * ???null,0,? * </p> * * <h3>:</h3> * * <blockquote> * * <pre class="code"> * * List{@code <User>} list = new ArrayList{@code <User>}(); * list.add(new User(2L)); * list.add(new User(50L)); * list.add(new User(50L)); * * assertEquals(new BigDecimal(100L), AggregateUtil.sum(list, "id", new Predicate{@code <User>}(){ * * {@code @Override} * public boolean evaluate(User user){ * return user.getId() {@code >} 10L; * } * })); * * </pre> * * <p> * ?,??: * </p> * * <pre class="code"> * * List{@code <User>} list = new ArrayList{@code <User>}(); * list.add(new User(2L)); * list.add(new User(50L)); * list.add(new User(50L)); * * Predicate{@code <Long>} predicate = new ComparatorPredicate{@code <Long>}(10L, ComparatorUtils.{@code <Long>} naturalComparator(), Criterion.LESS); * BigDecimal sum = AggregateUtil.sum(list, "id", new BeanPredicate{@code <User>}("id", predicate)); * assertEquals(new BigDecimal(100L), sum); * * </pre> * * </blockquote> * * @param <O> * the generic type * @param objectCollection * the object collection * @param propertyName * O??,Possibly indexed and/or nested name of the property to be modified,?? * <a href="../bean/BeanUtil.html#propertyName">propertyName</a> * @param includePredicate * the include predicate * @return <code>objectCollection</code> null empty, null<br> * <code>propertyName</code> null, {@link NullPointerException}<br> * <code>propertyName</code> blank, {@link IllegalArgumentException} * @see #sum(Collection, String[], Predicate) */ public static <O> BigDecimal sum(Collection<O> objectCollection, String propertyName, Predicate<O> includePredicate) { Validate.notBlank(propertyName, "propertyName can't be null/empty!"); return sum(objectCollection, toArray(propertyName), includePredicate).get(propertyName); } //*********************************************************************************************** /** * <code>objectCollection</code>, <code>propertyName</code> . * * <p> * {@link LinkedHashMap},key<code>propertyName</code>,value;<br> * ? <code>objectCollection</code> <code>propertyName</code>? * </p> * * <h3>:</h3> * * <blockquote> * * <pre class="code"> * * List{@code <User>} list = new ArrayList{@code <User>}(); * list.add(new User("")); * list.add(new User("")); * list.add(new User("")); * list.add(new User("")); * * Map{@code <String, Integer>} map = AggregateUtil.groupCount(list, "name"); * LOGGER.info(JsonUtil.format(map)); * * </pre> * * <b>:</b> * * <pre class="code"> * { * "": 1, * "": 1, * "": 2 * } * </pre> * * </blockquote> * * @param <T> * the generic type * @param <O> * the generic type * @param objectCollection * the object collection * @param propertyName * O??,Possibly indexed and/or nested name of the property to be modified,?? * <a href="../bean/BeanUtil.html#propertyName">propertyName</a> * @return <code>objectCollection</code> isNullOrEmpty , {@link Collections#emptyMap()}; <br> * <code>propertyName</code> null, {@link NullPointerException}<br> * <code>propertyName</code> blank, {@link IllegalArgumentException} * @see #groupCount(Collection , String, Predicate) * @see org.apache.commons.collections4.CollectionUtils#getCardinalityMap(Iterable) */ public static <T, O> Map<T, Integer> groupCount(Collection<O> objectCollection, String propertyName) { return groupCount(objectCollection, propertyName, null); } /** * <code>objectCollection</code>,?? <code>includePredicate</code>, <code>propertyName</code>. * * <p> * {@link LinkedHashMap},key<code>propertyName</code>,value;<br> * ? objectCollection <code>propertyName</code>? * </p> * * <h3>:</h3> * * <blockquote> * * <pre class="code"> * * List{@code <User>} list = new ArrayList{@code <User>}(); * list.add(new User("", 20)); * list.add(new User("", 30)); * list.add(new User("", 40)); * list.add(new User("", 50)); * * Map{@code <String, Integer>} map = AggregateUtil.groupCount(list, "name", new Predicate{@code <User>}(){ * {@code @Override} * public boolean evaluate(User user){ * return user.getAge() {@code >} 30; * } * }); * LOGGER.debug(JsonUtil.format(map)); * * </pre> * * <b>:</b> * * <pre class="code"> * { * "": 1, * "": 1 * } * * </pre> * * </blockquote> * * @param <T> * the generic type * @param <O> * the generic type * @param objectCollection * the object collection * @param propertyName * O??,Possibly indexed and/or nested name of the property to be modified,?? * <a href="../bean/BeanUtil.html#propertyName">propertyName</a> * @param includePredicate * ? ? <code>includePredicate</code>,null ?Object * @return <code>objectCollection</code> nullempty, {@link Collections#emptyMap()}<br> * <code>propertyName</code> null, {@link NullPointerException}<br> * <code>propertyName</code> blank, {@link IllegalArgumentException} * @see org.apache.commons.collections4.CollectionUtils#getCardinalityMap(Iterable) */ public static <T, O> Map<T, Integer> groupCount(Collection<O> objectCollection, String propertyName, Predicate<O> includePredicate) { if (isNullOrEmpty(objectCollection)) { return Collections.emptyMap(); } Validate.notBlank(propertyName, "propertyName can't be null/empty!"); Map<T, Integer> map = new LinkedHashMap<T, Integer>(); for (O obj : objectCollection) { if (null != includePredicate && !includePredicate.evaluate(obj)) { continue; } MapUtil.putSumValue(map, PropertyUtil.<T>getProperty(obj, propertyName), 1); } return map; } /** * <code>map</code>,keys,? value ?. * * <h3>:</h3> * <blockquote> * * <pre class="code"> * Map{@code <String, Integer>} map = new HashMap{@code <String, Integer>}(); * * map.put("a", 3007); * map.put("b", 3001); * map.put("c", 3002); * map.put("d", 3003); * map.put("e", 3004); * map.put("f", 3005); * map.put("g", -1005); * * LOGGER.info("" + AggregateUtil.getMinValue(map, "a", "b", "d", "g", "m")); * </pre> * * <b>:</b> * -1005 * * </blockquote> * * @param <K> * the key type * @param <T> * the generic type * @param map * map * @param keys * key * @return <code>map</code> nullempty, null;<br> * <code>keys</code> nullempty,<code>map</code>value?<br> * key?map key?,mapkey,warn level log<br> * keys key ? map ,null * @see MapUtil#getSubMap(Map, Object...) * @see java.util.Collections#min(Collection) */ @SafeVarargs public static <K, T extends Number & Comparable<? super T>> T getMinValue(Map<K, T> map, K... keys) { Map<K, T> subMap = MapUtil.getSubMap(map, keys); return isNullOrEmpty(subMap) ? null : Collections.min(subMap.values()); //? Number Comparable? } }