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

Java tutorial

Introduction

Here is the source code for com.feilong.core.util.MapUtil.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.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.feilong.core.bean.PropertyUtil;
import com.feilong.tools.jsonlib.JsonUtil;

import static com.feilong.core.Validator.isNotNullOrEmpty;
import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.bean.ConvertUtil.toArray;

/**
 * {@link Map}.
 * 
 * <h3>hashCodeequals:</h3>
 * <blockquote>
 * 
 * <p>
 * hashCode???<br>
 * {@link java.util.List List}??,???,; <br>
 * {@link java.util.HashMap HashMap}?{@link java.util.HashSet HashSet}? {@link java.util.Hashtable Hashtable} ,???.
 * </p>
 * 
 * <p>
 * JavahashCode??equals,??,?,??
 * </p>
 * <p>
 * <img src="http://venusdrogon.github.io/feilong-platform/mysource/hashCode-and-equals.jpg"/>
 * </p>
 * 
 * ??:
 * <ol>
 * <li>hashcode?,?,?,,,equals</li>
 * <li>equals?,??,?</li>
 * </ol>
 * </blockquote>
 * 
 * <h3> {@link java.util.Map }:</h3>
 * 
 * <blockquote>
 * <table border="1" cellspacing="0" cellpadding="4" summary="">
 * <tr style="background-color:#ccccff">
 * <th align="left">interface/class</th>
 * <th align="left"></th>
 * </tr>
 * 
 * <tr valign="top">
 * <td>{@link java.util.Map Map}</td>
 * <td>
 * <ol>
 * <li>An object that maps keys to values.</li>
 * <li>A map cannot contain duplicate keys</li>
 * <li>Takes the place of the Dictionary class</li>
 * </ol>
 * </td>
 * </tr>
 * 
 * <tr valign="top" style="background-color:#eeeeff">
 * <td>{@link java.util.HashMap HashMap}</td>
 * <td>
 * <ol>
 * <li>Hash table based implementation of the Map interface.</li>
 * <li>permits null values and the null key.</li>
 * <li>makes no guarantees as to the order of the map</li>
 * </ol>
 * <p>
 * :
 * </p>
 * <blockquote>
 * <ol>
 * <li>{@link java.util.HashMap HashMap} ?? {@link java.util.HashMap#DEFAULT_INITIAL_CAPACITY }16,DEFAULT_LOAD_FACTOR 0.75
 * <code>java.util.HashMap#addEntry</code>  2 * table.length 2?<br>
 * </ol>
 * </blockquote>
 * </td>
 * </tr>
 * 
 * <tr valign="top">
 * <td>{@link java.util.LinkedHashMap LinkedHashMap}</td>
 * <td>
 * <ol>
 * <li>Hash table and linked list implementation of the Map interface,</li>
 * <li>with predictable iteration order.</li>
 * </ol>
 * Note that: insertion order is not affected if a key is re-inserted into the map.
 * </td>
 * </tr>
 * 
 * <tr valign="top" style="background-color:#eeeeff">
 * <td>{@link java.util.TreeMap TreeMap}</td>
 * <td>
 * <ol>
 * <li>A Red-Black tree based NavigableMap implementation</li>
 * <li>sorted according to the natural ordering of its keys, or by a Comparator.</li>
 * <li> key?null, <code>NullComparator</code>key ?null.</li>
 * </ol>
 * </td>
 * </tr>
 * 
 * <tr valign="top">
 * <td>{@link java.util.Hashtable Hashtable}</td>
 * <td>
 * <ol>
 * <li>This class implements a hashtable, which maps keys to values.</li>
 * <li>synchronized.</li>
 * <li>Any non-null object can be used as a key or as a value.</li>
 * </ol>
 * </td>
 * </tr>
 * 
 * <tr valign="top" style="background-color:#eeeeff">
 * <td>{@link java.util.Properties Properties}</td>
 * <td>
 * <ol>
 * <li>The Properties class represents a persistent set of properties.</li>
 * <li>can be saved to a stream or loaded from a stream.</li>
 * <li>Each key and its corresponding value in the property list is a string.</li>
 * </ol>
 * </td>
 * </tr>
 * 
 * <tr valign="top">
 * <td>{@link java.util.IdentityHashMap IdentityHashMap}</td>
 * <td>
 * <ol>
 * <li>using reference-equality in place of object-equality when comparing keys (and values).</li>
 * <li>==equals()key.</li>
 * </ol>
 * <p style="color:red">
 * ?:?  Map ????? Map ??,??
 * </p>
 * </td>
 * </tr>
 * 
 * <tr valign="top" style="background-color:#eeeeff">
 * <td>{@link java.util.WeakHashMap WeakHashMap}</td>
 * <td>
 * <ol>
 * <li>A hashtable-based Map implementation with weak keys.</li>
 * <li>key"",key??,key?GC</li>
 * </ol>
 * </td>
 * </tr>
 * 
 * <tr valign="top">
 * <td>{@link java.util.EnumMap EnumMap}</td>
 * <td>
 * <ol>
 * <li>A specialized Map implementation for use with enum type keys.</li>
 * <li>Enum maps are maintained in the natural order of their keys</li>
 * <li>??key</li>
 * </ol>
 * </td>
 * </tr>
 * </table>
 * </blockquote>
 * 
 * @author <a href="http://feitianbenyue.iteye.com/">feilong</a>
 * @see java.util.AbstractMap.SimpleEntry
 * @see org.apache.commons.collections4.MapUtils
 * @see "com.google.common.collect.Maps"
 * @since 1.0.0
 */
