详解Java-集合

详解Java-集合,第1张

目录

1. 集合的理解和好处:

1.1 数组(不足的地方,引出集合)

1.2 集合(好处)

2. 集合的框架体系

3.Collection接口和常用方法

3.1 Collection接口实现类的特点

3.2 Collection接口遍历元素方式 1-使用Iterator(迭代器)

3.3 Collection接口遍历对象方式2-for循环增强

4.List接口和常用方法

4.1 List接口基本介绍

4.2 List接口常用方法

4.3 List的三种遍历方式

 5 ArrayList底层结构和源码分析

5.1 ArrayList的注意事项

5.2 ArrayList的底层 *** 作机制源码分析

6. Vector底层结构和源码分析

 6.1 Vector基本介绍

6.2 Vector和ArrayList的比较

7 LinkedList底层介绍

7.1 LinkedList的全面说明

7.2 LinkedList的底层 *** 作机制

 7.3 LinkedList的增删改查案例

8. ArrayList和LinkedList比较

9.Set接口和常用方法

9.1 Set接口基本介绍

 9.2 Set接口的常用方法

9.3 Set接口的遍历方式

 9.4 Set接口的常用方法举例

10. Set接口实现类-Hashset

10.1 HashSet的全面说明

10.2 HashSet底层机制说明

11.Set接口实现类-LinkedHashSet

12.Map接口和常用方法

12.1Map接口实现类的特点(JDK-8)

12.2 Map接口常用方法

12.3 Map遍历方式

13. Map接口实现类-HashMap

13.1 HashMap介绍

13.2 HashMap底层机制

14. Map接口实现类-Hashtable

14.1Hashtable的基本介绍

14.2 HashMap和Hashtable对比

15.Map接口实现类-Properties

15.1 基本介绍

15.2 基本使用

16. 如何选择集合实现类


1. 集合的理解和好处: 1.1 数组(不足的地方,引出集合)
  • 长度开始时必须指定,而且一旦指定,不能更改
  • 保存的必须为同一类型的元素
  • 使用数组进行增加或删除 *** 作比较麻烦
1.2 集合(好处)
  • 可以动态保存任意对象
  • 提供了一系列方便的 *** 作对象的方法:add,remove,set,get等
  • 使用集合添加,删除新元素的示意代码简洁明了
2. 集合的框架体系

Java的集合类很多,主要分为两大类,这里只讲解常用的,如图:

3.Collection接口和常用方法 3.1 Collection接口实现类的特点
  • Collection实现子类可以存放多个元素,每个元素可以是Object
  • 有些Collection的实现类可以存放重复的元素,有些不可以
  • 有些Collection的实现类是有序的,有些是无序的
  • Collection接口没有直接实现的子类,是通过它的子接口Set和List来实现的

常用方法

3.2 Collection接口遍历元素方式 1-使用Iterator(迭代器)

基本介绍

  • Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
  • 所有实现了Collection接口的集合对象类都有一个Iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
  • Iterator仅用于遍历集合,Iterator本身并不存放对象

->迭代器的执行原理

  •  Iterator iterator = coll.iterator();//得到一个集合的迭代器
  • hasNext():判断是否还有下一个元素
  • while(Iterator.hasNext()){
  • //next()作用:1.下移 2.将下移以后集合位置上的元素返回
  • System.out.println(iterator.next)
  • }

->iterator接口的方法

 提示:在调用iterator方法之前必须要调用Iterator.hasNext进行检测。若不调用,下一条记录将会无效,直接调用it.next()会抛出NoSuchElementException异常

