JAVA集合框架(四)

JAVA集合框架(四)

五. Map接口和实现类

1. Map集合概述

Interface Map
Interface SortedMap
Class TreeMap
Class HashMap

Map接口的特点:

  • 用于存储任意键值对(Key-Value)
  • 键:无序、无下标、不允许重复(唯一)
  • 值:无序、无下标、允许重复

方法:

  • V put(K key,V value) //将对象存入到集合中,关联键值。key重复则覆盖原值。
  • Object get (Object key) //根据键获取对应的值。
  • keySet //返回所有key。
  • Collection values() //返回包含所有值的Collection集合。
  • Set<Map. Entry<K, V>> //键值匹配的Set集合。

2. Map接口使用

以HashMap的实例:

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

public class Application {
    public static void main(String[] args) {
    //Map接口的使用
        //特点:(1)存储键值对(2)键不能重复,值可重复(3)无序
        Map<String, String> map = new HashMap<>();
    //添加元素
        map.put("cn","中国");
        map.put("uk","英国");
        map.put("us","美国");
        map.put("ca","加拿大");
        System.out.println("元素个数"+map.size());
        System.out.println(map);
    //删除
        map.remove("us");
        System.out.println(map);
    //遍历  1.keyset方法
        System.out.println("-----keyset方法-----");
        //Set<String> keyset = map.keySet();
        //for (String s : keyset) { 可缩写为:for (String s : map.keySet()) {
        for (String s : map.keySet()) {
            System.out.println(s+"-->"+map.get(s));
        }
        //使用entrySet方法   效率更高
        System.out.println("-----entrySet方法-----");
//      Set<Map.Entry<String, String>> entries = map.entrySet();
//      for (Map.Entry<String, String> entry : entries) {  可缩写为
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println(entry);
        }
    //判断
        System.out.println("含有键uk吗:"+map.containsKey("uk"));
        System.out.println("含有值 中国吗:"+map.containsValue("中国"));
        System.out.println("是否为空:"+map.isEmpty());
    }
}
---------------结果---------------
元素个数4
{uk=英国, cn=中国, us=美国, ca=加拿大}
{uk=英国, cn=中国, ca=加拿大}
-----keyset方法-----
uk-->英国
cn-->中国
ca-->加拿大
-----entrySet方法-----
uk=英国
cn=中国
ca=加拿大
含有键uk吗:true
含有值 中国吗:true
是否为空:false
Map集合的实现类
HashMap[重点]

JDK1. 2版本,线程不安全,运行效率快;允许用null 作为key或是value。

实例:

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

public class Application {
    public static void main(String[] args) {
        //HashMap集合的使用
        //存储结构:哈希表(数组+链表+红黑树)
        //使用key的hashcode和equals判断重复
        HashMap<Person, String> hashmap = new HashMap<>();
        //添加元素
        Person p1 = new Person("张三", 20);
        Person p2 = new Person("李四", 19);
        Person p3 = new Person("王五", 18);
        hashmap.put(p1,"北京");
        hashmap.put(p2,"上海");
        hashmap.put(p3,"张家口");
        hashmap.put(new Person("王五", 18),"石家庄");
        System.out.println("元素个数:"+hashmap.size());
        System.out.println(hashmap);
        //删除
        hashmap.remove(p3);
        System.out.println(hashmap);
        //遍历  1. keySet
        System.out.println("-----keySet方法-----");
        for (Person person : hashmap.keySet()) {
            System.out.println(person+"-->"+hashmap.get(person));
        }
        System.out.println("-----entrySet方法-----");
        Set<Map.Entry<Person, String>> entries = hashmap.entrySet();
        for (Map.Entry<Person, String> entry : entries) {
            System.out.println(entry.getKey()+"-->"+entry.getValue());
        }
        //判断
        System.out.println("包含键p1吗:"+hashmap.containsKey(p1));
        System.out.println("包含值张家口吗:"+hashmap.containsValue("张家口"));
        System.out.println("是否为空:"+hashmap.isEmpty());
    }
}
---------------结果---------------
元素个数:3
{[name=王五 age=18]=石家庄, [name=张三 age=20]=北京, [name=李四 age=19]=上海}
{[name=张三 age=20]=北京, [name=李四 age=19]=上海}
-----keySet方法-----
[name=张三 age=20]-->北京
[name=李四 age=19]-->上海
-----entrySet方法-----
[name=张三 age=20]-->北京
[name=李四 age=19]-->上海
包含键p1吗:true
包含值张家口吗:false
是否为空:false
import java.util.Objects;

