Map详解

map的特点

Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。

需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。

map的体系

è¿éåå¾çæè¿°

代码:

public class mapDemo {
    public static void main(String[] args) {
        //创建Map对象
        Map<String, String> map = new HashMap<String,String>();       //数据采用的哈希表结构
        //给map中添加元素
        map.put("星期一", "Monday");
        map.put("星期二", "Tuesday");
        map.put("星期三", "Wednesday");
        map.put("星期日", "Sunday");
        //{星期二=Tuesday, 星期三=Wednesday, 星期日=Sunday, 星期一=Monday}
        System.out.println(map);

        //当给Map中添加元素,会返回key对应的原来的value值,若key没有对应的值,返回null
        System.out.println(map.put("星期一", "Mon")); // Monday
        //{星期二=Tuesday, 星期三=Wednesday, 星期日=Sunday, 星期一=Monday}
        System.out.println(map);

        //根据指定的key获取对应的value        Sunday
        System.out.println(map.get("星期日"));
        //根据key删除元素,会返回key对应的value值      Sunday
        System.out.println(map.remove("星期日"));
        //{星期二=Tuesday, 星期三=Wednesday, 星期一=Mon}
        System.out.println(map);
             //是否包含指定的key       true
        System.out.println(map.containsKey("星期三"));
        // //是否包含指定的Value       false
        System.out.println(map.containsValue("星期三"));
        //判断为空           false
        System.out.println(map.isEmpty());
         //返回map里面所有的value组成的collection      
        // Tuesday	Wednesday	Mon	
        Collection<String> values = map.values();
        for(String v:values){
            System.out.print(v+"\t");
        }
        System.out.println();
        //遍历1:  keySet()
        //返回map里面所有的key组成的collection      
        //星期二	星期三	星期一	
        Collection<String> keys = map.keySet();
        for(String v:keys){
            System.out.print(v+"\t");
        }
        System.out.println();
        Iterator<String> it =keys.iterator();
        //星期二=Tuesday
        //星期三=Wednesday
        //星期一=Mon
        while(it.hasNext()) {
            //得到每一个key
            String key = it.next();
            //通过key获取对应的value
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }
        //遍历2    .entrySet()
        //返回map所包含的键值所组成的set集合,每个集合元素都是map.entry对象
        //星期二=Tuesday	星期三=Wednesday	星期一=Mon	
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Iterator<Map.Entry<String, String>> iterator = entries.iterator();
        while(iterator.hasNext()){
            System.out.print(iterator.next()+"\t");
        }
        System.out.println();

          //遍历3
        for (Map.Entry<String, String> entry: map.entrySet()) {

            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key+"---"+value);
        }
    }
}

注意:Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。

 

HashMap的实现特点

1. HashMap和HashTable比较相似,区别是:

  • HashMap线程不安全
  • HashMap可以存储null键和null值
  • 由于线程不安全,所以相对的性能较高

2. HashMap存储不保证顺序存储
3. HashMap迭代整个集合的时间和容量及键值对有关,HashMap的一个实例有两个影响其性能的参数:初始容量和负载因子。
4. HashMap是以哈希桶方式存储的。
5. 其Iterator迭代器采用的是快速失败机制
6. 如果使用自定义类当作Key,需要注意其equals方法和hashCode方法
7. HashMap有线程安全版:ConcurrentHashMap

8.,jdk8:HashMap底层是(数组和链表 )散列表+红黑树,创建HashMap对象,没有初始table(类型是Node),仅仅只是加载因子,只有第一个添加的时候容量才变为16,当链表的节点>=8 && 桶的个数(table容量)>=64,会把链表结构变成红黑树。;jdk7:数组和链表 ,创建HashMap对象时,table(Entry)容量才变为16,不论链表的节点数是多少都不会变成树。

9.装载因子*初始容量<散列表元素,散列表扩容2倍;装载因子默认是0.75,装载因子初始值大了,可以减少扩容次数,但是散列冲突可能性变大;装载因子小了,散列冲突可能性变小,扩容次数增加;初始容量默认16,初始容量过大,遍历影响速度,过小扩容次数增多。

 

哈希桶