public class CollectionIterator {
    public static void main(String[] args) { 
        Collection col = new ArrayList(); 
        //Book是一个含有名字,作者,价格属性的类含有构造器和get set 方法,这里就不写出了
        col.add(new Book("三国演义", "罗贯中", 10.1)); 
        col.add(new Book("小李飞刀", "古龙", 5.1)); 
        col.add(new Book("红楼梦", "曹雪芹", 34.6)); 
        //现在希望能够遍历 col 集合 
        //1. 先得到 col 对应的 迭代器 
        Iterator iterator = col.iterator(); 
        //2. 使用 while 循环遍历 
        while (iterator.hasNext()) {//判断是否还有数据 
            //返回下一个元素,类型是 Object 
            Object obj = iterator.next(); 
            System.out.println("obj=" + obj); 
        } 
        //一个快捷键,快速生成 while => itit 
        //显示所有的快捷键的的快捷键 ctrl + j 
       
        //3. 当退出 while 循环后 , 这时 iterator 迭代器,指向最后的元素 
        // iterator.next();//NoSuchElementException 
        //4. 如果希望再次遍历,需要重置我们的迭代器 
        iterator = col.iterator(); 
        System.out.println("===第二次遍历==="); 
        while (iterator.hasNext()) { 
            Object obj = iterator.next(); 
            System.out.println("obj=" + obj); 
        } 
    } 
}

3.3 Collection接口遍历对象方式2-for循环增强
  • 增强for循环,可以代替Iterator迭代器,特点:增强for就是简化版的Iterator,本质一样,只能用于遍历集合或数组。
  • 基本语法:for(元素类型 :集合名或数组名){
  • 访问元素
  • }
//演示增强for循环的遍历方法
public class CollectionExercise { 
    public static void main(String[] args) { 
        List list = new ArrayList(); 
        list.add(new Dog("小黑", 3)); 
        list.add(new Dog("大黄", 100)); 
        list.add(new Dog("大壮", 8)); 
        //使用 for 增强 
        for (Object dog : list) { 
            System.out.println("dog=" + dog); 
        }
    }
}

class Dog { 
    private String name; 
    private int age; 
    public Dog(String name, int age) { 
        this.name = name; 
        this.age = 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; 
    } 
    @Override 
    public String toString() { 
        return "Dog{" + 
        "name='" + name + '\'' + 
        " age=" + age + 
    '}'; 
    } 
}

4.List接口和常用方法 4.1 List接口基本介绍
  • list接口是Collection接口的子接口
  • List集合类中元素有序(即添加顺序和取出顺序一致),且可重复
  • List容器中的每个元素都有对应的顺序索引,即支持索引
  • List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
  • List接口实现类有:
  • 常用的有ArrayList,LinkedList和Vector
4.2 List接口常用方法
public class ListMethod { 
    public static void main(String[] args) { 
        List list = new ArrayList(); 
        list.add("张三丰"); 
        list.add("贾宝玉");
        // void add(int index, Object ele):在 index 位置插入 ele 元素 
        //在 index = 1 的位置插入一个对象 
        list.add(1, "韩顺平"); 
        System.out.println("list=" + list); 
        // boolean addAll(int index, Collection eles):从 index 位置开始将 eles 中的所有元素添加进来 
        List list2 = new ArrayList(); 
        list2.add("jack"); 
        list2.add("tom"); 
        list.addAll(1, list2); 
        System.out.println("list=" + list); 
        // Object get(int index):获取指定 index 位置的元素  
        // int indexOf(Object obj):返回 obj 在集合中首次出现的位置 
        System.out.println(list.indexOf("tom"));//2 
        // int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置 
        list.add("汤姆"); 
        System.out.println("list=" + list); 
        System.out.println(list.lastIndexOf("汤姆")); 
        // Object remove(int index):移除指定 index 位置的元素,并返回此元素 
        list.remove(0); 
        System.out.println("list=" + list); 
        // Object set(int index, Object ele):设置指定 index 位置的元素为 ele , 相当于是替换.  
        list.set(1, "玛丽"); 
        System.out.println("list=" + list); 
        // List subList(int fromIndex, int toIndex):返回从 fromIndex 到 toIndex 位置的子集合 
        // 注意返回的子集合 fromIndex <= subList < toIndex
        List returnlist = list.subList(0, 2); 
        System.out.println("returnlist=" + returnlist); 
    } 
}

4.3 List的三种遍历方式

 5 ArrayList底层结构和源码分析 5.1 ArrayList的注意事项
  • permits all elements,including null,ArrayList 可以加入多个null,并且多个
  • ArrayList是由数组来实现数据存储的
  • ArrayList基本等同于Vector,ArrayList线程不安全,但执行效率高,在多线程情况下,不建议使用ArrayList