public final class MapUtil {

    /** The Constant LOGGER. */
    private static final Logger LOGGER = LoggerFactory.getLogger(MapUtil.class);

    /** Don't let anyone instantiate this class. */
    private MapUtil() {
        //AssertionError?. ?????. ???.
        //see Effective Java 2nd
        throw new AssertionError("No " + getClass().getName() + " instances for you!");
    }

    /**
     * ?<code>arrayValueMap</code> ???map.
     * 
     * <h3>:</h3>
     * <blockquote>
     * <ol>
     * <li>map ???? <code>arrayValueMap</code>key?key,value?<code>value</code></li>
     * <li> {@link LinkedHashMap},???? <code>arrayValueMap</code>??</li>
     * <li>?? {@link #toArrayValueMap(Map)}</li>
     * </ol>
     * </blockquote>
     * 
     * <h3>1:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, String[]>} arrayValueMap = new LinkedHashMap{@code <String, String[]>}();
     * 
     * arrayValueMap.put("province", new String[] { "??" });
     * arrayValueMap.put("city", new String[] { "?" });
     * LOGGER.info(JsonUtil.format(ParamUtil.toSingleValueMap(arrayValueMap)));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "province": "??",
     * "city": "?"
     * }
     * </pre>
     * 
     * </blockquote>
     * 
     * <p>
     * arrayValueMapkey,?map,value?,
     * </p>
     * 
     * <h3>2:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, String[]>} arrayValueMap = new LinkedHashMap{@code <String, String[]>}();
     * 
     * arrayValueMap.put("province", new String[] { "?", "??" });
     * arrayValueMap.put("city", new String[] { "?" });
     * LOGGER.info(JsonUtil.format(ParamUtil.toSingleValueMap(arrayValueMap)));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "province": "?",
     * "city": "?"
     * }
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param arrayValueMap
     *            the array value map
     * @return <code>arrayValueMap</code>nullempty, {@link Collections#emptyMap()},<br>
     *         <code>arrayValueMap</code>key,?map,value?,<br>
     *         <code>arrayValueMap</code>keyvaluenull,?map,value null
     * @since 1.8.0 change type to generics
     */
    public static <K, V> Map<K, V> toSingleValueMap(Map<K, V[]> arrayValueMap) {
        if (isNullOrEmpty(arrayValueMap)) {
            return Collections.emptyMap();
        }
        Map<K, V> singleValueMap = newLinkedHashMap(arrayValueMap.size());//??? ? arrayValueMap ??
        for (Map.Entry<K, V[]> entry : arrayValueMap.entrySet()) {
            singleValueMap.put(entry.getKey(), null == entry.getValue() ? null : entry.getValue()[0]);
        }
        return singleValueMap;
    }

