浅谈Java中的集合

下面内容自己结合B站视频写的总结

为什么要用集合

一般情况下保存多个数据使用的数组,但是数组灵活性不够,有很多不足,如:

  • 长度开始时必须指定,而且指定之后不能修改
  • 保存的必须为同一类型的元素
  • 使用数组进行增删比较麻烦
    集合
    1、集合可以根据需要动态增加或减少其大小
    2、Java集合框架提供了丰富的方法和工具,可以方便地对集合进行操作,如添加、删除、搜索、排序等

集合的框架体系

1、集合主要分2组:单列集合,双列集合
2、Collection接口有两个重要的子接口List和Set,它们的实现子类都是单列集合
3、Map接口的实现子类是双列集合。存放的K-V
例如:

package collection;
import java.util.ArrayList;
import java.util.HashMap;

@SuppressWarnings("all")
public class Collection_01 {
    public static void main(String[] args) {
        ArrayList arrayList=new ArrayList();
        arrayList.add("Tom");
        arrayList.add("Alen");

        HashMap hashMap=new HashMap();
        hashMap.put("安徽","合肥");
        hashMap.put("江苏","南京");
    }
}

在这里插入图片描述
在这里插入图片描述

Collection接口和常用的方法

介绍
1、Collection实现子类可以存放多个元素,每个元素可以是Object
2、有些Collection的实现类,可以存放重复的元素,有些不可以
3、有些Collection的实现类,有些是有序的(List),有些不是有序的(Set)
4、Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
5、Collection接口是不能直接实例化的,只有实现其接口的类可以实例化,拿ArrayList举例:

  • add:添加单个元素
  • remove:删除指定元素
  • contains: 查找元素是否存在
  • size:获取元素个数
  • isEmpty:判断是否为空
  • clear:清空
  • addAll:添加多个元素
  • containsAll:查找多个元素是否存在
  • removeAll:删除多个元素
    例如:
package collection;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("all")
public class Collection_02 {
    public static void main(String[] args) {
        List list1 = new ArrayList();
        list1.add("sls");
        list1.add("scy");
        list1.add(520);  //list.add(new Integer(520))
        list1.add("wy");
        System.out.println(list1);

        list1.remove(2);
        list1.remove("scy");
        System.out.println(list1);

        if(list1.contains("wy")){
            System.out.println("存在\"wy\"");
        }
        System.out.println(list1.contains("wy"));

        System.out.println(list1.size());

        System.out.println(list1.isEmpty());

        list1.clear();

        System.out.println("list:"+list1);

        List list2=new ArrayList();
        list2.add("qwe");
        list2.add("asd");

        list1.addAll(list2);
        System.out.println(list1);

        System.out.println(list1.containsAll(list2));

        list1.add("nihao");
        list1.removeAll(list2);
        System.out.println(list1);
    }
}

输出结果:

[sls, scy, 520, wy]
[sls, wy]
存在"wy"
true
2
false
list:[]
[qwe, asd]
true
[nihao]

迭代器

Collection接口遍历元素方式一:使用Lterator(迭代器)
介绍:
1、Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
2、所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现Iterator接口的对象,即可以返回一个迭代器
3、Iterator仅用于遍历集合,Iterator本身并不存在存放对象

基本语法

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

4、在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检查,若不调用,且下一条记录无效,直接调用iterator.next()就会抛出NoSuchElementException异常
注意:可以通过“itit”快速生成关于 iterator迭代器的while循环
例如:

package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

@SuppressWarnings("all")
public class CollectionIterator {
    public static void main(String[] args) {
        Collection col=new ArrayList();
        col.add(new Persons("森小胖","小胖",25));
        col.add(new Persons("申小玉","小玉",24));
        col.add(new Persons("汪小洋","小洋",26));

        // System.out.println(col);
        //现在希望能够遍历col集合
        //1、先得到col对应的迭代器
        //2、使用while循环遍历
        //3、当推出while循环后,这时候iterator迭代器,指向最后的元素
        //4、如果希望再次遍历,需要重置迭代器 即iterator= col.iterator();

        Iterator iterator= col.iterator();
        
        while (iterator.hasNext()){
            //返回下一个元素,类型是Objext
            Object person =iterator.next();
            System.out.println(person);

        }
        System.out.println("再次遍历");
        iterator= col.iterator();
        //快速生成while -->itit
        while (iterator.hasNext()){
            //返回下一个元素,类型是Objext
            Object person =iterator.next();
            System.out.println(person);

        }
    }
}
class Persons{

