com.feilong.core.util.AggregateUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.feilong.core.util.AggregateUtil.java

Source

/*
 * 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?
    }
}