    /**
     * ??map??map.
     * 
     * <p style="color:green">
     *  {@link LinkedHashMap},??? ? <code>singleValueMap</code>??
     * </p>
     * 
     * <p>
     * ?? {@link #toSingleValueMap(Map)}
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, String>} singleValueMap = new LinkedHashMap{@code <String, String>}();
     * 
     * singleValueMap.put("province", "??");
     * singleValueMap.put("city", "?");
     * 
     * LOGGER.info(JsonUtil.format(ParamUtil.toArrayValueMap(singleValueMap)));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "province": ["??"],
     * "city": ["?"]
     * }
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param singleValueMap
     *            the name and value map
     * @return ? <code>singleValueMap</code> nullempty, {@link Collections#emptyMap()}<br>
     *         ? <code>singleValueMap</code> value?, <code>arrayValueMap</code>
     * @since 1.6.2
     */
    public static <K> Map<K, String[]> toArrayValueMap(Map<K, String> singleValueMap) {
        if (isNullOrEmpty(singleValueMap)) {
            return Collections.emptyMap();
        }
        Map<K, String[]> arrayValueMap = newLinkedHashMap(singleValueMap.size());//????singleValueMap??
        for (Map.Entry<K, String> entry : singleValueMap.entrySet()) {
            arrayValueMap.put(entry.getKey(), toArray(entry.getValue()));//?Value???V,???Object
        }
        return arrayValueMap;
    }

    /**
     *  <code>null != map  null != value</code>?key/value putmap.
     * 
     * <p>
     *  <code>map</code> null,??<br>
     *  <code>value</code> null,??<br>
     *  <code>key</code> null,?<code>map</code><code>key</code>??null <br>
     * </p>
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param map
     *            the map to add to
     * @param key
     *            the key
     * @param value
     *            the value
     * @see org.apache.commons.collections4.MapUtils#safeAddToMap(Map, Object, Object)
     * @since 1.4.0
     */
    public static <K, V> void putIfValueNotNull(final Map<K, V> map, final K key, final V value) {
        if (null != map && null != value) {
            map.put(key, value);
        }
    }

    /**
     * Put all if not null.
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param map
     *            the map
     * @param m
     *            mappings to be stored in this map
     * @see java.util.Map#putAll(Map)
     * @since 1.6.3
     */
    public static <K, V> void putAllIfNotNull(final Map<K, V> map, Map<? extends K, ? extends V> m) {
        if (null != map && null != m) {
            map.putAll(m);// m null 
        }
    }

    /**
     *  <code>null != map  isNotNullOrEmpty(value)</code>?key/value putmap.
     * 
     * <p>
     *  <code>map</code> null,??<br>
     *  <code>value</code> nullempty,??<br>
     *  <code>key</code> null,?<code>map</code><code>key</code>??null <br>
     * </p>
     *
     * <p>
     * ?,
     * </p>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * if (isNotNullOrEmpty(taoBaoOAuthLoginForCodeEntity.getState())){
     *     nameAndValueMap.put("state", taoBaoOAuthLoginForCodeEntity.getState());
     * }
     * 
     * </pre>
     * 
     * ???:
     * 
     * <pre class="code">
     * MapUtil.putIfValueNotNullOrEmpty(nameAndValueMap, "state", taoBaoOAuthLoginForCodeEntity.getState());
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param map
     *            the map
     * @param key
     *            the key
     * @param value
     *            the value
     * @since 1.6.3
     */
    public static <K, V> void putIfValueNotNullOrEmpty(final Map<K, V> map, final K key, final V value) {
        if (null != map && isNotNullOrEmpty(value)) {
            map.put(key, value);
        }
    }

