集合-----Map接口

  • Map是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承 Collection

  • Map是一个key对应一个value,所以它不能存在相同的 key ,当然value值可以相同

  • Map接口提供了重要的针对键、值进行操作的接口方法

1.1HashMap

特点:

  • 1.底层实现1.7之前:数组+链表 1.8以后:数组+链表+红黑树

  • 2.key不允许重复,如果key的值相同,后添加的数据会覆盖之前的数据

  • 3.HashMap是非线程安全的,允许存放null键,null值。

案例:

package Map;

import set.Teacher;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapTest {
    public static void main(String[] args) {
        //创建Hashmap对象
        Map<String, Teacher> maps=new HashMap<>();
        //创建teacher对象
        Teacher t1=new Teacher("唐僧",25,100);
        Teacher t2=new Teacher("孙悟空",3000,80);
        Teacher t3=new Teacher("猪八戒",2000,60);
        Teacher t4=new Teacher("沙悟净",2000,50);
        Teacher t5=new Teacher("沙悟净1",2000,50);
        //Teacher对象添加到HashMap中
        maps.put("唐僧",t1);
//        maps.put("孙悟空",t1);
        maps.put("孙悟空",t2);
        maps.put("猪八戒",t3);
        maps.put("沙悟净",t4);
        maps.put("沙悟净",t5);
        System.out.println("maps的数量为:"+maps.size());
        System.out.println("maps的containsKey:"+maps.containsKey("孙悟空"));
        System.out.println("maps的containsValue:"+maps.containsValue(t2));
        Teacher t0=maps.get("猪八戒");
        System.out.println("maps的get方法,根据key获取value的值"+t0);
        System.out.println("-----------遍历key--------------");
        Set<String> keys=maps.keySet();
        for (String key:keys){
            System.out.println(key);
        }//遍历为4是因为t4和t5的key值相等,当t5要存时,t4早它一步存储,就不存储t5的key
        System.out.println("===========遍历value----------");
        Collection<Teacher> values=maps.values();
        for (Teacher value:values){
            System.out.println(value);
        }//遍历是沙悟净1,因为t5和t4的key一样,put时t5在t4后,改变了value值
        System.out.println("根据key删除元素");
        Teacher tt=maps.remove("猪八戒");
        System.out.println("删除的元素为:"+tt);

        System.out.println("===========keyvalue对对象Entry----------");
        //用getKey(),getValue()
        Set<Map.Entry<String,Teacher>> entries=maps.entrySet();
        for (Map.Entry<String,Teacher>entry:entries){
            String key=entry.getKey();
            Teacher value=entry.getValue();
            System.out.println("key为:"+key+",value为:"+value);
        }
    }
}
package set;

public class Teacher implements  Comparable<Teacher>{
    private String name;
    private int age;
    private int level;

    public Teacher(String name, int age, int level) {
        this.name = name;
        this.age = age;
        this.level = level;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }


    //比较两个对象大小(通过年龄比大小)
    //大于0 当前对象大于传入的对象o,小于0 当前对象小于传入的对象o
    //等于0表示相等
    @Override
    public int compareTo(Teacher o) {
         return this.age-o.age;
    }
    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", level=" + level +
                '}';
    }
}

maps的数量为:4
maps的containsKey:true
maps的containsValue:true
maps的get方法,根据key获取value的值Teacher{name='猪八戒', age=2000, level=60}
-----------遍历key--------------
孙悟空
沙悟净
猪八戒
唐僧
===========遍历value----------
Teacher{name='孙悟空', age=3000, level=80}
Teacher{name='沙悟净1', age=2000, level=50}
Teacher{name='猪八戒', age=2000, level=60}
Teacher{name='唐僧', age=25, level=100}
根据key删除元素
删除的元素为:Teacher{name='猪八戒', age=2000, level=60}
===========keyvalue对对象Entry----------
key为:孙悟空,value为:Teacher{name='孙悟空', age=3000, level=80}
key为:沙悟净,value为:Teacher{name='沙悟净1', age=2000, level=50}
key为:唐僧,value为:Teacher{name='唐僧', age=25, level=100}

package Map;

import set.Student;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class HashMapTest2 {
    public static void main(String[] args) {
        Map<Student,Integer> maps= new HashMap<>();
        //Students类重写hashcode和equals,所有hashmap判断key是否相等,根据Student的hashcode和equals
        //创建Student对象
        Student s1=new Student("001","jack",20);
        Student s2=new Student("002","jim",22);
        Student s3=new Student("003","luck",23);
        Student s4=new Student("001","laowang",28);
        maps.put(s1,100);
        maps.put(s2,120);
        maps.put(s3,80);
        maps.put(s4,500);
        System.out.println("maps的数量是:"+maps.size());
        for (Map.Entry<Student,Integer> entry:maps.entrySet()){
            System.out.println(entry);
        }
        System.out.println("=================");
        //HashSet是重写的HashMap   key来判断不重复  value不包含
        Set<Student> sets=new HashSet<>();
        sets.add(s1);
        sets.add(s2);
        sets.add(s3);
        sets.add(s4);
        System.out.println("sets的数量是:"+sets.size());
        for (Student s:sets){
            System.out.println(s);
        }

    }
}
package set;