    private String name;
    private String small_name;
    private int age;

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

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

输出结果:

{name='森小胖', small_name='小胖', age=25}
{name='申小玉', small_name='小玉', age=24}
{name='汪小洋', small_name='小洋', age=26}
再次遍历
{name='森小胖', small_name='小胖', age=25}
{name='申小玉', small_name='小玉', age=24}
{name='汪小洋', small_name='小洋', age=26}

for循环增强

Collection接口遍历元素方式二:增强for循环
介绍:
1、增强for循环,可以代替iterator迭代器,事实上增强for循环就是简化的iterator,本质一样,只能用于遍历集合或数组
2、增强for循环底层仍然是迭代器
基本语法

for{元素类型 元素名:集合名或组名}(
	访问元素
)

注意:可以通过“I”快速生成关于 增强for循环
例如:

package collection;

import java.util.ArrayList;
import java.util.Collection;

@SuppressWarnings("all")
public class CollectionFor {
    public static void main(String[] args) {
        Collection col=new ArrayList();
        col.add(new Peoples("森小胖","小胖",25));
        col.add(new Peoples("申小玉","小玉",24));
        col.add(new Peoples("汪小洋","小洋",26));
        // 使用增强for循环遍历集合
        for (Object people : col){
            System.out.println(people);
        }
        System.out.println();
        // 使用增强for循环遍历数组
        int[] nums = {1,2,3,4,5,6,7,8,8,6,5,3};
        for (int i:nums){
            System.out.print(i+" ");
        }
    }
}
class Peoples{

    private String name;
    private String small_name;
    private int age;

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

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

输出结果:

{name='森小胖', small_name='小胖', age=25}
{name='申小玉', small_name='小玉', age=24}
{name='汪小洋', small_name='小洋', age=26}

1 2 3 4 5 6 7 8 8 6 5 3 

List接口

介绍
1、List接口是Collection接口的子接口
2、List集合类中的元素有序,则添加顺序和取出顺序一致,且可重复
3、List集合中的每个元素都有其对应的顺序索引,即支持索引。
4、List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
5、JDK API中List接口的实现常有:ArrayList、LinkedList和Vector

例如

package list;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("all")
public class List_ {
    public static void main(String[] args) {
        List  list=new ArrayList();
        list.add("sls");
        list.add("scy");
        list.add("wy");
        list.add("sls");
        System.out.println(list);
//        List集合中的每个元素都有其对应的顺序索引,即支持索引
//        索引是从0开始
        System.out.println(list.get(2));

    }
}

输出结果:

[sls, scy, wy, sls]
wy

6、List集合里添加了一些根据索引来操作集合元素的方法