    /**
     * <code>map</code><code>key</code>,<code>value</code>;?put.
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * Map{@code <String, Integer>} map = new HashMap{@code <String, Integer>}();
     * MapUtil.putSumValue(map, "1000001", 5);
     * MapUtil.putSumValue(map, "1000002", 5);
     * MapUtil.putSumValue(map, "1000002", 5);
     * LOGGER.debug(JsonUtil.format(map));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "1000001": 5,
     * "1000002": 10
     * }
     * </pre>
     * 
     * </blockquote>
     * 
     * ?:
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * if (disadvantageMap.containsKey(disadvantageToken)){
     *     disadvantageMap.put(disadvantageToken, disadvantageMap.get(disadvantageToken) + 1);
     * }else{
     *     disadvantageMap.put(disadvantageToken, 1);
     * }
     * 
     * </pre>
     * 
     * ???:
     * 
     * <pre class="code">
     * MapUtil.putSumValue(disadvantageMap, disadvantageToken, 1);
     * </pre>
     * 
     * </blockquote>
     * 
     * @param <K>
     *            the key type
     * @param map
     *            the map
     * @param key
     *            the key
     * @param value
     *            the value
     * @return  <code>map</code> null, {@link NullPointerException}<br>
     *          <code>value</code> null, {@link NullPointerException}<br>
     * @see org.apache.commons.collections4.bag.HashBag
     * @see org.apache.commons.lang3.mutable.MutableInt
     * @see "java.util.Map#getOrDefault(Object, Object)"
     * @see <a href="http://stackoverflow.com/questions/81346/most-efficient-way-to-increment-a-map-value-in-java">most-efficient-way-to-
     *      increment-a-map-value-in-java</a>
     * @since 1.5.5
     */
    public static <K> Map<K, Integer> putSumValue(Map<K, Integer> map, K key, Integer value) {
        Validate.notNull(map, "map can't be null!");
        Validate.notNull(value, "value can't be null!");

        Integer v = map.get(key);//?? map.containsKey(key),?2  two potentially expensive operations
        map.put(key, null == v ? value : value + v);//Suggestion: you should care about code readability more than little performance gain in most of the time.
        return map;
    }

    /**
     *  map put  key value(?).
     * 
     * <h3>:</h3>
     * <blockquote>
     * <ol>
     * <li>map????key,valuelist?.</li>
     * <li>map???key,ArrayList</li>
     * </ol>
     * </blockquote>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * Map{@code <String, List<String>>} mutiMap = newLinkedHashMap(2);
     * MapUtil.putMultiValue(mutiMap, "name", "");
     * MapUtil.putMultiValue(mutiMap, "name", "");
     * MapUtil.putMultiValue(mutiMap, "age", "30");
     * 
     * LOGGER.debug(JsonUtil.format(mutiMap));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
    {
        "name":         [
            "",
            ""
        ],
        "age": ["30"]
    }
     * 
     * </pre>
     * 
     * </blockquote>
     * 
     * <h3>??:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * private void putItemToMap(Map{@code <String, List<Item>>} map,String tagName,Item item){
     *     List{@code <Item>} itemList = map.get(tagName);
     * 
     *     if (isNullOrEmpty(itemList)){
     *         itemList = new ArrayList{@code <Item>}();
     *     }
     *     itemList.add(item);
     *     map.put(tagName, itemList);
     * }
     * 
     * </pre>
     * 
     * ???:
     * 
     * <pre class="code">
     * 
     * private void putItemToMap(Map{@code <String, List<Item>>} map,String tagName,Item item){
     *     com.feilong.core.util.MapUtil.putMultiValue(map, tagName, item);
     * }
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param map
     *            the map
     * @param key
     *            the key
     * @param value
     *            the value
     * @return  <code>map</code> null, {@link NullPointerException}<br>
     * @see "com.google.common.collect.ArrayListMultimap"
     * @see org.apache.commons.collections4.MultiValuedMap
     * @see org.apache.commons.collections4.IterableMap
     * @see org.apache.commons.collections4.MultiMapUtils
     * @see org.apache.commons.collections4.multimap.AbstractMultiValuedMap#put(Object, Object)
     * @since 1.6.2
     */
    public static <K, V> Map<K, List<V>> putMultiValue(Map<K, List<V>> map, K key, V value) {
        Validate.notNull(map, "map can't be null!");

        List<V> list = ObjectUtils.defaultIfNull(map.get(key), new ArrayList<V>());
        list.add(value);

        map.put(key, list);
        return map;
    }