5.2 ArrayList的底层 *** 作机制源码分析
  • ArrayList中维护了一个Object类型的数组elementDate(可以Debug看源码)
  • transient Object[] elementDate; //transient 表示短暂的,瞬间,表示该属性不会被序列化
  • 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementDate容量为零,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementDate为1.5倍
  • 如果使用的是指定大小的构造器,则初始elementDate的容量为指定大小,如果需要扩容,则直接扩容elementDate为1.5倍

源码分析 

6. Vector底层结构和源码分析  6.1 Vector基本介绍
  • vector类的定义说明
  • Vector底层也是一个对象数组,protected Object[] elementDate;
  • Vector是线程同步的,即线程安全
  • 在开发中,需要线程同步安全时,考虑使用Vector 


//底层源码分析
//new Vector()

public Vector() { 
    this(10); 
} 
//补充:如果是 Vector vector = new Vector(8); 走的方法: 
public Vector(int initialCapacity) { 
    this(initialCapacity, 0); 
} 

vector.add(i) 
//下面这个方法就添加数据到 vector 集合 
public synchronized boolean add(E e) { 
    modCount++; 
    ensureCapacityHelper(elementCount + 1); 
    elementData[elementCount++] = e; 
    return true; 
} 

//确定是否需要扩容 条件 : minCapacity - elementData.length>0 
private void ensureCapacityHelper(int minCapacity) { 
    if (minCapacity - elementData.length > 0)
    grow(minCapacity); 
} 

//如果 需要的数组大小 不够用,就扩容 , 扩容的算法 
newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); 
//就是扩容两倍.  
private void grow(int minCapacity) { 
    int oldCapacity = elementData.length; 
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? 
    capacityIncrement : oldCapacity); 
    if (newCapacity - minCapacity < 0) 
        newCapacity = minCapacity; 
    if (newCapacity - MAX_ARRAY_SIZE > 0) 
        newCapacity = hugeCapacity(minCapacity); 
    elementData = Arrays.copyOf(elementData, newCapacity); 
}

6.2 Vector和ArrayList的比较
底层结构版本线程安全(同步)效率扩容机制
ArrayList可变数组jdk1.2不安全,效率高

如果是有参构造1.5倍

如果是无参,第一次10,第二次开始按1.5倍扩容

Vector可变数组jdk1.0安全,效率不高

如果是无参,默认10,满后就按二倍扩容

有参直接按指定的大小的二倍扩容

7 LinkedList底层介绍 7.1 LinkedList的全面说明
  • LinkedList底层实现了双向链表和双端队列特点
  • 可以添加任意元素且可以重复,包括null
  • 线程不安全,没有实现同步
7.2 LinkedList的底层 *** 作机制
  • LinkedList底层维护了一个双向链表
  • LinkedList中维护了两个属性first和last分别指向首节点和尾节点
  • 每个节点(Node对象)里面又维护了prev,next,item三个属性,其中prev指向前一个,next指向后一个,最终实现双向链表
  • 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高

 7.3 LinkedList的增删改查案例
public class LinkedListCRUD { 
    public static void main(String[] args) { 
        LinkedList linkedList = new LinkedList(); 
        linkedList.add(1); 
        linkedList.add(2); 
        linkedList.add(3); 
        System.out.println("linkedList=" + linkedList); 
        //演示一个删除结点的 
        linkedList.remove(); // 这里默认删除的是第一个结点 
        //linkedList.remove(2); 
        System.out.println("linkedList=" + linkedList); 
        //修改某个结点对象 
        linkedList.set(1, 999); 
        System.out.println("linkedList=" + linkedList); 
        //得到某个结点对象 
        //get(1) 是得到双向链表的第二个对象
        Object o = linkedList.get(1); 
        System.out.println(o);//999 
        //因为 LinkedList 是 实现了 List 接口, 遍历方式 
        System.out.println("===LinkeList 遍历迭代器===="); 
        Iterator iterator = linkedList.iterator(); 
        while (iterator.hasNext()) { 
            Object next = iterator.next(); 
            System.out.println("next=" + next); 
        } 
        System.out.println("===LinkeList 遍历增强 for===="); 
        for (Object o1 : linkedList) { 
            System.out.println("o1=" + o1); 
        } 
        System.out.println("===LinkeList 遍历普通 for===="); 
        for (int i = 0; i < linkedList.size(); i++) { 
            System.out.println(linkedList.get(i)); 
        }
    }
}