  • void add(int index,Object ele) //在index位置插入ele元素
  • boolean addAll(int index,Collection eles) //从index位置开始将eles中的所有元素添加进来
  • Object get(int index) //获取指定index位置的元素
  • int indexOf(Object obj) //返回obj在当前集合中末次出现的位置
  • int lastindexOf(Object obj) //返回obj在当前集合中末次出现的位置
  • Object set(int index, object ele) //设置指定index位置的元素为ele,类似替换
  • Object remove(int index) //移除指定index位置的元素,并返回此元素
  • List subList(int fromindex,int toindex) //返回从fromindex到toindex位置的子集合

例如:

package list;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("all")
public class List_01 {
    public static void main(String[] args) {
        List list1 =new ArrayList();
        list1.add("tom");
        list1.add("Jack");
        list1.add("Alien");

        //在索引等于1的位置插入一个对象
        list1.add(1,"sls");
        System.out.println(list1);

        List list2 =new ArrayList();
        list2.add("scy");
        list2.add("wy");

        //将list2插入list1中“tom“的后面
        list1.addAll(1,list2);
        System.out.println(list1);

        //返回list1中“sls”第一次出现位置
        System.out.println(list1.indexOf("sls"));

        list1.add("scy");
        System.out.println(list1);
        //返回list1中“scy”最后一次出现的位置
        System.out.println(list1.lastIndexOf("scy"));

        //删除索引为2的元素
        list1.remove(2);
        System.out.println(list1);

        //将索引为1的位置元素替换成“ly”
        list1.set(1,"ly");
        System.out.println(list1);

        //返回索引为3至4的元素
        List list3=list1.subList(3,5);
        System.out.println(list3);
    }
}

输出结果:

[tom, sls, Jack, Alien]
[tom, scy, wy, sls, Jack, Alien]
3
[tom, scy, wy, sls, Jack, Alien, scy]
6
[tom, scy, sls, Jack, Alien, scy]
[tom, ly, sls, Jack, Alien, scy]
[Jack, Alien]

List三种遍历方式

1、方式一:使用iterator

Iterator iter = col.iterator();
while(iter.hasNext()){
	Object o=iter.next();
}

2、方式二:使用增强for循环

for(Object o:col){
}

3、方式三:使用普通for

for(int i=0;i<list.size();i++){
	Object object=list.get(i);
} 

例如:

package list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@SuppressWarnings("all")
public class ListFor {
    public static void main(String[] args) {
        List list=new ArrayList();
        list.add("sls");
        list.add("wy");
        list.add("scy");

        Iterator iter = list.iterator();
        while(iter.hasNext()){
            Object o=iter.next();
            System.out.print(o+" ");
        }
        System.out.println();

        for(Object o:list){
            System.out.print(o+" ");
        }
        System.out.println();

        for(int i=0;i<list.size();i++){
            Object obj=list.get(i);
            System.out.print(obj+" ");
        }
    }
}

输出结果:

sls wy scy 
sls wy scy 
sls wy scy 

ArrayList

介绍
1、ArrayList可以放任何元素包括null
2、ArrayList是由数组来实现数据存储
3、ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高),在多线程情况下,不建议使用ArrayList,源码中,没有使用synchronized

底层介绍
1、ArrayList中维护了一个Object类型的数组elementData,即:transient Object[] elementData; //transient 表示瞬间的,短暂的,表示该属性不会被序列化
2、当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍
3、如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
图3
例如:

package list;
import java.util.ArrayList;
public class ArrayList_01 {
    public static void main(String[] args) {
        // 创建一个空的elementData数组={}
        // 执行list.add :1、先确定是否要扩容  2、然后执行
        // 确定minCapacity:第一次扩容为10
        // 记录集合被修改的次数 modCount++,如果elementData的大小不够,就调用grow()去扩展
        // 使用扩容机制来确定要扩容到多大11
        // 第一次newCapacity=10
        // 第二次及以后,按照1.5倍扩容
        // 扩容时使用Arrays.copyOf()
        ArrayList list = new ArrayList();
        for (int i=1;i<=10;i++){
            list.add(i);
        }
        for (int i=11;i<=15;i++){
            list.add(i);
        }
        list.add(20);
        list.add(21);
        list.add(null);
        System.out.println(list);
    }
}

输出结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 20, 21, null]

Vector

介绍
1、Vector底层也是一个对象数组,protected Object[] elementData;
2、Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
3、在开发中,需要线程同步安全时,考虑使用Vector
在这里插入图片描述

Vector与ArrayList的比较

__底层结构版本线程安全(同步)效率扩容倍数
ArrayList可变数组jdk1.2不安全,效率高如果有参构造1.5倍,如果是无参:1、第一次10;2、从第二次开始按1.5陪扩容
Vector可变数组jdk1.0安全,效率不高如果是无参,默认10,满后,就按2倍扩容,如果指定大小,则每次直接按2倍扩

例如:

package list;

import java.util.Vector;

@SuppressWarnings("all")
public class Vector_01 {
    public static void main(String[] args) {
        //无参构造
        Vector vector = new Vector();
        for (int i=0;i<10;i++){
            vector.add(i);
        }
        vector.add(11);
        System.out.println(vector);
    }
}

输出结果:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11]

LinkedList

介绍
1、LinkedList底层实现了双向链表和双端队列特点
2、可以添加任意元素(元素可以重复),包括null
3、线程不安全,没有实现同步

底层机制
1、LinkedList底层维护了一个双向链表
2、LinkedList中维护了两个属性first和last分别指向首节点和尾节点
3、每个节点(Node对象),里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
4、LinkedList元素的添加和删除不是通过数组完成的,相对来说效率较高