    /**
     * <code>map</code> <code>key</code> ??map.
     * 
     * <p>
     * ?: key?map key?,mapkey,warn level log
     * </p>
     * 
     * <p>
     * map {@link LinkedHashMap},key? ? <code>keys</code>?
     * </p>
     * 
     * <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", 3001);
     * map.put("d", 3003);
     * LOGGER.debug(JsonUtil.format(MapUtil.getSubMap(map, "a", "c")));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "a": 3007,
     * "c": 3001
     * }
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param <T>
     *            the generic type
     * @param map
     *            the map
     * @param keys
     *             key?map key?,mapkey,warn level log
     * @return  <code>map</code> nullempty, {@link Collections#emptyMap()};<br>
     *          <code>keys</code> nullempty, <code>map</code><br>
     *          key?map key?,mapkey,warn level log
     */
    @SafeVarargs
    public static <K, T> Map<K, T> getSubMap(Map<K, T> map, K... keys) {
        if (isNullOrEmpty(map)) {
            return Collections.emptyMap();
        }
        if (isNullOrEmpty(keys)) {
            return map;
        }
        //??? ,key? ? <code>keys</code>?
        Map<K, T> returnMap = newLinkedHashMap(keys.length);
        for (K key : keys) {
            if (map.containsKey(key)) {
                returnMap.put(key, map.get(key));
            } else {
                LOGGER.warn("map:[{}] don't contains key:[{}]", JsonUtil.format(map.keySet(), 0, 0), key);
            }
        }
        return returnMap;
    }

    /**
     *  sub map(??keys).
     * 
     * <p>
     *  {@link LinkedHashMap},key? ? <code>map</code>?
     * </p>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, Integer>} map = new LinkedHashMap{@code <String, Integer>}();
     * 
     * map.put("a", 3007);
     * map.put("b", 3001);
     * map.put("c", 3002);
     * map.put("g", -1005);
     * 
     * LOGGER.debug(JsonUtil.format(MapUtil.getSubMapExcludeKeys(map, "a", "g", "m")));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "b": 3001,
     * "c": 3002
     * }
     * 
     * </pre>
     * 
     * </blockquote>
     * 
     * <p>
     *  <code>excludeKeys</code>? map ?key,warnlog
     * </p>
     * 
     * @param <K>
     *            the key type
     * @param <T>
     *            the generic type
     * @param map
     *            the map
     * @param excludeKeys
     *            the keys
     * @return  <code>map</code> nullempty, {@link Collections#emptyMap()};<br>
     *          <code>excludeKeys</code> nullempty, <code>map</code>
     * @since 1.0.9
     */
    @SafeVarargs
    public static <K, T> Map<K, T> getSubMapExcludeKeys(Map<K, T> map, K... excludeKeys) {
        if (isNullOrEmpty(map)) {
            return Collections.emptyMap();
        }
        return isNullOrEmpty(excludeKeys) ? map : removeKeys(new LinkedHashMap<K, T>(map), excludeKeys); //??? 
    }

