hashcode原理、比较器、聚合操作

  • List查找的低效率
    假设在List中存放着无重复名称,没有顺序的2000000个Hero
    要把名字叫做“hero 1000000”的对象找出来
    List的做法是对每一个进行挨个遍历,直到找到名字叫做“hero 1000000”的英雄。
    最差的情况下,需要遍历和比较2000000次,才能找到对应的英雄。
    测试逻辑:
    1. 初始化2000000个对象到ArrayList中
    2. 打乱容器中的数据顺序
    3. 进行10次查询,统计每一次消耗的时间
    不同计算机的配置情况下,所花的事件是有区别的。 在本机上,花掉的时间大概是600毫秒左右

    package collection;
         
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
         
    import charactor.Hero;
         
    public class TestCollection {
        public static void main(String[] args) {
            List<Hero> heros = new ArrayList<Hero>();
                
            for (int j = 0; j < 2000000; j++) {
                Hero h = new Hero("Hero " + j);
                heros.add(h);
            }
                
            // 进行10次查找,观察大体的平均值
            for (int i = 0; i < 10; i++) {
                // 打乱heros中元素的顺序
                Collections.shuffle(heros);
                 
                long start = System.currentTimeMillis();
         
                String target = "Hero 1000000";
         
                for (Hero hero : heros) {
                    if (hero.name.equals(target)) {
                        System.out.println("找到了 hero!" );
                        break;
                    }
                }
                long end = System.currentTimeMillis();
                long elapsed = end - start;
                System.out.println("一共花了:" + elapsed + " 毫秒");
            }
                 
        }
    }
  • HashMap的性能表现
    使用HashMap 做同样的查找
    1. 初始化2000000个对象到HashMap中。
    2. 进行10次查询
    3. 统计每一次的查询消耗的时间
    可以观察到,几乎不花时间,花费的时间在1毫秒以内

    package collection;
      
    import java.util.HashMap;
      
    import charactor.Hero;
      
    public class TestCollection {
        public static void main(String[] args) {
              
            HashMap<String,Hero> heroMap = new HashMap<String,Hero>();
            for (int j = 0; j < 2000000; j++) {
                Hero h = new Hero("Hero " + j);
                heroMap.put(h.name, h);
            }
            System.out.println("数据准备完成");
      
            for (int i = 0; i < 10; i++) {
                long start = System.currentTimeMillis();
                  
                //查找名字是Hero 1000000的对象
                Hero target = heroMap.get("Hero 1000000");
                System.out.println("找到了 hero!" + target.name);
                  
                long end = System.currentTimeMillis();
                long elapsed = end - start;
                System.out.println("一共花了:" + elapsed + " 毫秒");
            }
      
        }
    }
  • HashMap原理与字典
    在展开HashMap原理的讲解之前,首先回忆一下大家初中和高中使用的汉英字典。


    比如要找一个单词对应的中文意思,假设单词是Lengendary,首先在目录找到Lengendary在第 555页。 


    然后,翻到第555页,这页不只一个单词,但是量已经很少了,逐一比较,很快就定位目标单词Lengendary。


    555相当于就是Lengendary对应的hashcode

  • 分析HashMap性能卓越的原因
    -----hashcode概念-----
    所有的对象,都有一个对应的hashcode(散列值)
    比如字符串“gareen”对应的是1001 (实际上不是,这里是方便理解,假设的值)
    比如字符串“temoo”对应的是1004
    比如字符串“db”对应的是1008
    比如字符串“annie”对应的也是1008

    -----保存数据-----
    准备一个数组,其长度是2000,并且设定特殊的hashcode算法,使得所有字符串对应的hashcode,都会落在0-1999之间
    要存放名字是"gareen"的英雄,就把该英雄和名称组成一个键值对,存放在数组的1001这个位置上
    要存放名字是"temoo"的英雄,就把该英雄存放在数组的1004这个位置上
    要存放名字是"db"的英雄,就把该英雄存放在数组的1008这个位置上
    要存放名字是"annie"的英雄,然而 "annie"的hashcode 1008对应的位置已经有db英雄了,那么就在这里创建一个链表,接在db英雄后面存放annie


    -----查找数据-----
    比如要查找gareen,首先计算"gareen"的hashcode是1001,根据1001这个下标,到数组中进行定位,(根据数组下标进行定位,是非常快速的) 发现1001这个位置就只有一个英雄,那么该英雄就是gareen.
    比如要查找annie,首先计算"annie"的hashcode是1008,根据1008这个下标,到数组中进行定位,发现1008这个位置有两个英雄,那么就对两个英雄的名字进行逐一比较(equals),因为此时需要比较的量就已经少很多了,很快也就可以找出目标英雄
    这就是使用hashmap进行查询,非常快原理。

    这是一种用空间换时间的思维方式
  • HashSet判断是否重复
    HashSet的数据是不能重复的,相同数据不能保存在一起,到底如何判断是否是重复的呢?
    根据HashSet和HashMap的关系,我们了解到因为HashSet没有自身的实现,而是里面封装了一个HashMap,所以本质上就是判断HashMap的key是否重复。


    再通过上一步的学习,key是否重复,是由两个步骤判断的:
    hashcode是否一样
    如果hashcode不一样,就是在不同的坑里,一定是不重复的
    如果hashcode一样,就是在同一个坑里,还需要进行equals比较
    如果equals一样,则是重复数据
    如果equals不一样,则是不同数据。



  • Comparator
    假设Hero有三个属性 name,hp,damage
    一个集合中放存放10个Hero,通过Collections.sort对这10个进行排序
    那么到底是hp小的放前面?还是damage小的放前面?Collections.sort也无法确定
    所以要指定到底按照哪种属性进行排序
    这里就需要提供一个Comparator给定如何进行两个对象之间的大小比较

    package charactor;
      
    public class Hero  {
        public String name;
        public float hp;
      
        public int damage;
      
        public Hero() {
      
        }
      
        public Hero(String name) {
     
            this.name = name;
        }
      
        public String toString() {
            return "Hero [name=" + name + ", hp=" + hp + ", damage=" + damage + "]\r\n";
        }
     
        public Hero(String name, int hp, int damage) {
            this.name = name;
            this.hp = hp;
            this.damage = damage;
        }
      
    }
    package collection;
         
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Random;
        
    import charactor.Hero;
         
    public class TestCollection {
        public static void main(String[] args) {
            Random r =new Random();
            List<Hero> heros = new ArrayList<Hero>();
                
            for (int i = 0; i < 10; i++) {
                //通过随机值实例化hero的hp和damage
                heros.add(new Hero("hero "+ i, r.nextInt(100), r.nextInt(100)));
            }
            System.out.println("初始化后的集合:");
            System.out.println(heros);
                
            //直接调用sort会出现编译错误,因为Hero有各种属性
            //到底按照哪种属性进行比较,Collections也不知道,不确定,所以没法排
            //Collections.sort(heros);
                
            //引入Comparator,指定比较的算法
            Comparator<Hero> c = new Comparator<Hero>() {
                @Override
                public int compare(Hero h1, Hero h2) {
                    //按照hp进行排序
                    if(h1.hp>=h2.hp)
                        return 1;  //正数表示h1比h2要大 
                    else
                        return -1;
                } // h1>h2 1 , h1 == h2 0 , h1 < h2 -1 这样就是升序 类似于compare比较 1 0 -1
    
            };
            Collections.sort(heros,c);
            System.out.println("按照血量排序后的集合:");
            System.out.println(heros);
        }
    }
  • Comparable
    使Hero类实现Comparable接口
    在类里面提供比较算法
    Collections.sort就有足够的信息进行排序了,也无需额外提供比较器Comparator

    结果:倒序
    package charactor;
        
    public class Hero implements Comparable<Hero>{
        public String name;
        public float hp;
           
        public int damage;
           
        public Hero(){
              
        }
          
        public Hero(String name) {
            this.name =name;
      
        }
          
        //初始化name,hp,damage的构造方法
        public Hero(String name,float hp, int damage) {
            this.name =name;
            this.hp = hp;
            this.damage = damage;
        }
      
        @Override
        public int compareTo(Hero anotherHero) {
            if(damage<anotherHero.damage) 
                return 1; 
            else
                return -1;
        }// damage < anotherHero.damage 1 , damage == anotherHero.damage , damage > anotherHero.damage -1 这样就是降序 类似于compare比较 1 0 -1
    
        @Override
        public String toString() {
            return "Hero [name=" + name + ", hp=" + hp + ", damage=" + damage + "]\r\n";
        }
          
    }
    package collection;
       
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Random;
      
    import charactor.Hero;
       
    public class TestCollection {
        public static void main(String[] args) {
            Random r =new Random();
            List<Hero> heros = new ArrayList<Hero>();
              
            for (int i = 0; i < 10; i++) {
                //通过随机值实例化hero的hp和damage
                heros.add(new Hero("hero "+ i, r.nextInt(100), r.nextInt(100)));
            }
              
            System.out.println("初始化后的集合");
            System.out.println(heros);
              
            //Hero类实现了接口Comparable,即自带比较信息。
            //Collections直接进行排序,无需额外的Comparator
            Collections.sort(heros);
            System.out.println("按照伤害高低排序后的集合");
            System.out.println(heros);
              
        }
    }
  • TreeSet
    默认情况下,TreeSet中的数据是从小到大排序的,不过TreeSet的构造方法支持传入一个Comparator
     
    public TreeSet(Comparator comparator) 
     
    通过这个构造方法创建一个TreeSet,使得其中的的数字是倒排序的

  • 聚合操作
    JDK8之后,引入了对集合的聚合操作,可以非常容易的遍历,筛选,比较集合中的元素。


    像这样:
     String name =heros
                .stream()
                .sorted((h1,h2)->h1.hp>h2.hp?-1:1)
                .skip(2)
                .map(h->h.getName())
                .findFirst()
                .get();

    但是要用好聚合,必须先掌握Lambda表达式

    package lambda;
      
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Random;
     
    import charactor.Hero;
      
    public class TestAggregate {
      
        public static void main(String[] args) {
            Random r = new Random();
            List<Hero> heros = new ArrayList<Hero>();
            for (int i = 0; i < 10; i++) {
                heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100)));
            }
     
            System.out.println("初始化集合后的数据 (最后一个数据重复):");
            System.out.println(heros);
             
            //传统方式
            Collections.sort(heros,new Comparator<Hero>() {
                @Override
                public int compare(Hero o1, Hero o2) {
                    return (int) (o2.hp-o1.hp);
                }
            });
             
            Hero hero = heros.get(2);
            System.out.println("通过传统方式找出来的hp第三高的英雄名称是:" + hero.name);
             
            //聚合方式
            String name =heros
                .stream()
                .sorted((h1,h2)->h1.hp>h2.hp?-1:1) //降序
                .skip(2)
                .map(h->h.getName())
                .findFirst()
                .get();
     
            System.out.println("通过聚合操作找出来的hp第三高的英雄名称是:" + name);
             
        }
    }













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值