例如:

package list;

public class LinkedList_01 {
    public static void main(String[] args) {
        Node ls =new Node("sls");
        Node cy =new Node("scy");
        Node w =new Node("wy");
        //形成双向链表
        ls.next=cy;
        cy.pre=ls;
        cy.next=w;
        w.pre=cy;
        //让first引用指向ls,就是双向链表的第一个节点
        //让last引用指向w,就是双向链表的最后一个节点
        Node first=ls;
        Node last=w;

        //遍历,从头到尾
        while (true){
            if(first == null){
                break;
            }
            System.out.println(first.toString());
            first=first.next;
        }
        //遍历,从尾到头
        while (true){
            if(last == null){
                break;
            }
            System.out.println(last.toString());
            last=last.pre;
        }
        System.out.println();
        //在ls与cy自己之间插入对象hh
        Node hh=new Node("whh");
        ls.next=hh;
        hh.next=cy;
        hh.pre=ls;
        cy.pre=hh;

        Node last1=w;
        Node first1=ls;
        while (true){
            if(first1 == null){
                break;
            }
            System.out.println(first1.toString());
            first1=first1.next;
        }
        while (true){
            if(last1 == null){
                break;
            }
            System.out.println(last1.toString());
            last1=last1.pre;
        }
    }
}

//定义一个Node类
class Node{
    public Object item; //存放数据
    public Node next;
    public Node pre;
    public Node(Object str){
        this.item=str;
    }

    @Override
    public String toString() {
        return "Node{" +
                "item=" + item +
                '}';
    }
}

输出结果:

Node{item=sls}
Node{item=scy}
Node{item=wy}
Node{item=wy}
Node{item=scy}
Node{item=sls}

Node{item=sls}
Node{item=whh}
Node{item=scy}
Node{item=wy}
Node{item=wy}
Node{item=scy}
Node{item=whh}
Node{item=sls}

例如:

package list;
import java.util.Iterator;
import java.util.LinkedList;

public class LinkedListCRUD_01 {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        //添加一个节点
        linkedList.add("sls");
        linkedList.add("wy");
        linkedList.add("scy");
        System.out.println(linkedList);

        //默认删除第一个节点
        linkedList.remove();
        System.out.println(linkedList);

        //修改某个节点
        linkedList.set(1,"hefei");
        System.out.println(linkedList);

        //获取双向链表某个对象
        Object obj=linkedList.get(0);
        System.out.println(obj);

        //遍历方式 1、迭代器 2、增强for循环
        for (Object o:linkedList){
            System.out.print(o+" ");
        }
        System.out.println();

        Iterator ite= linkedList.iterator();
        while (ite.hasNext()){
            Object next=ite.next();
            System.out.print(next+" ");
        }

    }
}

ArrayList和LinkedList比较

-底层结构增删的效率改查的效率
ArrayList可变数组较低,数组扩容较高
LinkedList双向链表较高,通过链表追加较低

注意
1、如果改查的操作多,就选择ArrayList
2、如果增删的操作多,就选择LinkedList
3、一般来说,在程序中。80%-90%都是查询,因此大部分情况会选择ArrayList
4、在一个项目中,根据业务灵活选择,也可能这样,一个模块使用ArrayList,另外一个模块是LinkedList

Set接口

介绍
1、无序(添加和取出的顺序不一致),没有索引
2、不允许重复元素,所以最多包含一个null
3、和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样
4、Set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
5、Set接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
6、取出的顺序虽然不是添加的顺序,但是它是固定的
注意
可以使用迭代器,增强for循环,不能使用索引的方式
例如:

package set;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

@SuppressWarnings("all")
public class Set_01 {
    public static void main(String[] args) {
        //以Set 接口实现类 HashSet来讲解Set接口的方法
        Set set=new HashSet();
        set.add("sls");
        set.add("wy");
        set.add("scy");
        set.add("sls");
        set.add("sws");
        set.add(null);
        set.add(null);

        System.out.println(set);

        //遍历 1、迭代器 2、增强for循环

        Iterator ite = set.iterator();
        while (ite.hasNext()) {
            Object next =  ite.next();
            System.out.print(next+" ");

        }
        System.out.println();
        set.remove(null);
        for (Object obj:set){
            System.out.print(obj+" ");
        }

    }
}