    /**
     *  <code>map</code>  <code>keys</code>.
     * 
     * <h3>?</h3>
     * 
     * <blockquote>
     * <p>
     * ??<code>map</code>, <code>keys</code>,<br>
     *  <code>map</code>?key, {@link Map#remove(Object)},<br>
     * ??,warn
     * </p>
     * </blockquote>
     * 
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * Map{@code <String, String>} map = newLinkedHashMap(3);
     * 
     * map.put("name", "feilong");
     * map.put("age", "18");
     * map.put("country", "china");
     * 
     * LOGGER.debug(JsonUtil.format(MapUtil.removeKeys(map, "country")));
     * 
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "name": "feilong",
     * "age": "18"
     * }
     * 
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param map
     *            the map
     * @param keys
     *            the keys
     * @return  <code>map</code> null, {@link NullPointerException}<br>
     * @since 1.6.3
     */
    @SafeVarargs
    public static <K, V> Map<K, V> removeKeys(Map<K, V> map, K... keys) {
        Validate.notNull(map, "map can't be null!");
        for (K key : keys) {
            if (map.containsKey(key)) {
                map.remove(key);
            } else {
                LOGGER.warn("map:[{}] don't contains key:[{}]", JsonUtil.format(map.keySet(), 0, 0), key);
            }
        }
        return map;
    }

    /**
     *  <code>map</code> keyvalue.
     * 
     * <p>
     * <span style="color:red">?map</span>.<br>
     * ?map,??key?value,map(key)?(value),??key
     * </p>
     * 
     * <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", 3001);
     * map.put("d", 3003);
     * LOGGER.debug(JsonUtil.format(MapUtil.invertMap(map)));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "3001": "c",
     * "3007": "a",
     * "3003": "d"
     * }
     * </pre>
     * 
     * ? b
     * 
     * </blockquote>
     * 
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param map
     *            the map
     * @return <code>map</code> null, {@link NullPointerException}<br>
     *         <code>map</code> empty,  new HashMap
     * @see org.apache.commons.collections4.MapUtils#invertMap(Map)
     * @since 1.2.2
     */
    public static <K, V> Map<V, K> invertMap(Map<K, V> map) {
        return MapUtils.invertMap(map);// HashMap
    }

    /**
     * ? <code>map</code>keykey,? <code>map</code> value<code>extractPropertyName</code>,?map.
     * 
     * <h3>:</h3>
     * <blockquote>
     * <ol>
     * <li>?,<code>map</code>? <code>includeKeys</code>, warn log</li>
     * <li>map?,? map key?</li>
     * </ol>
     * </blockquote>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <Long, User>} map = new LinkedHashMap{@code <Long, User>}();
     * map.put(1L, new User(100L));
     * map.put(2L, new User(200L));
     * map.put(5L, new User(500L));
     * map.put(4L, new User(400L));
     * 
     * LOGGER.debug(JsonUtil.format(MapUtil.extractSubMap(map, "id")));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
    {
    "1": 100,
    "2": 200,
    "5": 500,
    "4": 400
    }
     * </pre>
     * 
     * </blockquote>
     * 
     * @param <K>
     *            the key type
     * @param <O>
     *            the generic type
     * @param <V>
     *            the generic type
     * @param map
     *            the map
     * @param extractPropertyName
     *            O??,Possibly indexed and/or nested name of the property to be modified,??
     *            <a href="../bean/BeanUtil.html#propertyName">propertyName</a>
     * @return  <code>map</code> nullempty, {@link Collections#emptyMap()}<br>
     *          <code>extractPropertyName</code> null, {@link NullPointerException}<br>
     *          <code>extractPropertyName</code> blank, {@link IllegalArgumentException}<br>
     * @since 1.8.0 remove class param
     */
    public static <K, O, V> Map<K, V> extractSubMap(Map<K, O> map, String extractPropertyName) {
        return extractSubMap(map, null, extractPropertyName);
    }