public class Person {
    private String name;
    private int age;

    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 Person() {
    }

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

    @Override
    public String toString() {
        return "[name="+name+" age="+age+"]";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return getAge() == person.getAge() && getName().equals(person.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }
}
HashMap源码简单分析

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 -->默认初始大小=16 (1左移4位=2^4)
static final int MAXIMUM_CAPACITY = 1 << 30; --> 最大容量
static final float DEFAULT_LOAD_FACTOR = 0.75f; --> 默认加载因子 当添加元素的量大于容量的75%则扩容
static final int MIN_TREEIFY_CAPACITY = 64;–↓
static final int TREEIFY_THRESHOLD = 8;–>当数组长度大于64,链表长度大于8,则将该链表替换成红黑树,来提高运行效率
static final int UNTREEIFY_THRESHOLD = 6;–>当树的元素个数小于6时,将树调整回链表
transient Node<K,V>[ ] table; -->哈希表中的数组
size;–>元素个数

当创建hashmap之后没有添加元素,table=null,size=0。目的是为了节省空间。

//构造方法
//只有赋值加载因子,没有其他赋值
public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
//用来计算位置
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;//执行resize()后n=16,且没有元素
        if ((p = tab[i = (n - 1) & hash]) == null)//根据hash来找到位置
            tab[i] = newNode(hash, key, value, null);
        else {...}//暂时不解读
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)//当数据量达到当前容量的75%时,扩容为当前容量的二倍
            resize();
        afterNodeInsertion(evict);
        return null;
    }
//第一次执行的时候
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;//table=null;oldTab就=null;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;//给oldCap赋值0
        int oldThr = threshold;//threshold没有赋值=0,oldThr=0
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&   //扩容代码
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold  第一次执行也不满足
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;//给newCap赋值16
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//加载因子*默认初始大小=12
        }
        if (newThr == 0) {... }//不满足,也不用看
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;//把新建的newTabl赋值给table,也就是添加第一个元素时,大小初始化为16
        if (oldTab != null) {...}//后面的不分析
        }
        return newTab;
    }

总结:
(1)HashMap刚创建时,table是null,为了节省空间,当添加第一个元素是,table容量调整为16
(2)当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的个数。
(3) jdk1.8当每个链表长度大于8,并且元素个数大于等于64时,会调整为红黑树,目的提高执行效率
(4) jdk1.8当链表长度小于6时,调整成链表
(5)jdk1.8以前,链表时头插入,jdk1.8以后时是尾插入

HashSet其实用的是HashMap来实现的

HashTable(了解)

现已基本不用

  • JDK1. 0版本,线程安全,运行效率慢;不允许null作为key或是value。
Properties(以后讲)
  • Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。
TreeMap
  • 实现了SortedMap接口(是Map的子接口),可以对key自动排序。
import java.util.*;