输出结果:

[null, wy, scy, sls, sws]
null wy scy sls sws 
wy scy sls sws

HashSet

介绍
1、HashSet实现了Set
2、HashSet实际上是HashMap
3、可以存放null,但是只能有一个null
4、HashSet不保证元素的有序性,取决于hash后,在确定索引的结果
5、不能有重复元素/对象,在前面Set接口中有说过

例如:

package set;

import java.util.HashSet;

public class HashSet_01 {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        // 1、在执行add方法后,会返回一个boolean,如果添加成功会返回一个true,否则会返回一个false
        // 2、可以通过remove指定删除哪个对象
        System.out.println(hashSet.add("sls"));
        System.out.println(hashSet.add("scy"));
        System.out.println(hashSet.add("wy"));
        System.out.println(hashSet.add("sls"));
        System.out.println(hashSet.add("whh"));
        System.out.println(hashSet.add("hhw"));

        System.out.println(hashSet.remove("sls"));
        System.out.println(hashSet);

        HashSet set=new HashSet();
        set.add("cy");
        set.add("ls");
        set.add(new People("nihao"));
        set.add(new People("nihao"));
        System.out.println(set);

        set.add(new String("nh"));
        set.add(new String("nh"));
        System.out.println(set);

    }
}

class People{
    private String name;
    public People(String name) {
        this.name = name;
    }

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

输出结果

false
true
true
true
[wy, whh, hhw, scy]
[People{name='nihao'}, cy, ls, People{name='nihao'}]
[People{name='nihao'}, cy, ls, nh, People{name='nihao'}]

底层机制
1、HashSet底层是HashMap
2、添加一个元素时,先得到hash值会转换成索引值
3、找到存储数据表table,看这个索引位置是否已经存放有元素
4、如果没有直接加入
5、如果有,调用equals比较,如果相同,就放弃添加,如果相同,则添加到最后面
6、对于table,第一次添加时,table数组扩容到16,临界值(threshold)是16加载因子(loadFactor)是0.75=12,如果table数组使用到了临界值12,就会扩容到162=32,新的临界值就是32*0.75=24,依次类推
7、在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小小于等于MIN_TREEIFY_CAPACITY(默认是64),就会进行树化(红黑树),否则仍然采用数组扩容机制
例如:

package set;

public class HashSet_02 {
    public static void main(String[] args) {
        //模拟一个HashSet的底层,也就是HashMap的底层
        //1、创建一个数组,数组的类型是Node[]
        Node[] table = new Node[16];
        //创建一个节点,并连接到table数组索引下标为1的位置
        Node node1 = new Node("sls");
        node1.next=null;
        table[1]=node1;
        System.out.println(table);
        //再创建一个节点,并连接到table数组索引下标为1的位置,且在node1后
        Node node2 = new Node("ls",null);
        node1.next = node2;
        System.out.println(table);
        //再创建一个节点,并连接到table数组索引下标为1的位置,且在node2后
        Node node3 =new Node("cy",null);
        node2.next=node3;
        System.out.println(table);
    }
}
class Node{
    Object item;
    Node next;
    public Node(Object item) {
        this.item = item;
    }

    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }

    @Override
    public String toString() {
        return "Node{" + item +
                '}';
    }
}

debug结果:
在这里插入图片描述

TreeSet

直接举例

package set;

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSet_01 {
    public static void main(String[] args) {
        //1、当我们使用无餐构造器,创建TreeSet时,仍然时无序的
        //2、使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类),并指排序规则

        TreeSet treeSet = new TreeSet();
        treeSet.add("sls");
        treeSet.add("scy");
        treeSet.add("wy");
        treeSet.add("whh");
        System.out.println(treeSet);

        TreeSet treeSetOrder = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //调用String的CompareTo方法进行字符串大小的比较
                return ((String)o1).compareTo((String) o2);
            }
        });
        //从大到小排序
//        TreeSet treeSetOrder = new TreeSet(new Comparator() {
//            @Override
//            public int compare(Object o1, Object o2) {
//                //调用String的CompareTo方法进行字符串大小的比较
//                return ((String)o2).compareTo((String) o1);
//            }
//        });
        //如果按照长度大小排序(长度相等,只会输出一个)
        //        TreeSet treeSetOrder = new TreeSet(new Comparator() {