    /**
     * ? <code>map</code>keykey,? <code>map</code>value<code>extractPropertyName</code>
     * ,?map.
     * 
     * <h3>:</h3>
     * <blockquote>
     * <ol>
     * <li>key? <code>includeKeys</code></li>
     * <li>?,<code>map</code>? <code>includeKeys</code>, warn log</li>
     * <li>? <code>includeKeys</code>null,?mapkey</li>
     * <li>map?,?includeKeys?(includeKeysnull,map key?)</li>
     * </ol>
     * </blockquote>
     * 
     * <h3>:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <Long, User>} map = new LinkedHashMap{@code <Long, User>}();
     * map.put(1L, new User(100L));
     * map.put(2L, new User(200L));
     * map.put(53L, new User(300L));
     * map.put(5L, new User(500L));
     * map.put(6L, new User(600L));
     * map.put(4L, new User(400L));
     * 
     * Long[] includeKeys = { 5L, 4L };
     * LOGGER.debug(JsonUtil.format(MapUtil.extractSubMap(map, includeKeys, "id")));
     * </pre>
     * 
     * <b>:</b>
     * 
     * <pre class="code">
     * {
     * "5": 500,
     * "4": 400
     * }
     * </pre>
     * 
     * </blockquote>
     * 
     * <h3>:</h3>
     * 
     * <blockquote>
     * 
     * <pre class="code">
     * 
     * private Map{@code <Long, Long>} constructPropertyIdAndItemPropertiesIdMap(
     *                 String properties,
     *                 Map{@code <Long, PropertyValueSubViewCommand>} itemPropertiesIdAndPropertyValueSubViewCommandMap){
     *     Long[] itemPropertiesIds = StoCommonUtil.toItemPropertiesIdLongs(properties);
     * 
     *     Map{@code <Long, Long>} itemPropertiesIdAndPropertyIdMap = MapUtil
     *                     .extractSubMap(itemPropertiesIdAndPropertyValueSubViewCommandMap, itemPropertiesIds, "propertyId");
     * 
     *     return MapUtil.invertMap(itemPropertiesIdAndPropertyIdMap);
     * }
     * 
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param <O>
     *            the generic type
     * @param <V>
     *            the value type
     * @param map
     *            the map
     * @param includeKeys
     *            the include keys
     * @param extractPropertyName
     *            O??,Possibly indexed and/or nested name of the property to be modified,??
     *            <a href="../bean/BeanUtil.html#propertyName">propertyName</a>
     * @return  <code>map</code> nullempty, {@link Collections#emptyMap()}<br>
     *          <code>extractPropertyName</code> null, {@link NullPointerException}<br>
     *          <code>extractPropertyName</code> blank, {@link IllegalArgumentException}<br>
     *          <code>includeKeys</code> nullempty, then will extract map total keys<br>
     * @since 1.8.0 remove class param
     */
    public static <K, O, V> Map<K, V> extractSubMap(Map<K, O> map, K[] includeKeys, String extractPropertyName) {
        if (isNullOrEmpty(map)) {
            return Collections.emptyMap();
        }

        Validate.notBlank(extractPropertyName, "extractPropertyName can't be null/empty!");

        //excludeKeysnull,?key
        @SuppressWarnings("unchecked") // NOPMD - false positive for generics
        K[] useIncludeKeys = isNullOrEmpty(includeKeys) ? (K[]) map.keySet().toArray() : includeKeys;

        //???,??  includeKeys?
        Map<K, V> returnMap = newLinkedHashMap(useIncludeKeys.length);
        for (K key : useIncludeKeys) {
            if (map.containsKey(key)) {
                returnMap.put(key, PropertyUtil.<V>getProperty(map.get(key), extractPropertyName));
            } else {
                LOGGER.warn("map:[{}] don't contains key:[{}]", JsonUtil.format(map.keySet(), 0, 0), key);
            }
        }
        return returnMap;
    }

    //*************************************************************************************************