运行结果:

linkedList=[1, 2, 3]
linkedList=[2, 3]
linkedList=[2, 999]
999
===LinkeList 遍历迭代器====
next=2
next=999
===LinkeList 遍历增强 for====
o1=2
o1=999
===LinkeList 遍历普通 for====
2
999 

8. ArrayList和LinkedList比较
底层结构增删的效率改查的效率
ArrayList可变数组较低,数组扩容较高
LinkedList双向链表较高,通过链表追加较低
  • 如果我们改查的 *** 作较多,选择ArrayList
  • 如果我们增删的 *** 作多,选择LinkedList
  • 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList
9.Set接口和常用方法 9.1 Set接口基本介绍
  • 无序(添加和取出的顺序不一致),没有索引
  • 不允许重复元素,所以最多包含一个null
  • JDK API中Set接口的实现类有:
 9.2 Set接口的常用方法
  • 和list接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样
9.3 Set接口的遍历方式
  • 同Collection的遍历方式一样,因为Set接口是Collection接口的子接口
  • 可以使用迭代器,增强for,不能使用索引的方法来获取
 9.4 Set接口的常用方法举例
public class SetMethod {
    public static void main(String[] args) {
//1. 以 Set 接口的实现类 HashSet 来讲解 Set 接口的方法
//2. set 接口的实现类的对象(Set 接口对象), 不能存放重复的元素, 可以添加一个null
//3. set 接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
//4. 注意:取出的顺序的顺序虽然不是添加的顺序,但是他的结果固定.
        Set set = new HashSet();
        set.add("john");
        set.add("lucy");
        set.add("john");//重复
        set.add("jack");
        set.add("hsp");
        set.add("mary");
        set.add(null);//
        set.add(null);//再次添加 null
        for (int i = 0; i < 10; i++) {
            System.out.println("set=" + set);
        }
//遍历
//方式 1: 使用迭代器
        System.out.println("=====使用迭代器====");
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println("obj=" + obj);
        }
        set.remove(null);
//方式 2: 增强 for
        System.out.println("=====增强 for====");
        for (Object o : set) {
            System.out.println("o=" + o);
        }
//set 接口对象,不能通过索引来获取
    }
}

 运行结果:

set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
set=[null, hsp, mary, john, lucy, jack]
=====使用迭代器====
obj=null
obj=hsp
obj=mary
obj=john
obj=lucy
obj=jack
=====增强 for====
o=hsp
o=mary
o=john
o=lucy
o=jack

10. Set接口实现类-Hashset 10.1 HashSet的全面说明
  • HashSet实现了Set接口
  • HashSet底层实际上是HashMap

  •  可以存放null值,但是只能有一个null
  • HashSet不保证元素是有序的,取决于hash后,再确定索引的结果(即,不保证存放元素的顺序和取出顺序一致)
  • 不能有重复元素/对象,在前面Set接口使用已经讲过
10.2 HashSet底层机制说明

->分析HashSet底层是HashMap,HashMap的底层是数组+链表+红黑书

  1. HashSet底层是HashMap
  2. 添加一个元素时,先得到hash值,会转换成->索引值
  3. 找到存储数据表table,看这个索引位置是否已经存放的有元素
  4. 如果没有,直接加入
  5. 如果有,调用equals比较,如果相同就放弃添加,如果不相同,则以链表的形式添加到后面
  6. java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进化成红黑树  