//            @Override
//            public int compare(Object o1, Object o2) {
//
//                return ((String)o1).length()-((String) o2).length();
//            }
//        });
        treeSetOrder.add("sls");
        treeSetOrder.add("scy");
        treeSetOrder.add("wy");
        treeSetOrder.add("whh");
        treeSetOrder.add("scy");
        System.out.println();
    }
}

输出结果:

[scy, sls, whh, wy]

Map接口和常用的方法

1、Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value
2、Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
3、Map中的key不允许重复,原因和HashSet一样,前面分析过
4、Map中的value可以重复
5、Map的key可以为null,value也可以为null,注意key为null,只能有一个,value为null,可以多个
6、常用String类作为Map的key
7、key和value之间存在单向的一对一关系,即通过指定的key总能找到对应的value
8、Map存放数据的key-value示意图,一对k-v是放在一个Node中的,又因为Node实现Entry接口,有些书上也说一对k-v就是一个Entry
9、Map接口的常用实现类:HashMap、Hashtable和Properties

例如:

package map;

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

@SuppressWarnings("all")
public class Map_01 {
    public static void main(String[] args) {
        //使用实现类HashMap
        Map map = new HashMap();
        map.put("老大","sls");
        map.put("老二","scy");
        map.put("老大","whh");//当有相同的K值时,就等价于替换
        map.put("老三","sls");
        map.put(null,null);
        map.put("老四",null);
        map.put("老小",null);
        map.put("2","xs");
        System.out.println(map);
        //通过get方法,传入key,会返回对应的value
        System.out.println(map.get("老大"));
    }
}

输出结果:

{null=null, 老小=null, 2=xs, 老二=scy, 老四=null, 老三=sls, 老大=whh}
whh

例如:

package map;

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

public class Map_02 {
    public static void main(String[] args) {
        Map map=new HashMap();
        map.put("no1","sls");
        map.put("no2","scy");
        Set set=map.entrySet();
        System.out.println(set.getClass());
        for (Object obj:set){
            System.out.println(obj.getClass());
            //想从HashMap$Node取出k-v
            //可以做一个向下转型
            Map.Entry entry=(Map.Entry)obj;
            System.out.println(entry.getKey()+":"+entry.getValue());
        }

    }
}

输出结果:

class java.util.HashMap$EntrySet
class java.util.HashMap$Node
no2:scy
class java.util.HashMap$Node
no1:sls

9、Map接口的常用方法

  • put :添加
  • remove:根据健删除映射关系
  • get:根据键获取值
  • size:获取元素个数
  • isEmpty:判断个数是否为0
  • clear:清除
  • containsKey:查找健是否存在

例如:

package map;

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

public class Map_03 {
    public static void main(String[] args) {
        Map map=new HashMap();
        map.put("sls",new People("",100));
        map.put("sls","scy");//替换
        map.put("wy","ttt");
        map.put("bzd","ttt");
        map.put(null,"ly");
        map.put("na","nihao");

        System.out.println(map);

        map.remove("na");
        System.out.println(map);

        Object bzd = map.get("bzd");
        System.out.println("bzd : "+bzd);

        System.out.println(map.size());

        System.out.println(map.isEmpty());

        System.out.println(map.containsKey("wy"));

        map.clear();
        System.out.println(map);

    }
}

class People{
    private String name;
    private int age;

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

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

输出结果:

{null=ly, wy=ttt, na=nihao, sls=scy, bzd=ttt}
{null=ly, wy=ttt, sls=scy, bzd=ttt}
bzd : ttt
4
false
true
{}

Map三种遍历方式

例如:

package map;

import java.util.*;

public class MapFor {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        Map map=new HashMap();
        map.put("sls","scy");//替换
        map.put("wy","ttt");
        map.put("bzd","ttt");
        map.put(null,"ly");
        map.put("na","nihao");

        //方法一:先取出所有的Key,通过Key取出对应的Value
        Set keyset=map.keySet();
        //1、增强for循环
        for(Object key:keyset){
            System.out.print(key+" : "+map.get(key)+" | ");
        }
        System.out.println();