我们在利用哈希值存放数据时,会遇到冲突,常用的解决冲突的方法有:开放定址、链地址、溢出区,其中链地址就是哈希桶。
我们会将元素的哈希值设为桶,哈希值相同的元素放入一个桶中,就是如下图。根据哈希值和值找元素也是此步骤,找到哈希值的桶,如果桶中第一个元素是要找的则结束,否则继续找下一个元素,直至找到元素。

å¨è¿éæå¥å¾çæè¿°

上面讲的是Iterator迭代器的快速失败机制(fail-fast),这个不单单对于HashMap,所有Collection集合的Iterator迭代器都是快速失败,一旦在迭代过程中对集合进行结构化修改,除非是迭代器中的remove方法,其余都会抛出异常。因此遇到并发修改那么就会快速干净的失败,避免在未来的未确定时间发生非确定性行为的风险。并且需要注意,迭代器的快速失败机制行为无法得到保证,比如在有些情况,对迭代器中元素进行删除可能不会引发异常,但一定不要以为可以在迭代时进行删除元素。对于HashMap、ArrayList等集合,迭代时进行结构化操作都会导致异常,只要在操作特定元素时才不会抛出异常,如果想深入了解可以去查看一下这些集合的实现代码。

 

HashMap和HashTable的区别

 

TreeMap

è¿éåå¾çæè¿°

底层:红黑树。时间复杂度log(n)

实现了NavigableMap接口,而NavigableMap接口继承SortMap接口,所以TreeMap有序。

如果构造方法中传递了Comparator对象,那么就以Comparator对象的方法进行排序,否则,使用Comparable的compareTo(T o)。如果使用compareTo(T o)方法比较,key不能为null,并且实现Comparable接口,即使传入Comparator对象,不用compareTo(T o)方法比较,key也是不能为null。

非同步,想要同步使用Conllections进行封装。

代码示例:

public class TreeD {
    public static void main(String[] args) {
        TreeMap map=new TreeMap();
         map.put(new Yuan("科比", 100000, 24),100000);
        map.put(new Yuan("韦德", 50000, 3),50000);
        map.put(new Yuan("邓肯", 80000, 21),80000);
        map.put(new Yuan("麦迪", 60000, 1),60000);

        Set set = map.entrySet();
        for(Object obj : set){
            Map.Entry entry= (Map.Entry) obj;
            System.out.println(entry.getKey().toString());
        }

    }
}

------------------------------------------------------------------------------
public class Yuan implements Comparable<Yuan>{
    private String name;
    private int salay;
    private int id;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Yuan)) return false;
        Yuan yuan = (Yuan) o;
        return getSalay() == yuan.getSalay() &&
                getId() == yuan.getId() &&
                Objects.equals(getName(), yuan.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getSalay(), getId());
    }

    public Yuan(String name, int salay, int id) {
        this.name = name;
        this.salay = salay;
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getSalay() {
        return salay;
    }

    public void setSalay(int salay) {
        this.salay = salay;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Yuan{" +
                "name='" + name + '\'' +
                ", salay=" + salay +
                ", id=" + id +
                '}';
    }

    @Override
    public int compareTo(Yuan o) {
        if(this.id>o.id){
            return 1;
        }else if(this.id<o.id){
            return -1;
        }
        return Integer.compare(this.salay,o.salay);
    }
}

Map的其它类: 


IdentityHashMap和HashMap的具体区别,IdentityHashMap使用 == 判断两个key是否相等,而HashMap使用的是equals方法比较key值。有什么区别呢? 
对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等; 如果作用于引用类型的变量,则比较的是所指向的对象的地址。 
对于equals方法,注意:equals方法不能作用于基本数据类型的变量 
如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址; 
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。 

è¿éåå¾çæè¿°

 

 

(6)小结: 


HashMap 非线程安全 
HashMap:基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子

TreeMap:非线程安全基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态

适用场景分析: 
HashMap和HashTable:HashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKey()方法。HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。HashMap允许空键值,而HashTable不允许。

HashMap:适用于Map中插入、删除和定位元素。 
Treemap:适用于按自然顺序或自定义顺序遍历键(key)。

5.线程安全集合类与非线程安全集合类 
LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的; 
HashMap是非线程安全的,HashTable是线程安全的; 
StringBuilder是非线程安全的,StringBuffer是线程安全的。

数据结构 
ArrayXxx:底层数据结构是数组,查询快,增删慢 
LinkedXxx:底层数据结构是链表,查询慢,增删快 
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals() 
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序
 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值