一個 key 能儲存多個 value 的 map --- 自定義的 MultiValueMap,實現 Map 介面

guile發表於2019-03-09

需要實現一個 key 能儲存多個 value 的 map,一個鍵可以對應多個值。
這樣多儲存一些 key 對應的 value,方便業務中使用。
比如,儲存使用者每個小時內登陸的次數,用這樣的 map ,就比在 HashMap 的 value 裡存 時間和登陸次數 拼接的字串更方便。
需要的效果是:
user1  [2019-03-09 22:00:00, 10次]
user2  [2019-03-09 22:00:00, 8次]

上網搜“一個 key 儲存多個 value 的 map”,發現有的部落格裡說可以用 IdentityHashMap。
可是我試了一下,發現沒實現我要的功能。

繼續搜,找到一種解決方法:https://www.cnblogs.com/jiadp/p/9335437.html
這篇部落格裡,作者將 LinkedHashMap 的 value 替換成 List<Object> 來實現這樣的功能。

只不過這篇部落格裡的 LinkedMultiValueMap 實現的是 interface MultiValueMap 這樣的介面,用起來稍微有些不方便。
我在這篇部落格的基礎上做了一些修改,讓它實現了常見的 Map 介面,用起來更方便一些。

我修改後程式碼如下:

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/** 一個 Key 儲存多個 Value 的 Map,一個鍵可以對應多個值
 *  將 LinkedHashMap 的 value 替換成 List<Object> 來實現這樣的功能
 */
public class MultiValueMap<K, V> implements Map<String, Object>{
	//
	private Map<String, List<Object>> sourceMap =
			new LinkedHashMap<String, List<Object>>();

	public MultiValueMap(){}

	//
	public void put(String key, List<Object> values){
		// 遍歷新增進來的 List 的 Value,呼叫上面的 put(K, V) 方法新增
		for(Object value : values){
			put(key, value);
		}
	}

	//
	public void set(String key, Object value){
		// 移除這個 Key,新增新的 Key-Value
		sourceMap.remove(key);

		put(key, value);
	}

	//
	public void set(String key, List<Object> values){
		// 移除 Key,新增 List<V>
		sourceMap.remove(key);

		put(key, values);
	}

	//
	public void set(Map<String, List<Object>> map){
		// 移除所有值,遍歷 Map 裡的所有值新增進來
		sourceMap.clear();

		sourceMap.putAll(map);
	}

	//
	public void clear(){
		sourceMap.clear();
	}

	//
	public Set<String> keySet(){
		return sourceMap.keySet();
	}

	//
	public List<Object> values(){
		// 建立一個臨時 List 儲存所有的 Value
		List<Object> allValues = new ArrayList<>();

		// 遍歷所有的Key的Value新增到臨時List
		Set<String> keySet = sourceMap.keySet();

		for(Object key : keySet){
			allValues.addAll(sourceMap.get(key));
		}

		return allValues;
	}

	//
	public List<Object> getValues(Object key){
		return sourceMap.get(key);
	}

	//
	public Object getValue(String key, int index){
		List<Object> values = sourceMap.get(key);

		if(values != null && index < values.size()){
			return values.get(index);
		}

		return null;
	}

	//
	public int size(){
		return sourceMap.size();
	}

	//
	public boolean isEmpty(){
		return sourceMap.isEmpty();
	}

	//
	public boolean containsKey(Object key){
		return sourceMap.containsKey(key);
	}

	//
	public Object put(String key, Object value){
		if(key != null){
			// 如果沒有就建立一個 List 並新增 Value
			if(!sourceMap.containsKey(key)){
				List<Object> list = new ArrayList<Object>();
				sourceMap.put(key, list);
			}

			boolean re = sourceMap.get(key).add(value);

			return re;
		}else{
			return false;
		}
	}

	//
	public Object get(Object key){
		return sourceMap.get(key);
	}

	//
	public Object remove(Object key){
		return sourceMap.remove(key);
	}

	//
	public void putAll(Map map){
		for(Object entry : map.entrySet()){
			Map.Entry<String, Object> en = (Map.Entry<String, Object>) entry;

			String key = en.getKey();
			Object value = en.getValue();

			if(value instanceof List){
				List<Object> values = (List<Object>) value;

				put(key, values);
			}else{
				put(key, value);
			}
		}
	}