        //2、迭代器
        Iterator iter = keyset.iterator();
        while (iter.hasNext()) {
            Object next =  iter.next();
            System.out.print(next+" : "+map.get(next)+" | ");
        }

        System.out.println();

        //方法二:把所有的values取出
        Collection values = map.values();
        //可以使用所有的Collections使用的遍历方法
        //1、增强for循环
        for (Object value:values){
            System.out.print(value+" | ");

        }
        System.out.println();

        //2、迭代器
        Iterator iter1=values.iterator();
        while (iter1.hasNext()) {
            Object next =  iter1.next();
            System.out.print(next+" | ");
        }

        System.out.println();

        //方法三:通过EntrySet来获取k-v
        //EntrySet<Map.Entry<K,V>>
        Set entrySet =map.entrySet();
        //1、增强for循环
        for (Object entry:entrySet){
            //向下转型
            //将entry转换成Map.Entry (HashMap$Node 实现 Map.Emtry)
            Map.Entry m = (Map.Entry) entry;
            System.out.print(m.getKey()+" : "+m.getValue()+" | ");
        }

        System.out.println();
        //2、迭代器
        Iterator iter2= entrySet.iterator();
        while (iter2.hasNext()) {
            Object next =  iter2.next();
            Map.Entry m = (Map.Entry) next;
            System.out.print(m.getKey()+" : "+m.getValue()+" | --------");
            System.out.println(next.getClass());
        }

    }
}

输出结果:

null : ly | wy : ttt | na : nihao | sls : scy | bzd : ttt | 
null : ly | wy : ttt | na : nihao | sls : scy | bzd : ttt | 
ly | ttt | nihao | scy | ttt | 
ly | ttt | nihao | scy | ttt | 
null : ly | wy : ttt | na : nihao | sls : scy | bzd : ttt | 
null : ly | --------class java.util.HashMap$Node
wy : ttt | --------class java.util.HashMap$Node
na : nihao | --------class java.util.HashMap$Node
sls : scy | --------class java.util.HashMap$Node
bzd : ttt | --------class java.util.HashMap$Node

HashMap

介绍
1、HashMap是Map接口使用频率最高的实现类
2、HashMap是以key-value对的方式来存储数据(HashMap$Node)
4、key不能重复,但是value可以重复,允许使用null健和null值
5、如果添加相同的key,则会覆盖原来的key-value,等同于修改
6、与HashSet一样,不保证映射顺序,因为底层是以hash表的方式存储的
7、HashMap没有实现同步,因为线程是不安全的

底层机制
在这里插入图片描述
1、(k,v)是一个Node实现了Map.Entry<K,V>
2、jdk7.0的hashmap底层实现[数组+链表],jdk8.0底层实现[数组+链表+红黑树],和HashSet相似
3、HashMap底层维护了Node类型数组table,默认null
4、当创建对象的时,将加载因子(loadfactor)初始化为0.75
5、当添加key-val时。通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key是否和准备加入的key相等。如果相等,则直接替换value,如果不相等需要判断是树结构还是链表结构,做出相应的处理,如果添加时发现容量不够,则需要扩容。
6、第一次添加,则需要扩容table容量为16,临界值(threshold)为12
7、以后再扩容,则需要扩容table容量为原来的2倍,临界值为原来的2倍,即24,以此类推
8、在java8中,如果一条链表的元素个数超过TREEIFY_THRESGHOLD(默认是8),并且table的大小大于等于MIN_TREEIFY_CAPACITY(默认64),就会进行树化(即:红黑树)

例如:

package map;

import java.util.HashMap;

public class HashMap_01 {
    public static void main(String[] args) {
        HashMap hashMap = new HashMap();
        for (int i=1;i<=12;i++){
            hashMap.put(new Person(i),"test");
        }
        System.out.println(hashMap);
    }
}

class Person{
    private int age;

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

    @Override
    public String toString() {
        return "people{" +
                "age=" + age +
                '}';
    }
    //让其链接到同一个table数组索引后面
    @Override
    public int hashCode() {
        return 100;
    }
}

输出结果:

{people{age=1}=test, people{age=2}=test, people{age=3}=test, people{age=4}=test, people{age=5}=test, people{age=6}=test, people{age=7}=test, people{age=8}=test, people{age=9}=test, people{age=10}=test, people{age=12}=test, people{age=11}=test}