//1. 执行 HashSet() 
public HashSet() { 
    map = new HashMap<>(); 
} 
//2. 执行 add() 
public boolean add(E e) {//e = "java" 
    return map.put(e, PRESENT)==null;//(static) PRESENT = new Object(); 
} 
//3.执行 put() , 该方法会执行 hash(key) 得到 key 对应的 hash 值 算法 h = key.hashCode()) ^ (h >>> 16) 
public V put(K key, V value) {//key = "java" value = PRESENT 共享 
    return putVal(hash(key), key, value, false, true); 
}
//4.执行 putVal 
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, oolean evict) { 
    Node[] tab; Node p; int n, i; //定义了辅助变量 
    //table 就是 HashMap 的一个数组,类型是 Node[] 
    //if 语句表示如果当前 table 是 null, 或者 大小=0 
    //就是第一次扩容,到 16 个空间.  
    if ((tab = table) == null || (n = tab.length) == 0) 
        n = (tab = resize()).length; 
    //(1)根据 key,得到 hash 去计算该 key 应该存放到 table 表的哪个索引位置 
    //并把这个位置的对象,赋给 p 
    //(2)判断 p 是否为 null 
    //(2.1) 如果 p 为 null, 表示还没有存放元素, 就创建一个 Node     (key="java",value=PRESENT) 
    //(2.2) 就放在该位置 tab[i] = newNode(hash, key, value, null) 
    if ((p = tab[i = (n - 1) & hash]) == null) 
        tab[i] = newNode(hash, key, value, null); 
    else { 
    //一个开发技巧提示: 在需要局部变量(辅助变量)时候,在创建 
    Node e; K k; // 
    //如果当前索引位置对应的链表的第一个元素和准备添加的 key 的 hash 值一样 
    //并且满足 下面两个条件之一: 
    //(1) 准备加入的 key 和 p 指向的 Node 结点的 key 是同一个对象 
    //(2) p 指向的 Node 结点的 key 的 equals() 和准备加入的 key 比较后相同 
    //就不能加入
    if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) 
          e = p; 
    //再判断 p 是不是一颗红黑树,  
    //如果是一颗红黑树,就调用 putTreeVal , 来进行添加 
    else if (p instanceof TreeNode) 
        e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); 
    else {//如果 table 对应索引位置,已经是一个链表, 就使用 for 循环比较 
        //(1) 依次和该链表的每一个元素比较后,都不相同, 则加入到该链表的最后 
        // 注意在把元素添加到链表后,立即判断 该链表是否已经达到 8 个结点 就调用 treeifyBin()                        对当前这个链表进行树化(转成红黑树) 
        // 注意,在转成红黑树时,要进行判断, 判断条件 
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64)) 
        resize(); 
    // 如果上面条件成立,先 table 扩容.  
    // 只有上面条件不成立时,才进行转成红黑树 
    //(2) 依次和该链表的每一个元素比较过程中,如果有相同情况,就直接 break 
    for (int binCount = 0; ; ++binCount) { 
        if ((e = p.next) == null) { 
            p.next = newNode(hash, key, value, null); 
        if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st 
            treeifyBin(tab, hash); 
        break; 
    } 
    if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k)))) 
        break; 
        p = e; 
    } 
} 
if (e != null) { // existing mapping for key 
    V oldValue = e.value; 
    if (!onlyIfAbsent || oldValue == null) 
        e.value = value; 
    afterNodeAccess(e); 
    return oldValue; 
    } 
 } 
++modCount; 
//size 就是我们每加入一个结点 Node(k,v,h,next), size++ 
if (++size > threshold) 
    resize();//扩容 
afterNodeInsertion(evict); 
return null; 
}

 ->分析HashSet的扩容和转成红黑书机制

  1. HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor是0.75) = 12
  2. 如果table数组使用到了临界值12,就会扩容到16*2=32,新的临界值就是32*0.75 = 24,以此类推
  3. 在 Java8 中, 如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是 8 ), 
    并且 table 的大小 >= MIN_TREEIFY_CAPACITY(默认 64),就会进行树化(红黑树),  
    否则仍然采用数组扩容机制
11.Set接口实现类-LinkedHashSet
  • LinkedHashSet是HashSet的子类

  • LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表

  • LinkedHashSet根据元素hashcode值来决定元素的存储位置,同时使用链表维护元素的次序, 这使得元素看起来是以插入顺序保存的

  • LinkedHashSet不允许添加重复元素

12.Map接口和常用方法 12.1Map接口实现类的特点(JDK-8)
  • Map与Collection并列存在,用于保存具有映射关系的数据:Key-value
  • Map中的Key和value可以是任意引用类型的数据,会封装到HashMap$Node对象中
  • Map中的key不允许重复,原因和HashSet一样
  • Map中的value可以重复
  • Map的Key可以为null,value也可以为null,只能有一个key为null,可以有多个value为null
  • 常用String类作为Map中的key
  • key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value 
