数据统计分析中Treemap 应用实战(排序以及查找离指定key最近的key)

1. 应用场景


按天统计的逻辑利用hashmap进行存储,另外一篇文章已经提到。现在的问题是查询的交易记录表,如果当天没有交易记录,流量余额就是0,这个是不对的,应该是最近一笔交易记录时的流量余额。

1.必须有序

2.能够查询最近的一个key

此时hashmap不能满足要求,试试treemap,map有序的首选。查询了一下api,竟然支持,我利用的是lowerKey方法

Map.Entry<K,V> firstEntry()
Returns a key-value mapping associated with the least key in this map, or  null if the map is empty.
K firstKey()
Returns the first (lowest) key currently in this map.
Map.Entry<K,V> floorEntry(K key)
Returns a key-value mapping associated with the greatest key less than or equal to the given key, or  null if there is no such key.
K floorKey(K key)
Returns the greatest key less than or equal to the given key, or  null if there is no such key.
Map.Entry<K,V> higherEntry(K key)
Returns a key-value mapping associated with the least key strictly greater than the given key, or  null if there is no such key.
K higherKey(K key)
Returns the least key strictly greater than the given key, or  null if there is no such key.
Set<K> keySet()
Returns a  Set view of the keys contained in this map.
Map.Entry<K,V> lastEntry()
Returns a key-value mapping associated with the greatest key in this map, or  null if the map is empty.
K lastKey()
Returns the last (highest) key currently in this map.
Map.Entry<K,V> lowerEntry(K key)
Returns a key-value mapping associated with the greatest key strictly less than the given key, or  null if there is no such key.
K lowerKey(K key)
Returns the greatest key strictly less than the given key, or  null if there is no such key.

2. 如何运用

  private Map<String, BigDecimal> getRemainingFlux(String channelFlag, Date startTime, Date endTime) {
        TreeMap<String, BigDecimal> remainingFluxMap = new TreeMap<>((o1, o2) -> o1.compareTo(o2));
        AccountInfoExample accountInfoExample = new AccountInfoExample();
        accountInfoExample.createCriteria().andDownChannelFlagEqualTo(channelFlag);
        List<AccountInfo> accountInfoList = accountInfoMapper.selectByExample(accountInfoExample);
        Long accountId = 0L;
        if (CollectionUtils.isNotEmpty(accountInfoList)) {
            accountId = accountInfoList.get(0).getId();
        }
        RechargeInfoExample rechargeInfoExample = new RechargeInfoExample();
        rechargeInfoExample.createCriteria().andAccountIdEqualTo(accountId);
        rechargeInfoExample.setOrderByClause("create_time asc");
        List<RechargeInfo> rechargeInfoList = rechargeInfoMapper.selectByExample(rechargeInfoExample);
        if (CollectionUtils.isNotEmpty(rechargeInfoList)) {
            rechargeInfoList.forEach(rechargeInfo -> {
                String date = DateUtils.dateToStr(rechargeInfo.getCreateTime(), 11);
                BigDecimal accountBalance = rechargeInfo.getAccountBalance();
                remainingFluxMap.put(date, accountBalance);
            });
            String minDateString = remainingFluxMap.firstKey();
            int gapDays = DateUtils.getDaysBetweenDates(endTime, startTime);
            for (int i = 0; i <=gapDays; i++) {
                Date currentDay = DateUtils.addDay(startTime, i);
                String currentDayStr = DateUtils.dateToStr(DateUtils.getDateIgnoreTime(currentDay), 11);
                if (remainingFluxMap.get(currentDayStr) == null) {
                    //无交易记录情况下
                    Date minDate = null;
                    try {
                        minDate = DateUtils.getDateFromStr(minDateString);
                    } catch (Exception e) {
                    }
                    if (DateUtils.getDaysBetweenDates(currentDay, minDate) < 0) {
                        //小于最早充值记录日期,设置为0
                        remainingFluxMap.put(DateUtils.dateToStr(DateUtils.getDateIgnoreTime(currentDay), 11), new BigDecimal(0));
                    } else {
                        //寻找最近的一笔记录
                        Map.Entry<String, BigDecimal> availableChargeItemEntry = remainingFluxMap.lowerEntry(currentDayStr);
                        if (availableChargeItemEntry != null) {
                            remainingFluxMap.put(currentDayStr, availableChargeItemEntry.getValue());
                        }

                    }

                }

            }

        }
        return remainingFluxMap;
    }
1. lowerKey方法寻找的是小于指定key并且最大的一个,要注意很多文章说是上一个key,这个并不准确。指定的key不一定要在map中存在。比如我的指定key就是选择的日期中的某一天,都不在map中存在。

2. 比较器的简洁写法

TreeMap<String, BigDecimal> remainingFluxMap = new TreeMap<>((o1, o2) -> o1.compareTo(o2));

     arraylist没有构造方法设置比较器,可以利用下面的写法:

Comparator<ChannelRank> comparator=Comparator.comparing(ChannelRank::getRankScore).reversed();
            channelRankList.sort(comparator);

    或者

 Comparator<VoTransaction> comparator = (h1, h2) -> h2.getTime().compareTo(h1.getTime());

3.  如果声明为Map treeMap=new TreeMap(),则只有map的接口,没有lowerKey这些方法。因为treemap除了实现了map接口,还实现了NavigableMap接口

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
4. 除了比较器,顺便说一下list对filter功能,虽然本case并未运用

  List<ChannelDownStreamInfo> channelDownStreamInfoList = channelDownStreamInfoDbList.stream().filter(channelDownStreamInfoDb ->
                        channelDownStreamInfoDb.getChannelFlag().equalsIgnoreCase(channelDownStreamInfo.getChannelFlag()
                ) ).collect(Collectors.toList());
5. 补充知识点 lamda表达式的作用

只是使用一次的函数,不想去定义名字,也不想去写个复杂的函数结构,这个时候酒用lamda表达式

比如上面list的filter功能,如果不用lamda表达式,filter里面会实现predicted接口,如下:

 List<ChannelDownStreamInfo> channelDownStreamInfoList = channelDownStreamInfoDbList.stream().filter(new Predicate<ChannelDownStreamInfo>() {
                    @Override
                    public boolean test(ChannelDownStreamInfo channelDownStreamInfoDb) {
                        return channelDownStreamInfoDb.getChannelFlag().equalsIgnoreCase(channelDownStreamInfo.getChannelFlag());
                    }
                }).collect(Collectors.toList());
                channelStateDb = channelDownStreamInfoList.get(0).getChannelState();
            }
相比之下,lamda表达式简洁很多。






猜你喜欢

转载自blog.csdn.net/hanruikai/article/details/79209640