debug
在这里插入图片描述

Hashtable

介绍
1、存放的元素是K-V
2、Hashtable的键和值都不能为null,否则会抛出NullPointerException
3、Hashtable 使用的方法基本上和HashMap一样
4、Hashtable是线程安全的,而HashMap是线程不安全的

底层机制
1、底层数组Hashtable$Entry[]初始化大小为11
2、临界值threshold :8=11*0.75
3、扩容:

  • 执行方法addEntry(hash,key,value,index);添加K-V 封装到Entry
  • 当if(count>=threshold) 满足时,就进行扩容
  • 按照int newCapacity = (oldCapacity<<1)+1 的大小扩容

例如:

package map;

import java.util.Hashtable;

public class Hashtable_01 {
    public static void main(String[] args) {
        Hashtable hashtable = new Hashtable();
        hashtable.put("no1","sls");
        hashtable.put("no2","scy");
        // 异常:hashtable.put(null,"nihao");
        // 异常:hashtable.put("no3",null);
        hashtable.put("no3","sls");
        hashtable.put("no2","wy");

        System.out.println(hashtable);
    }
}

输出结果:

{no3=sls, no2=wy, no1=sls}

Hashtable和HashMap比较

版本线程安全(同步)效率允许null键null值
HashMap1.2不安全可以
Hashtable1.0安全较低不可见

Properties

介绍
1、Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据
2、它的使用特点与Hashtable类型相似,键和值都不能为null,否则会抛出NullPointerException
3、Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
4、说明:工作后xxx.properties文件通常作为配置文件

例如:

package map;

import java.util.Properties;

public class Properties_01 {
    public static void main(String[] args) {

        Properties properties = new Properties();
        properties.put("no1","sls");
        properties.put("no2","scy");
        properties.put("no3","wy");
        properties.put("no2","whh");//有相同的键也会替换
        System.out.println(properties);

        //通过k获取v
        System.out.println(properties.get("no2"));

        //删除
        properties.remove("no1");
        System.out.println(properties);

        //修改
        properties.put("no3","wh");
        System.out.println(properties);


    }
}

输出结果:

{no3=wy, no2=whh, no1=sls}
whh
{no3=wy, no2=whh}
{no3=wh, no2=whh}

TreeMap

直接举例

package map;

import java.util.Comparator;
import java.util.TreeMap;

public class Tree_Map {
    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap();
        //使用默认的构造器,创建TreeMap,是无序的
        treeMap.put("sxp","森小胖");
        treeMap.put("tom","汤姆");
        treeMap.put("xy","小玉");
        treeMap.put("wy","汪洋");

        System.out.println(treeMap);
        //按照传入K的字符串从小到大排序
//        TreeMap treeMap1 = new TreeMap(new Comparator() {
//            @Override
//            public int compare(Object o1, Object o2) {
//                return ((String) o1).compareTo((String)o2);
//            }
//        });
        //按照传入K的字符串从大到小排序
//        TreeMap treeMap1 = new TreeMap(new Comparator() {
//            @Override
//            public int compare(Object o1, Object o2) {
//                return ((String) o2).compareTo((String)o1);
//            }
//        });
        //按照K的长度大小排序(相同会省略)
        TreeMap treeMap1 = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o1).length()-((String) o2).length();
            }
        });

        treeMap1.put("sxp","森小胖");
        treeMap1.put("tom","汤姆");
        treeMap1.put("xy","小玉");
        treeMap1.put("wy","汪洋");

        System.out.println(treeMap1);

    }
}

输出结果:

{sxp=森小胖, tom=汤姆, wy=汪洋, xy=小玉}
{xy=汪洋, sxp=汤姆}

如何选择集合实现类

1、先判断存储的类型(一组对象或一组键值对)
2、一组对象:Collection接口
允许重复:List
增删多:LinkedList【底层维护了一个双向链表】
改查多:ArrayList【底层维护Object类型的可变数组】
不允许重复:Set
无序:HashSet【底层是HashMap,维护了一个哈希表 即(数组+链表+红黑树)】
排序:TreeSet
插入和取出顺序一致:LinkedHashSet【维护数组+双向链表】
3、一组键值对:Map接口
键无序:HashMap【底层是:哈希表 jdk8:数组+链表+红黑树】
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件:Properties

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值