	//
	public boolean containsValue(Object value){
		List<Object> values = values();

		if(value instanceof List){
			List<Object> aList = (List<Object>) value;

			// Returns true if this list contains all of the elements of the specified collection.
			return values.containsAll(aList);
		}else{
			return values.contains(value);
		}
	}

	//
	public Set entrySet(){
		return sourceMap.entrySet();
	}

	// ------------------------ 測試這個類 ------------------------------

	/** 獲得當前日期的字串,格式 yyyy-MM-dd HH:00:00 */
	public static String getStringTimeToHour(){
		SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:00:00");
		return fmt.format(new Date());
	}

	/** 生成一個隨機整數,範圍從 1 到 10   */
	public static int getRandomIntRangeFrom1To10(){
		int random = new Random().nextInt(10) + 1;
		return random;
	}

	// main
	public static void main(String[] a){
		String id1 = 123 + "";
		String id2 = 124 + "";
		String id3 = 125 + "";
		String id4 = 126 + "";

		Map<String, Object> map6 = new MultiValueMap<>();

		// 測試 put
		map6.put(id1, "Wade");
		map6.put(id1, getStringTimeToHour());
		map6.put(id1, getRandomIntRangeFrom1To10());

		map6.put(id2, "Ibrahimovic");
		map6.put(id2, getStringTimeToHour());
		map6.put(id2, getRandomIntRangeFrom1To10());

		map6.put(id3, "Dejokovic" );
		map6.put(id3, getStringTimeToHour());
		map6.put(id3, getRandomIntRangeFrom1To10());

		map6.put(id4, "Van Persie");
		map6.put(id4, getStringTimeToHour());
		map6.put(id4, getRandomIntRangeFrom1To10());

		// 測試 keySet。用 keySet 遍歷,並列印
		for(String key : map6.keySet()){ 
			List<Object> values = (List<Object>) map6.get(key);

			for(Object value : values){
				System.out.println(key + ": " + value);
			}

			System.out.println();
		}

		// 測試 putAll
		Map<String, Object> map = new HashMap<>();

		List<Object> valuesF = new ArrayList<>();
		String idF = "127";

		//map.put(idF, "Figo");
		//map6.putAll(map);

		valuesF.add("Figo");
		valuesF.add(getStringTimeToHour());
		valuesF.add(getRandomIntRangeFrom1To10());
		map.put(idF, valuesF);

		map6.putAll(map);

		// 測試 containsValue
		System.out.println( map6.containsValue("Figo"));
		System.out.println( map6.containsValue(valuesF));
		System.out.println();

		// 測試 entrySet。用 entrySet 遍歷,並列印
		for(Object entry : map6.entrySet()){
			Map.Entry<String, Object> en = (Map.Entry<String, Object>) entry;

			String key = en.getKey();
			Object value = en.getValue();

			System.out.println(key + "  " + value.toString());
		}

                System.out.println();
                
                // 測試 get
		System.out.println( map6.get( "127" ) );
	}

}

列印結果:

123: Wade
123: 2019-03-09 22:00:00
123: 10

124: Ibrahimovic
124: 2019-03-09 22:00:00
124: 8

125: Dejokovic
125: 2019-03-09 22:00:00
125: 8

126: Van Persie
126: 2019-03-09 22:00:00
126: 6

true
true

123  [Wade, 2019-03-09 22:00:00, 10]
124  [Ibrahimovic, 2019-03-09 22:00:00, 8]
125  [Dejokovic, 2019-03-09 22:00:00, 8]
126  [Van Persie, 2019-03-09 22:00:00, 6]
127  [Figo, 2019-03-09 22:00:00, 9]

[Figo, 2019-03-09 22:00:00, 9]

------------------------------------------------

如果這樣新增:

        String id211 = "212";

        List<Object> values2a = new ArrayList<>();
        values2a.add( "2019-3-1 01:00:00" );
        values2a.add( getRandomIntRangeFrom1To10() );
        map6.put( id211, values2a );

        List<Object> values2b = new ArrayList<>();
        values2b.add( "2019-3-1 02:00:00" );
        values2b.add( getRandomIntRangeFrom1To10() );
        map6.put( id211, values2b );

列印結果就是: 212  [[2019-3-1 01:00:00, 8], [2019-3-1 02:00:00, 3]]

可以起到儲存時間段和登陸次數的作用。

 

 

相關文章