public class Student {
    private String no;
    private String name;
    private int age;

    public Student(String no, String name, int age) {
        this.no = no;
        this.name = name;
        this.age = age;
    }

    public String getNo() {
        return no;
    }

//    public void setNo(String no) {
//        this.no = no;
//    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "no='" + no + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //如果两个学生那个学号相同认定同一个学生
    //使用equals判断对象是否相等
    @Override
    public boolean equals(Object obj) {
        Student otherStu=(Student) obj;
        if (this.getNo()==null||otherStu.getNo()==null){
            return false;
        }
        return this.getNo().equals((otherStu.getNo()));
    }

    //重写hashCode 适用hashset和hashmap
    //equals相等hashcode必须相等,hashcode相等equals不一定相等
    @Override
    public int hashCode() {
        //只有相同的学号字符串相等,hashcode也相同
        //不同的字符串,hashcode有可能相同,不影响,因为hashset和hashmap
        //盘算hash相等之后判断equals
        return this.getNo().hashCode();

    }
}

数据结构:

java1.7 HashMap结构

大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色

的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next

  1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。

  2. loadFactor:负载因子,默认为 0.75。当前容器满时,当前容量*0.75

  3. threshold:扩容的阈值,等于 capacity * loadFactor

 java1.8 HashMap结构:

Java1.8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑 树 组成。

根据 Java1.7 HashMap 的介绍,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。为了降低这部分的开销,在 Java1.8 中,当链表中的元素超过了 8 个以后, 会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。

  • 如何判断key是否相同:第一步计算key的hashcode是否想相同,如果不同,就认为key不相同,如果相同进行第二步判断,判断key的equals是否为true,如果为false就是认为key不相同,如果为true就认为key相同

  • 所以我们重写hashcode和equals的原则,hashcode相同,equals不一定相等,但是equals相等,hashcode必须相同

重点:hashmap()中数据存放位置和hashcode()相关,当存储数据时,它会通过底层的hashcode(),得出它的哈希值,hash值对当前长度取余,余数就是数据存放在hashmap中的位置

 1.2HashTable

  • Hashtable,它的操作接口和HashMap相同。

  • HashMap和HashTable的区别在于:

    • Hashtable是线程安全的,而HashMap是非线程安全的

    • Hashtable不允许空的键值对,而HashMap可以

    • Hashtable与HashMap都实现Map接口,但二个类的继承的父类不是同一个

 HashTable底层实现:数组+链表+红黑树

案例:

package Map;

import com.sun.javafx.collections.MappingChange;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.stream.Stream;

public class HashTableTest {
    public static void main(String[] args) {
        Hashtable<String,Integer> hashtable=new Hashtable<>();
//        hashtable.put(null,1);//运行报错,key不能是null,空指针
//        hashtable.put("a",null);运行报错,value不能是null,空指针
        HashMap<String,Integer> hashMap=new HashMap<>();
        hashMap.put(null,1);//key可以为null
        hashMap.put("jack",null);//value可以为null
        hashMap.put(null,null);
        //hashmap的key和value都能为null,因为key不能重复,无论存储key为null多少次,最终就有一个key为null,value会被覆盖
        for (Map.Entry<String,Integer>entry:hashMap.entrySet()){
            System.out.println(entry);
        }
    }
}

1.3ConcurrentHashMap

  • 特点:ConcurrentHashMap是线程安全并且高效的HashMap,比HashTable效率高的多

    key和value都不能为null

  • 常用方法:同HashMap

  • 数据结构:JDK8 数组+链表+红黑树,数组的结构可能是链表,也可能是红黑树,红黑树是为了提高查找效率。采用CAS+Synchronized保证线程安全。CAS表示原子操作,例如:i++不是原子操作。Synchronized:表示锁,多线程能够保证只有一个线程操作。

 ConcurrentHashMap比HashTable效率要高

案例:

package Map;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapTest {
    public static void main(String[] args) {
//        ConcurrentHashMap 的value和key都不能为null
        ConcurrentHashMap<String,Integer> concurrentHashMap=new ConcurrentHashMap<>();
        concurrentHashMap.put("jack",20);
        System.out.println("concurrentHashMap的数量为:"+concurrentHashMap.size());
        Set<Map.Entry<String,Integer>>entries=concurrentHashMap.entrySet();
        for (Map.Entry<String,Integer>entry:entries){
            System.out.println(entries);
        }
    }
}

 1.4LinkedHashMap

  • LinkedHashMap继承自HashMap,它主要是用链表实现来扩展HashMap类,HashMap中条目是没有顺序的,但是在LinkedHashMap中元素既可以按照它们插入的顺序排序,也可以按它们最后一次被访问的顺序排序

  • 保持Map中元素的插入顺序或者访问顺序,就使用LinkedHashMap

案例 :

package Map;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;

public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap<String,Integer> lhm=new LinkedHashMap<>();
        lhm.put("laowang",45);
        lhm.put("jack",23);
        lhm.put("jim",50);
        lhm.put("lucy",28);

        System.out.println("lhm的数量:"+lhm.size());
        Set<String> keys=lhm.keySet();
        Iterator<String> it=keys.iterator();
        while (it.hasNext()){
            String key=it.next();
            Integer value=lhm.get(key);
            System.out.println("key="+key+",value="+value);
        }

    }

}

 lhm的数量:4
key=laowang,value=45
key=jack,value=23
key=jim,value=50
key=lucy,value=28

案例二: 


将集合变量list中的重复元素去掉,保证如下list集合中添加顺序不变,不能使用循环遍历操作
ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("b");
        list.add("c");
        list.add("b");
        list.add("b");
        list.add("b");
        list.add("c");
        list.add("c");
        list.add("d");
        list.add("c");
        list.add("c");
        list.add("c");
        list.add("d");
        list.add("d");
        list.add("d");
        list.add("d");
        list.add("d");
单独定义方法去除重复,方法定义public void getSingle(ArrayList list);
不能修改变方法的名称返回值和参数
 
private static void getSingle(List<String> list) {
        //1,创建一个LinkedHashSet集合
        LinkedHashSet<String> lhs = new LinkedHashSet<>();
        //2,将List集合中所以的元素添加到LinkedHashSet集合中
        lhs.addAll(list);
        //3,将List集合中的元素清除
        list.clear();
        //4,将LinkedHashSet集合中元素添加到List集合中
        list.addAll(lhs);
    }

1.5TreeMap

  • TreeMap特点: 可以对Map集合中的元素进行排序。

    • 1.TreeMap基于红黑树数据结构的实现

    • 2.键可以使用Comparable或Comparator接口, 重写compareTo方法来排序

    • 3.自定义的类必须实现接口和重写方法,否则抛异

    • 4.Key值不允许重复,如果重复会把原有的value值覆盖。

  • 使用元素的自然顺序(字典顺序)进行排序:

    • 对象(本身具有比较功能的元素)进行排序。

    • 自定义对象(本身没有比较功能的素)进行排序(要进行比较那就让元素具有比较功能,

      那就要实现Comparable这个接口里compareTo的方法)

  • 使用比较器进行排序:

    • 定义一个类实现Comparator接口,覆盖compare方法,将类对象作为参数传递给TreeSet集合的构造方法

案例 :

package Map;

import com.sun.javafx.collections.MappingChange;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class TreeMapTest {
    public static void main(String[] args) {
        Product p1=new Product("苹果",20);
        Product p2=new Product("巧克力",18);
        Product p3=new Product("香蕉",25);
        //创建外部比较器对象
        ProductComparator comparator=new ProductComparator();
        //new TreeMap传入外部比较器对象
        TreeMap<Product,String>treeMap=new TreeMap<>(comparator);
        treeMap.put(p1,"我是苹果");
        treeMap.put(p2,"我是巧克力");
        treeMap.put(p3,"我是香蕉");
        Set<Map.Entry<Product, String>> entries = treeMap.entrySet();
        for (Map.Entry<Product, String> entry : entries) {
            System.out.println(entry);
        }
        System.out.println("=======================================");
        TreeSet<Product> treeSet = new TreeSet<>(comparator);
        treeSet.add(p1);
        treeSet.add(p2);
        treeSet.add(p3);
        for (Product p : treeSet) {
            System.out.println(p);
        }
    }

    }

使用外比较器(Comparator()) 

package Map;

import java.util.Comparator;

public class ProductComparator implements Comparator<Product>{
    //判断o1和噢对象大小的规则
    //正数 o1小于o2  负数o1大于o2  0 o1等于o2
    @Override
    public int compare(Product o1, Product o2) {
        if (o1.getPrice()<o2.getPrice()){
            return 1;
        }else if(o1.getPrice()>o2.getPrice()){
            return -1;
        }else{
            return 0;
        }
    }
}
package Map;

public class Product {
    private String name;
    private double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值