public class Map_ {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("no1", "郭靖");//k-v
        map.put("no2", "张无忌");//k-v
        map.put("no1", "张三丰");//当有相同的 k , 就等价于替换. map.put("no3", "张三丰");//k-v
        map.put(null, null); //k-v
        map.put(null, "abc"); //等价替换
        map.put("no4", null); //k-v
        map.put("no5", null); //k-v
        map.put(1, "赵敏");//k-v
        map.put(new Object(), "金毛狮王");//k-v
        // 通过 get 方法,传入 key ,会返回对应的 value
        System.out.println(map.get("no2"));//张无忌
        System.out.println("map=" + map);
    }
}

运行结果:

张无忌

map={no2=张无忌, null=abc, no1=张三丰, 1=赵敏, no4=null, no5=null, java.lang.Object@5cad8086=金毛狮王}

12.2 Map接口常用方法
public class MapMethod {
    public static void main(String[] args) {
//演示 map 接口常用方法
        Map map = new HashMap();
        map.put("邓超", new Book("", 100));//OK
        map.put("邓超", "孙俪");//替换-> 一会分析源码
        map.put("王宝强", "马蓉");//OK
        map.put("宋喆", "马蓉");//OK
        map.put("刘令博", null);//OK
        map.put(null, "刘亦菲");//OK
        map.put("鹿晗", "关晓彤");//OK
        System.out.println("map=" + map);
// remove:根据键删除映射关系
        map.remove(null);
        System.out.println("map=" + map);
// get:根据键获取值
        Object val = map.get("鹿晗");
        System.out.println("val=" + val);
// size:获取元素个数
        System.out.println("k-v=" + map.size());
// isEmpty:判断个数是否为 0
        System.out.println(map.isEmpty());//F
// clear:清除 k-v
//map.clear();
        System.out.println("map=" + map);
// containsKey:查找键是否存在
        System.out.println("结果=" + map.containsKey("鹿晗"));//T
        }
}
class Book {
    private String name;
    private int num;
    public Book(String name, int num) {
        this.name = name;
        this.num = num;
    }
}

 运行结果:

map={邓超=孙俪, 宋喆=马蓉, 刘令博=null, null=刘亦菲, 王宝强=马蓉, 鹿晗=关晓彤}
map={邓超=孙俪, 宋喆=马蓉, 刘令博=null, 王宝强=马蓉, 鹿晗=关晓彤}
val=关晓彤
k-v=5
false
map={邓超=孙俪, 宋喆=马蓉, 刘令博=null, 王宝强=马蓉, 鹿晗=关晓彤}
结果=true

12.3 Map遍历方式

Map遍历方式案例演示

  • containsKey:查找键是否存在
  • KeySet:获取所有的键
  • entrySet:获取所有关系k-v
  • values:获取所有的值
public class MapFor {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("邓超", "孙俪");
        map.put("王宝强", "马蓉");
        map.put("宋喆", "马蓉");
        map.put("刘令博", null);
        map.put(null, "刘亦菲");
        map.put("鹿晗", "关晓彤");
//第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
        Set keyset = map.keySet();
        //(1) 增强 for
        System.out.println("-----第一种方式-------");
        for (Object key : keyset) {
            System.out.println(key + "-" + map.get(key));
        }

        //(2) 迭代器
        System.out.println("----第二种方式--------");
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()) {
            Object key = iterator.next();
            System.out.println(key + "-" + map.get(key));
        }

//第二组: 把所有的 values 取出
        Collection values = map.values();
        //这里可以使用所有的 Collections 使用的遍历方法
        //(1) 增强 for
        System.out.println("---取出所有的 value 增强 for----");
        for (Object value : values) {
            System.out.println(value);
        }
        //(2) 迭代器
        System.out.println("---取出所有的 value 迭代器----");
        Iterator iterator2 = values.iterator();
        while (iterator2.hasNext()) {
            Object value = iterator2.next();
            System.out.println(value);
        }
