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
    评论
Java TreeMap可以按照value排序,可以通过实现Comparator接口来实现。具体步骤如下: 1. 创建一个实现Comparator接口的类,重写compare方法,比较两个value的大小。 2. 创建一个TreeMap对象,并将实现了Comparator接口的类作为参数传入。 3. 将需要排序的键值对添加到TreeMap中。 4. 使用entrySet()方法获取TreeMap中的所有键值对,并将其转换为List。 5. 使用Collections.sort()方法对List进行排序。 6. 遍历排序后的List,输出键值对。 示例代码如下: ``` import java.util.*; public class TreeMapSortByValue { public static void main(String[] args) { // 创建一个实现Comparator接口的类 ValueComparator vc = new ValueComparator(); // 创建一个TreeMap对象,并将实现了Comparator接口的类作为参数传入 TreeMap<String, Integer> map = new TreeMap<>(vc); // 将需要排序的键值对添加到TreeMap中 map.put("apple", 10); map.put("banana", 5); map.put("orange", 8); // 使用entrySet()方法获取TreeMap中的所有键值对,并将其转换为List List<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet()); // 使用Collections.sort()方法对List进行排序 Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() { @Override public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return o1.getValue().compareTo(o2.getValue()); } }); // 遍历排序后的List,输出键值对 for (Map.Entry<String, Integer> entry : list) { System.out.println(entry.getKey() + " : " + entry.getValue()); } } } // 实现Comparator接口的类 class ValueComparator implements Comparator<String> { Map<String, Integer> map = new HashMap<>(); public ValueComparator() {} public ValueComparator(Map<String, Integer> map) { this.map.putAll(map); } @Override public int compare(String s1, String s2) { if (map.get(s1) >= map.get(s2)) { return 1; } else { return -1; } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值