TreeMap根据value排序遇到的问题及分析

1、前言

针对集合排序,我们通常都会借助具有排序功能的集合,来处理我们的数据。比如ArrayList,TreeMap等。但是使用不同的排序工具,可能会遇到不同的问题。

2、案例分析

 2.1 需求:根据HashMap中的数据,按照value排序。例如:

/*
排序之前的结果:
	key:value = a:3
	key:value = b:5
	key:value = c:1
	key:value = d:4
	key:value = e:2

要求输出结果为:
	key:value = c:1
	key:value = e:2
	key:value = a:3
	key:value = d:4
	key:value = b:5
*/
Map<String, Integer> map = new HashMap<>();
map.put("d", 4);
map.put("e", 2);
map.put("a", 3);
map.put("b", 5);
map.put("c", 1);

2.2 根据Map的特性,里面存储的是一个个kv结构的 Entry可以借助ArrayList的有序性,将 Entry 放到List中排序后取出。如下:

// 借助ArrayList排序
List<Map.Entry<String, Integer>> entryList = Lists.newArrayList(map.entrySet());
entryList.sort((o1, o2)->{return  o1.getValue()-o2.getValue();});
entryList.forEach(c -> System.out.println("key:value = " + c.getKey() + ":" + c.getValue()));

/*
排序结果:
    key:value = c:1
    key:value = e:2
    key:value = a:3
    key:value = d:4
    key:value = b:5
*/

2.3 Map中本身也具有排序的集合,如 TreeMap 。但是 TreeMap 传入比较器的参数默认都是Key作为入参,为了取出Value进行比较,需要将对象作为参数传进去,以获取Value值。需要我们定制比较器。如下:

public class MapValueComparator implements Comparator<String> {
	
	Map<String, Integer> map;
	
	public MapValueComparator(Map<String, Integer> map){
		this.map = map;
	}

	@Override
	public int compare(String o1, String o2) {
		return map.get(o1)-map.get(o2);
	}

}

通过 TreeMap 排序:

// 通过TreeMap排序
TreeMap<String, Integer> treeMap = new TreeMap<>(new MapValueComparator(map));
treeMap.putAll(map);
treeMap.forEach((k,v) -> System.out.println("key:value = " + k + ":" + v));
/*
排序结果:
    key:value = c:1
    key:value = e:2
    key:value = a:3
    key:value = d:4
    key:value = b:5
*/

2.4 以上两种排序方式均可以完成指定功能排序。但是如果Value重复,是否会还是保持原来的结果呢?如图:

public class TreeMapTest {
	
	@Test
	public void test01() {
		Map<String, Integer> map = new HashMap<>();
		map.put("d", 4);
		map.put("e", 2);
		map.put("a", 3);
		map.put("b", 5);
		map.put("c", 1);
		map.put("f", 2);
		
		System.out.println("------------自然顺序-------------------");
		map.forEach((k,v) -> System.out.println("key:value = " + k + ":" + v));
		
		// 方法1:借助List排序
		System.out.println("------------借助List排序-------------------");
		List<Map.Entry<String, Integer>> entryList = Lists.newArrayList(map.entrySet());
		entryList.sort((o1, o2)->{return  o1.getValue()-o2.getValue();});
		entryList.forEach(c -> System.out.println("key:value = " + c.getKey() + ":" + c.getValue()));
		
		
		// 方法2:借助treeMap按照value排序
		System.out.println("------------treeMap排序-------------------");
		TreeMap<String, Integer> treeMap = new TreeMap<>(new MapValueComparator(map));
		treeMap.putAll(map);
		treeMap.forEach((k,v) -> System.out.println("key:value = " + k + ":" + v));
		
		/*
		 * 执行结果:
		 * ------------自然顺序-------------------
			key:value = a:3
			key:value = b:5
			key:value = c:1
			key:value = d:4
			key:value = e:2
			key:value = f:2
			------------借助List排序-------------------
			key:value = c:1
			key:value = e:2
			key:value = f:2
			key:value = a:3
			key:value = d:4
			key:value = b:5
			------------treeMap排序-------------------
			key:value = c:1
			key:value = e:2
			key:value = a:3
			key:value = d:4
			key:value = b:5
		 * 
		 * 
		 * */
	}
}

从运行的结果来看。List的排序依然没有问题,但是TreeMap的结果 丢失数据 。我们来分析一下TreeMap为什么会丢失数据。

3、TreeMap 源码的分析追踪

 3.1 追踪 putAll() 方法:

3.2 追踪 put() 方法:

 

从源码中可以看到,put第一个值时,直接放入TreeMap中。第二个开始就会调用比较器compare方法。比较结果小于0就将当前的数据放在节点的左侧;如果大于0,就放在节点的右侧;等于0时,也就是比较的结果就会覆盖当前值。

这也就是比较器结果相同的时,为什么数据会被覆盖掉的原因。这里是Map集合的key不会重复,相同是就会覆盖的特性。

4、如何处理这种数据

只需要加在定义比较器的时候,不返回等于0的值,对于等于0的数据特殊处理即可。如下:

public class MapValueComparator implements Comparator<String> {

    private Map<String, Integer> map;

    public MapValueComparator(Map<String,Integer> map){
        this.map = map;
    }

    @Override
    public int compare(String o1, String o2) {
        int o1v = map.get(o1);
        int o2v = map.get(o2);
        // 特殊处理
        return o1v-o2v == 0 ? 1 : o1v-o2v;
    }
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值