//第三组: 通过 EntrySet 来获取 k-v
        Set entrySet = map.entrySet();// EntrySet>
        //(1) 增强 for
        System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
        for (Object entry : entrySet) {
            //将 entry 转成 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
        //(2) 迭代器
        System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
        Iterator iterator3 = entrySet.iterator();
        while (iterator3.hasNext()) {
            Object entry = iterator3.next();
            //System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
            //向下转型 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
    }
}
13. Map接口实现类-HashMap 13.1 HashMap介绍
  • Map接口的常用实现类:HashMap,Hashtable,和Properties
  • HashMap是Map接口使用频率最高的实现类
  • HashMap是以key-value对的方式来存储数据(HashMap$Node类型)
  • key不能重复,但是值可以重复,允许使用null键和null值
  • 如果添加相同的key,则会覆盖原来的key-val,等同于修改
  • 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的。(jdk8的Hash Map底层,数组加链表加红黑树)
  • HashMap没有实现同步,因此是线程不安全的(方法没有做同步互斥的 *** 作,没有synchronized)
13.2 HashMap底层机制
  1. 扩容机制和HashSet相同,因为HashSet的底层就是HashMap
  2. HashMap的底层维护了Node类型的数组table,默认为Null
  3. 当创建对象时,将加载因子(loadfactor)初始化为0.75
  4. 当添加key-value时,通过key的哈希值得到在table的索引,然后判断索引处是否有元素,如果没有元素直接添加,如果该索引有元素,继续判断该元素的key和准备加入的Key是否相等,如果相等,则直接替换val,如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容
  5. 第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor是0.75) = 12
  6. 如果table数组使用到了临界值12,就会扩容到16*2=32,新的临界值就是32*0.75 = 24,以此类推
  7. 在 Java8 中, 如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是 8 ), 
  8. 并且 table 的大小 >= MIN_TREEIFY_CAPACITY(默认 64),就会进行树化(红黑树),  
  9. 否则仍然采用数组扩容机制
14. Map接口实现类-Hashtable 14.1Hashtable的基本介绍
  • Hashtable是线程安全的,HashMap是线程不安全的
  • Hashtable的键和值都不能为Null,否则会抛出NullPointException
  • 使用方法基本和HashMap一样
14.2 HashMap和Hashtable对比
  • 版本

    线程安全(同步)

    效率

    允许null键null值

    HashMap

    1.2

    不安全

    可以

    Hashtable

    1.0

    安全

    较低

    不可以

15.Map接口实现类-Properties 15.1 基本介绍
  • Properties类继承自Hashtable类并且实现了Map接口,也是使用了一种键值对的形式来保存数据
  • 它的使用特点和Hashtable类似
  • Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象并进行读取和修改
  • xxx.properties文件常作为配置文件,这个知识点在IO流举例
15.2 基本使用
public class Properties_ {
    public static void main(String[] args) {
//1. Properties 继承 Hashtable
//2. 可以通过 k-v 存放数据,当然 key 和 value 不能为 null
//增加
        Properties properties = new Properties();
//properties.put(null, "abc");//抛出 空指针异常
//properties.put("abc", null); //抛出 空指针异常
        properties.put("john", 100);//k-v
        properties.put("lucy", 100);
        properties.put("lic", 100);
        properties.put("lic", 88);//如果有相同的 key , value 被替换
        System.out.println("properties=" + properties);
        //通过 k 获取对应值
        System.out.println(properties.get("lic"));//88
//删除
        properties.remove("lic");
        System.out.println("properties=" + properties);
//修改
        properties.put("john", "约翰");
        System.out.println("properties=" + properties);
    }
}

16. 如何选择集合实现类

->先判断存储类型(一组对象或一组键值对)

一组对象:Collection接口

        允许重复:List

                增删多:LinkedList

                改查多:ArrayList

        不允许重复:Set

                无序:HashSet

                排序:TreeSet

                插入和取出顺序一致:LinkedHashSet

一组键值对:Map

        键无序:HashMap

        键排序:TreeMap

        键插入和取出顺序一致:LinkedHashMap

        读取文件:Properties

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/922970.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-16
下一篇2022-05-16

发表评论

登录后才能评论

评论列表(0条)

    保存