• 一些常用的集合工具的代码块(缓慢更新XD,更新了多属性过滤:) )



    更新记录

    虽然经常放鸽子,但是还是要记录一下更新
    2017.8.30 更新了listToMap的方法,现在可以指定多个属性进行分组了,例如你要指定一个学生集合,按照名字和年龄相同的放在一组,现在只要调用listToMap(list,"name","age")就好啦 ^_^

    鱼的记忆

      我发现在项目中常常要用到一些集合的处理,不同的项目我经常会编写自己的集合工具代码块,后来我发现我总是在写一样的代码块(可能是我记性不好吧:),毕竟鱼的记忆只有7秒),所以我意识到了是时候将这些代码块做一些整理,这样以后直接调用就好了。然后我发现markdown很多不错的功能我好像都没用过,以后多试试其他功能。
      以后会不断的更新哒,免得总是做重复的无用功。

    List to Map

       list 转 map 这个还是挺常用的,在java8下可以用收集器很容易就做到,java7的话使用googleguava好像也挺不错,不过我实在不喜欢匿名内部类的写法,所以索性就自己写个啦。so let's start

            //小明和小红的一些考试成绩,该类的属性分别是[id,学生姓名,考试科目,考试成绩]
            List<StudentScore> studentList = Arrays.asList(
                    new StudentScore(1,"小明","语文",85),
                    new StudentScore(2,"小明","数学",90),
                    new StudentScore(3,"小明","英语",80),
                    new StudentScore(4,"小红","语文",80),
                    new StudentScore(5,"小红","数学",80),
                    new StudentScore(6,"小红","英语",80));
            //现在要将集合转换成为 学生姓名 ->[科目] 的map集合
            //例如[小明]->[语文,数学,英语] 这样的map集合,该怎么办咧
            
    
    

    Java8 Ways

      这种情况在做一些集合处理数据的时候有时会出现,那么首先是java8下的写法

            //转换(以下代码静态导入了java.util.stream.Collectors下的所有静态方法)
            Map<String, List<String>> stuMap = studentScoreList.stream()
            .collect(groupingBy(StudentScore::getStudentName
            , mapping(StudentScore::getSubjectName, toList())));
            //输出
            stuMap.forEach((s, strings) -> System.out.println("key: " + s +"	 value: " + strings));
            
    
    输出结果:
    key: 小明	 value: [语文, 数学, 英语]
    key: 小红	 value: [语文, 数学, 英语]
    
    

       java8在前面的博客已经有介绍了,这里简单说一下,代码第三行将集合按照学生姓名进行了分组,第4行使用了下游收集器,将学生的考试科目名称进行了收集,并且是以list的集合形式收集的,因此就做到了以上的输出效果。
    不得不承认,java8的流操作包办了几乎一切集合的操作,确实方便,那么在java7中该怎么做呢,我自己写了工具方法,java7环境要转换的话直接调用就好啦。

    Java7 Ways

            //转换,三个参数分别是[要转换的集合,作为key值的属性名,作为value值的属性名]
            Map<String, List<String>> stuMapJava7 = CollectionUtils.listToMap(studentScoreList, "studentName", "subjectName");
            for (Map.Entry<String, List<String>> entry : stuMapJava7.entrySet()) {
                System.out.println("key: " + entry.getKey() +"	 value: " + entry.getValue());
            }
    
    输出结果:
    key: 小明	 value: [语文, 数学, 英语]
    key: 小红	 value: [语文, 数学, 英语]
    
    

      这里我提供了两个重载方法,一个就是上面演示的三个参数的分别是[要转换的集合,作为key值的属性名,作为value值的属性名],另一个方法提供两个参数分别是[要转的集合,作为key值的属性名],另外一个方法的value值就是对象本身了。下面是代码,方法中我认为比较巧妙的一点是通过对list集合地址内容的修改来完成相关集合的生成。

      /**
           * 该方法用于list转map的重载方法,可自定义map映射的属性值 by LDF
           * @param list            用于转换的初始集合list
           * @param key             用于分组的key值,key值可以不唯一,不唯一的话类似于数据库的groupBy操作进行分组
           * @param valueProperName value值的属性名
           * @param <T>             初始集合list中的对象的泛型
           * @param <K>             转换后map集合的value值的泛型
           * @return 形如 key -> [valueProperName] 的map集合
           */
          public static <T, K> Map<String, List<K>> listToMap(List<T> list, String key, String valueProperName) {
                Map<String, List<K>> returnMap = new HashMap<>();
                try {
                      for (T t : list) {
                            Field name = t.getClass().getDeclaredField(key);//通过反射获得私有属性,这里捕获获取不到属性异常
                            name.setAccessible(true);//获得访问和修改私有属性的权限
                            String keyName = name.get(t).toString();//获得key值
                            List<K> tempList = returnMap.get(keyName);
                            if (tempList == null) {
                                  tempList = new ArrayList<>();
                                  Field field = t.getClass().getDeclaredField(valueProperName);//同上,通过反射拿到私有属性
                                  field.setAccessible(true);
                                  K k = (K) field.get(t);//强转,这里抛出转换异常
                                  tempList.add(k);//这里的添加已经同步影响到map集合了,因为引用的是地址
                                  returnMap.put(keyName, tempList);
                            } else {
                                  Field field = t.getClass().getDeclaredField(valueProperName);
                                  field.setAccessible(true);
                                  K k = (K) field.get(t);
                                  tempList.add(k);//这里的添加已经同步影响到map集合了,因为引用的是地址
                            }
                      }
                } catch (NoSuchFieldException | IllegalAccessException e) {
                      e.printStackTrace();
                }
                return returnMap;
          }
          
            /**
         * <h1 style="color:#007979"> 根据多属性进行list转map分组</h1>
         *
         * @param list    要转换的集合 by LDF
         * @param strings 作为key的属性名,这里可以指定多个属性哦,用逗号分开就可以了,例如要指定名字和年龄都相同的为一组(假设你要转换的集合叫list)参数这里就 填写(list, "name", "age")
         * @param <T>     集合里对象的泛型
         * @return
         */
        public static <T> Map<String, List<T>> listToMap(List<T> list, String... strings) {
            Map<String, List<T>> returnMap = new HashMap<>();
            try {
                for (T t : list) {
                    StringBuffer stringBuffer = new StringBuffer();
                    for (String s : strings) {
                        Field name1 = t.getClass().getDeclaredField(s);//通过反射获得私有属性,这里捕获获取不到属性异常
                        name1.setAccessible(true);//获得访问和修改私有属性的权限
                        String key = name1.get(t).toString();//获得key值
                        stringBuffer.append(key);
                    }
                    String KeyName = stringBuffer.toString();
    
                    List<T> tempList = returnMap.get(KeyName);
                    if (tempList == null) {
                        tempList = new ArrayList<>();
                        tempList.add(t);
                        returnMap.put(KeyName, tempList);
                    } else {
                        tempList.add(t);
                    }
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return returnMap;
        }
    

    filter 过滤

    • java8Ways
            List<StudentScore> filterStuListJava8 = studentScoreList
                    .stream()
                    .filter(s -> s.getScore()>=85)
                    .collect(toList());
            System.out.println(filterStuListJava8);
    

    输出结果

    [StudentScore{id=1, studentName='小明', subjectName='语文', score=85},
     StudentScore{id=2, studentName='小明', subjectName='数学', score=90}]
    

    声明式的链式代码看的确实很爽,那么在java7该如何实现呢

    • java7Ways
      filter两个参数分别是[需要过滤的集合,过滤条件],返回[过滤后的集合]
            List<StudentScore> filterStuListMy = filter(studentScoreList, new IPredicate<StudentScore>() {
                @Override
                public boolean apply(StudentScore studentScore) {
                    return studentScore.getScore() >= 85;
                }
            });
            System.out.println(filterStuListMy);
    

    输出结果

    [StudentScore{id=1, studentName='小明', subjectName='语文', score=85},
     StudentScore{id=2, studentName='小明', subjectName='数学', score=90}]
    

    那么是如何实现这样的效果的咧
    依旧是写了辅助类来实现声明式的效果
    首先是一个函数接口

    //@FunctionalInterface(java7 中没有该注释)
    public interface IPredicate<T> {
        boolean apply(T t);
    }
    

    然后是一个实现方法

       /**
         * 该方法接受一个需要过滤的集合和一个过滤条件(通过重写接口的apply方法来定义条件) by LDF
         * @param t 需要过滤的集合
         * @param iPredicate 过滤的条件
         * @param <T> 集合的泛型类
         * @return 过滤后的集合
         */
        public static <T> List<T> filter(List<T> t, IPredicate<? super T> iPredicate){
    
            List<T> returnList = new ArrayList<>();
            for (T t1 : t) {
                if (iPredicate.apply(t1)) {
                    returnList.add(t1);
                }
            }
            return returnList;
        }
    

    ps:你还可以使用google的guava集合类库完成过滤操作,操作方法十分相似,区别是guava的返回值为Collection,而自定义的方法就比较灵活了

    带条件的distinc

    java8流操作的已经包含了distinc了,但是该distinc是不带条件的,如果想要根据集合的某一个属性来去重,该怎么办咧?代码如下
    例如要根据学生姓名来去重

            //去重
            studentScoreList.stream()
                    .filter(distinctByKey(StudentScore::getStudentName))
                    .forEach(System.out::println);
    

    去重方法如下,利用一个map集合将第一次看见的值放入,并标记为true,然后用该map里的值与Null作对比(因为如果是null的话就证明这条数据没出现过,因此是非重复的数据,可以通过过滤)

    备注:该方法来自于Oracle的Stuart Marks(java8的开发者)与 Tagir Valeev(IntelliJ IDEA的开发者)的修正

        /**
         * 该方法根据集合中对象的某一个属性进行去重
         * @param keyExtractor 去重的条件属性
         * @param <T>
         * @return
         */
        public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
            Map<Object,Boolean> seen = new ConcurrentHashMap<>();
            return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;}
    

    tip:写例子的时候有时候发现function或者Predicate的接口参数编译器总是不通过,找了半天发现原来是guava包里的两个弄混了(因为是同名),这点大家还是要注意一哈:)

  • 相关阅读:
    java设计模式----代理模式
    其他技术----nginx开光
    Less的使用
    C++ 引用和指针
    leetcode 220 Contains Duplicate
    python网络数据采集1
    404
    前端知识点
    tcl自动生成fifo empty checker
    漫话:如何给女朋友解释什么是"大案牍术"?
  • 原文地址:https://www.cnblogs.com/invoker-/p/7399549.html
Copyright © 2020-2023  润新知