    /**
     * Creates a {@code HashMap} instance, with a high enough "initial capacity" that it <i>should</i> hold {@code expectedSize} elements
     * without growth.
     * This behavior cannot be broadly guaranteed, but it is observed to be true for OpenJDK 1.7. It also can't be guaranteed that the
     * method isn't inadvertently <i>oversizing</i> the returned map.
     * 
     * <h3>????:</h3>
     * <blockquote>
     * 
     * <pre class="code">
     * Map{@code <String, Map<Long, List<String>>>} map = new HashMap{@code <String, Map<Long, List<String>>>}(16);
     * </pre>
     * 
     * JDK1.7,? :
     * 
     * <pre class="code">
     * Map{@code <String, Map<Long, List<String>>>} map = new HashMap{@code <>}(16);
     * </pre>
     * 
     * ???1.5+,??:
     * 
     * <pre class="code">
     * Map{@code <String, Map<Long, List<String>>>} map = MapUtil.newHashMap(16);
     * </pre>
     * 
     * </blockquote>
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param expectedSize
     *            the number of entries you expect to add to the returned map
     * @return a new, empty {@code HashMap} with enough capacity to hold {@code expectedSize} entries without resizing
     * @see "com.google.common.collect.Maps#newHashMapWithExpectedSize(int)"
     * @see java.util.HashMap#HashMap(int)
     * @since 1.7.1
     */
    public static <K, V> HashMap<K, V> newHashMap(int expectedSize) {
        return new HashMap<K, V>(toInitialCapacity(expectedSize));
    }

    /**
     * Creates a {@code LinkedHashMap} instance, with a high enough "initial capacity" that it <i>should</i> hold {@code expectedSize}
     * elements without growth. This behavior cannot be broadly guaranteed, but it is observed to be true for OpenJDK 1.7. <br>
     * It also can't be guaranteed that the method isn't inadvertently <i>oversizing</i> the returned map.
     *
     * @param <K>
     *            the key type
     * @param <V>
     *            the value type
     * @param expectedSize
     *            the number of entries you expect to add to the returned map
     * @return a new, empty {@code LinkedHashMap} with enough capacity to hold {@code expectedSize} entries without resizing
     * @see "com.google.common.collect.Maps#newLinkedHashMapWithExpectedSize(int)"
     * @see java.util.LinkedHashMap#LinkedHashMap(int)
     * @since 1.7.1
     */
    public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(int expectedSize) {
        return new LinkedHashMap<K, V>(toInitialCapacity(expectedSize));
    }

    /**
     * <code>size</code>? <code>initialCapacity</code> (for {@link java.util.HashMap}).
     * 
     * <p>
     * ?? hashmap size,??
     * </p>
     *
     * @param size
     *            map size
     * @return the int
     * @see <a href="http://www.iteye.com/topic/1134016">java hashmap,?100,new HashMap(?),why </a>
     * @see <a href=
     *      "http://stackoverflow.com/questions/30220820/difference-between-new-hashmapint-and-guava-maps-newhashmapwithexpectedsizein">
     *      Difference between new HashMap(int) and guava Maps.newHashMapWithExpectedSize(int)</a>
     * @see <a href="http://stackoverflow.com/questions/15844035/best-hashmap-initial-capacity-while-indexing-a-list">Best HashMap initial
     *      capacity while indexing a List</a>
     * @see java.util.HashMap#HashMap(Map)
     * @see "com.google.common.collect.Maps#capacity(int)"
     * @see java.util.HashMap#inflateTable(int)
     * @see org.apache.commons.collections4.map.AbstractHashedMap#calculateNewCapacity(int)
     * @since 1.7.1
     */
    private static int toInitialCapacity(int size) {
        Validate.isTrue(size >= 0, "size :[%s] must >=0", size);

        // google guava ,? guava ????
        //guava 19 (int) (expectedSize / 0.75F + 1.0F)
        //guava 18  expectedSize + expectedSize / 3
        //google-collections 1.0  Math.max(expectedSize * 2, 16)

        //This is the calculation used in JDK8 to resize when a putAll happens it seems to be the most conservative calculation we can make.  
        return (int) (size / 0.75f) + 1;//0.75 is the default load factor
    }

}