public class Application {
    public static void main(String[] args) {
        //定制比较
        TreeMap<Person, String> treeMap = new TreeMap<>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                int n1=o1.getName().compareTo(o2.getName());
                int n2=o1.getAge()-o2.getAge();
                return n1==0?n2:n1;
            }
        });
        //添加元素
        Person p1 = new Person("张三", 20);
        Person p2 = new Person("李四", 20);
        Person p3 = new Person("王五", 20);
        treeMap.put(p1,"北京");
        treeMap.put(p2,"张家口");
        treeMap.put(p3,"石家庄");
        System.out.println("元素个数为:"+treeMap.size());
        System.out.println(treeMap);
        //删除
        treeMap.remove(p3);
        System.out.println(treeMap);
        //遍历
        System.out.println("-----keySet-----");
        for (Person person : treeMap.keySet()) {
//          System.out.println(person.getName()+"-->"+person.getAge());
            System.out.println(person+"-->"+treeMap.get(person));
        }
        System.out.println("-----entrySet-----");
        for (Map.Entry<Person, String> entry : treeMap.entrySet()) {
            System.out.println(entry);
        }
        //判断
        System.out.println(treeMap.containsKey(p2));
        System.out.println(treeMap.containsValue("张家口"));
        System.out.println(treeMap.isEmpty());
    }
}
---------------结果---------------
元素个数为:3
{[name=张三 age=20]=北京, [name=李四 age=20]=张家口, [name=王五 age=20]=石家庄}
{[name=张三 age=20]=北京, [name=李四 age=20]=张家口}
-----keySet-----
[name=张三 age=20]-->北京
[name=李四 age=20]-->张家口
-----entrySet-----
[name=张三 age=20]=北京
[name=李四 age=20]=张家口
true
true
false
import java.util.Objects;

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

    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 Person() {
    }

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

    @Override
    public String toString() {
        return "[name="+name+" age="+age+"]";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return getAge() == person.getAge() && getName().equals(person.getName());
    }

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

    @Override
    public int compareTo(Person o) {
        int n1=this.name.compareTo(o.getName());
        int n2=this.age-o.getAge();
        return n1==0?n2:n1;
    }
}
Colletions工具类
  • 概念:集合工具类,定义了除了存取以外的集合常用方法。

方法:

  • public static void reverse(List<?> list) //反转集合中元素的顺序
  • public static void shuffle(List<?> list) //随机重置集合元素的顺序
  • public static void sort(List list) //升序排序(元素类型必须实现
    Comparable接口)

实例:

import java.util.*;

public class Application {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(30);
        list.add(20);
        list.add(40);
        list.add(5);
        System.out.println("排序之前:"+list);
        Collections.sort(list);
        System.out.println("排序之后:"+list);
        //binarySearch  二分查找
        System.out.println("30所在的下标为:"+Collections.binarySearch(list, 30));
        //copy复制
        ArrayList<Integer> list1 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            list1.add(0);
        }
        Collections.copy(list1,list);
        System.out.println("复制后:"+list1);
        //reverse反转
        Collections.reverse(list1);
        System.out.println("反转后"+list1);
        //打乱
        Collections.shuffle(list);
        System.out.println("打乱后"+list);
        //补充:list转成数组
        Integer[] array = list.toArray(new Integer[6]);
        //new Integer[0]给的长度小于list长度时,长度随list,大于list时,随给的长度
        System.out.println("数组长度:"+array.length);
        System.out.println("集合变数组:"+Arrays.toString(array));
        //数组转成集合-->受限集合:不能添加和删除
        String[] str={"张三","李四","王五"};
        List<String> list2 = Arrays.asList(str);
        System.out.println("数组变集合:"+list2);
        //把基本类型数组转成集合时,需要修改为包装类
        int[] num={10,20,30,40};
        List<int[]> ints = Arrays.asList(num);//类型为int[]
        System.out.println("错误示范:"+ints);
        //应该改成:
        Integer[] num2={10,20,30,40};
        List<Integer> list3 = Arrays.asList(num2);
        System.out.println("list3:"+list3);
    }
}
---------------结果---------------
排序之前:[30, 20, 40, 5]
排序之后:[5, 20, 30, 40]
30所在的下标为:2
复制后:[5, 20, 30, 40]
反转后[40, 30, 20, 5]
打乱后[5, 40, 20, 30]
数组长度:6
集合变数组:[5, 40, 20, 30, null, null]
数组变集合:[张三, 李四, 王五]
错误示范:[[I@2503dbd3]
list3:[10, 20, 30, 40]

集合总结

集合的概念:

  • 对象的容器,和数组类似,定义了对多个对象进行操作的常用方法。

List集合:

  • 有序、有下标、元素可以重复。 (ArrayList、 LinkedList、 Vector)

Set集合:

  • 无序、无下标、元素不可重复。(HashSet、 TreeSet)

Map集合:

  • 存储一对数据,无序、无下标,键不可重复,值可重复。(HashMap、 HashTable、 TreeMap )

Collections:

  • 集合工具类,定义了除了存取以外的集合常用方法。

另几篇链接:
JAVA集合框架(一)链接
JAVA集合框架(二)链接
JAVA集合框架(三)链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值