• Java类集框架详细汇总-底层分析


    前言:

    Java的类集框架比较多,也十分重要,在这里给出图解,可以理解为相应的继承关系,也可以当作重要知识点回顾;

    Collection集合接口

    Collection集合接口

    继承自:Iterable

    public interface Collection<E> extends Iterable<E>
    

    java.util.Collection是单值集合操作的最大父接口,其中有几个核心操作方法以及常用操作方法;

    Modifier and Type Method(public) Description
    boolean add(E e) 确保此集合包含指定的元素(可选操作)。
    boolean addAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此集合(可选操作)。
    void clear() 从集合中删除所有元素(可选操作)。
    boolean contains(Object o) 如果该集合包含指定的元素,则返回true。
    boolean remove(Object o) 如果存在,则从此集合中删除指定元素的单个实例(可选操作)。
    int size() 返回此集合中的元素数。
    Object[] toArray() 返回一个包含此集合中所有元素的数组。
    Iterator<E> iterator() 返回对此集合中的元素进行迭代的迭代器。

    上面方法中有两个特殊的方法就是cotainsremove;都需要equals方法的支持才能删除与查询数据;否则找不到元素。

    后面都是衍生出的子类方法。

    List集合

    最大特点:允许保存重复的元素,并在其父接口上扩充了其他的方法;

    继承关系:

    public interface List<E> extends Collection<E>
    
    Modifier and Type Method(public) Description
    void add(int index, E element) Inserts the specified element at the specified position in this list (optional operation).
    boolean add(E e) Appends the specified element to the end of this list (optional operation).
    ListIterator<E> listIterator() Returns a list iterator over the elements in this list (in proper sequence).
    static <E> List<E> of() Returns an unmodifiable list containing zero elements.
    default void forEach(Consumer<? super T> action) Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.

    实例:

    package Java从入门到项目实战.Java类集框架.List集合;
    import java.util.List;
    public class 多数据保存 {
        public static void main(String[] args) {
            List<String> all = List.of("xbhg","Hello","World","welcome");
            Object[] result = all.toArray();
            for (Object t: result) {
                System.out.println(t);
            }
            System.out.println("----------分割线----------");
            all.forEach(System.out::println); //方法引用部分的引用构造方法
        }
    }
    

    ArrayList子类

    继承结构如下:

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
    

    实例化:重复元素允许保存并且按照添加时的顺序保存;

    package Java从入门到项目实战.Java类集框架.List集合;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class ArrayList实例化List {
        public static void main(String[] args) {
            List<String> all = new ArrayList<String>();
            all.add("Hello");
            all.add("你好");
            all.add("你好");
            all.add("xbhog");
            System.out.println(all);
            all.forEach(System.out::print);
            //lambda表达式
            all.forEach((str)->{
                System.out.print(str+"、");
            });
        }
    }
    

    集合操作方法:

    package Java从入门到项目实战.Java类集框架.List集合;
    import java.util.ArrayList;
    import java.util.List;
    
    public class ArrayLIst集合相关操作 {
        public static void main(String[] args) {
            List<String> all = new ArrayList<String>();
            System.out.println("集合是否为空?"+all.isEmpty()+"、集合元素个数"+all.size());
            all.add("1");
            all.add("2");
            all.add("3");
            System.out.println("集合是否为空?"+all.isEmpty()+"、集合元素个数"+all.size());
            System.out.println(all.get(1));
            System.out.println(all.remove("3"));
            System.out.println("集合是否为空?"+all.isEmpty()+"、集合元素个数"+all.size());
        }
    }
    

    ArrayList原理分析:重点

    首先需要明确ArrayList是通过数组实现的;这样就出现了ArrayList通过什么样的方式进行的扩容操作,以及在什么情况下才会扩容?

    ArrayList类中的数组是在构造方法中进行的空间开辟的;其对应的有无参和有参构造方法:

    无参构造方法:使用空数组(长度为0)初始化,在第一次使用时会为其开辟空间为(初始化程度为10);

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    //默认的开辟空间大小
    private static final int DEFAULT_CAPACITY = 10;
    /**
         * Shared empty array instance used for empty instances.
         */
    private static final Object[] EMPTY_ELEMENTDATA = {};
    /**
         * Shared empty array instance used for default sized empty instances. We
         * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
         * first element is added.
         */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    

    有参构造方法:长度大于0则以指定长度开辟数组空间;如果长度为0,则按照无参构造方法进行;如果负数则抛出ILLegaLArgumentException异常;

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // defend against c.toArray (incorrectly) not returning Object[]
            // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
    

    当数组扩充后利用数组复制的形式,将旧数组中的数据复制到开辟的新数组中;

    其最大程度为:

    /**
         * The maximum size of array to allocate (unless necessary).
         * Some VMs reserve some header words in an array.
         * Attempts to allocate larger arrays may result in
         * OutOfMemoryError: Requested array size exceeds VM limit
         */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    

    ArrayList保存自定义类对象:

    该操作必然包含了相关的增删改查;由于contains与remove方法的实现都需要通过对象比较俩完成;所以我们需要覆写equals方法

    package Java从入门到项目实战.Java类集框架.List集合;
    
    import java.util.ArrayList;
    import java.util.List;
    
    class Person{
        private String name;
        private int age;
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
        public boolean equals(Object obj){
            if(this== obj) return true;
            if(obj == null) return false;
            if(!(obj instanceof Person)) return false;
            Person pe = (Person) obj;
            return this.name.equals(pe.name) && this.age == pe.age;
        }
    
        @Override
        public String toString() {
            return "姓名:"+this.name +"、年龄:"+this.age;
        }
    }
    public class ArrayList保存自定义类对象 {
        public static void main(String[] args) {
            List<Person> std = new ArrayList<Person>();
            std.add(new Person("xbhog",1));
            std.add(new Person("小明",2));
            std.add(new Person("小白",3));
            System.out.println("-------删除前的数据内容----------");
            std.forEach((person)->{
                System.out.println("姓名:"+person.getName()+"、年龄:"+person.getAge());
            });
            System.out.println("-------删除后的数据内容----------");
            std.remove(new Person("小白",3));
            std.forEach((person)->{
                System.out.println("姓名:"+person.getName()+"、年龄:"+person.getAge());
            });
            System.out.println("-------查看数据是否存在----------");
            System.out.println(std.contains(new Person("小明",2)));
        }
    }
    

    LinkedList子类:

    继承结构如下:基于链表形式的实现

    public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable
    

    实现LinkedList的集合操作:

    package Java从入门到项目实战.Java类集框架.List集合;
    
    import java.util.LinkedList;
    import java.util.List;
    
    public class LinkList链表操作 {
        public static void main(String[] args) {
            List<String> all = new LinkedList<String>();
            all.add("java");
            all.add("python");
            all.add("Linux");
            System.out.println(all);
            System.out.println(all.get(2));
            System.out.println(all.get(1));
        }
    }
    

    Vector子类:

    继承结构如下:

    public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
    

    从继承结构上可以看出,Vector子类的使用方式与ArrayList的使用方式相同;

    package Java从入门到项目实战.Java类集框架.List集合;
    import java.util.List;
    import java.util.Vector;
    public class Vector子类实现List接口 {
        public static void main(String[] args) {
            List<String> all = new Vector<String>();
            all.add("asda");
            all.add("你好");
            all.add("buhao");
            System.out.println(all);
        }
    }
    

    不同点:

    以下是vector操作方法,采用的方式是synchronized 同步处理;属于线程安全,但是效率没有ArrayList高;

    在考虑线程并发访问的情况下才能去使用vector子类。

    public synchronized void copyInto(Object[] anArray)
    

    Set集合

    主要特点是:内部不允许保存重复元素

    继承结构如下:

    public interface Set<E> extends Collection<E>
    

    实例化:

    package Java从入门到项目实战.Java类集框架.Set集合;
    import java.util.Set;
    public class set的基本使用 {
        public static void main(String[] args) {
            //不能有重复值,如果有的话会报错
            //Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: 世界
    //        Set<String> all = Set.of("你好","xbhog","世界","世界");
            Set<String> all = Set.of("你好","xbhog","世界");
            System.out.println(all);
        }
    }
    

    HashSet子类:

    特点:散列存放且不允许保存重复元素 即:无序存放

    继承结构如下:

    public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable
    

    HashSet保存数据:

    package Java从入门到项目实战.Java类集框架.Set集合;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
    public class HashSet保存数据 { //数据采用无序的保存方式,且不允许保存重复的数据
        public static void main(String[] args) {
            //保存的类型是String
            Set<String> all = new HashSet<String>();
            all.add("小李");
            all.add("小红");
            all.add("小1");
            all.add("小2");
            all.add("小花");
            all.add("小花");
            System.out.println(all);
            Iterator<String> iter = all.iterator();
            while(iter.hasNext()){
                String str  = iter.next();
                System.out.print(str+"、");
            }
        }
    }
    

    LinkedHashSet子类:JDK1.4加入---解决HashSet无法顺序保存的数据

    实现是基于链表保存的数据:增加的顺序就是集合的保存顺序,且不会保存重复的数据。

    package Java从入门到项目实战.Java类集框架.Set集合;
    import java.util.Iterator;
    import java.util.LinkedHashSet;
    import java.util.LinkedList;
    import java.util.Set;
    public class 链表实现LinkHashSet {  //基于链表的操作,且保存的数据为顺序保存
        public static void main(String[] args) {
            Set<String> all = new LinkedHashSet<String>();
            all.add("小李老师");
            all.add("小bai");
            all.add("小明");
            all.add("小黄");
            System.out.println(all);
            Iterator<String> iter = all.iterator();
            while (iter.hasNext()) {
                String str = iter.next();
                System.out.println(str + "、");
            }
        }
    }
    

    TreeSet子类:

    特点:使得集合中保存的数据进行有序排列

    其继承结构如下:

    public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable
    

    TreeSet子类继承AbstractSet抽象类并实现了NavigableSet接口(该接口为排序标准接口,是Set的子类)

    TreeSet保存数据:

    package Java从入门到项目实战.Java类集框架.Set集合;
    import java.util.*;
    public class TreeSet子类的有序排列 {
        //所有保存的数据按照从大到小的顺序(字符按照字母大小顺序依次比较)
        public static void main(String[] args) {
            Set<String> all = new TreeSet<String>();
            all.add("11");
            all.add("hello");
            all.add("hello");
            all.add("world");
            all.add("add");
            all.add("部署");
            all.add("啊哈");
            System.out.println(all);
        }
    }
    

    优先级:数字排序>字母排序>汉字排序

    TreeSet子类排序分析:

    该类子在进行有序数据存储时依据的是Comparable接口实现排序;需要注意的是在覆写compareTo()方法时需要进行类中全部属性的比较;否则出现部分属性相同时被误判为同一个对象;导致重复元素判断失败;

    package Java从入门到项目实战.Java类集框架.Set集合;
    import java.util.Set;
    import java.util.TreeSet;
    class Member implements Comparable<Member>{
        private  String name;
        private  int age;
        public Member(String name,int age){
            this.name = name;
            this.age = age;
        }
        public String toString(){
            return "姓名:"+this.name +"、年龄:"+this.age;
        }
        @Override
        public int compareTo(Member per){
            if(this.age < per.age) return -1;
            else if(this.age > per.age) return 1;  //年龄相同时比较名字
            else {
                return this.name.compareTo(per.name);
            }
        }
    }
    
    public class TreeSet子类排序分析 {
        public static void main(String[] args) {
            Set<Member> all = new TreeSet<Member>();
            all.add(new Member("张三",12));
            all.add(new Member("李四",12));
            all.add(new Member("王五",20));
            all.add(new Member("王五",20));
            all.forEach(System.out::println);
        }
    }
    

    关于compareTo的相关描述,可以到源码下的注释中翻译了解。

    重复元素消除:(非排序的集合中的重复元素)

    依靠两种方法:

    1. Hash码:public int Hashcode();
    2. 对象比较:public boolean equals(Object obj);

    在进行对象比较的过程中,首先会使用hashCode()方法与集合中已经保存的代码进行匹配比较;如果代码相同则再使用equals()方法进行属性的依次比较;如果全部相同;则为相同元素;

    package Java从入门到项目实战.Java类集框架.Set集合;
    import java.util.*;
    class Person{
        private String name;
        private int age;
        public Person(String name,int age){
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Person)) return false;
    
            Person person = (Person) o;
    
            if (age != person.age) return false;
            if (name != null ? !name.equals(person.name) : person.name != null) return false;
    
            return true;
        }
        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            return result;
        }
    }
    public class 重复元素消除 {
        public static void main(String[] args) {
            Set<Person> all = new HashSet<Person>();
            all.add(new Person("张三",19));
            all.add(new Person("李四",19));
            all.add(new Person("王五",20));
            all.add(new Person("王五",20));
            all.add(new Person("魏六",66));
            all.add(new Person("xbhog",10));
            System.out.println("------------第一种输入方式-----------");
            all.forEach((person -> {
                System.out.println(person.getName()+"----"+person.getAge());
            }));
            System.out.println("------------第二种输入方式-----------");
            Iterator<Person> iter = all.iterator();
            while(iter.hasNext()){
                Person per = iter.next();
                System.out.println(per.getName() + " "+per.getAge());
            }
        }
    }
    

    通过hashSet 保存了重复元素,再两个方法的作用实现去重操作。

    集合输出

    在类框架中的对于集合的标准输出为:IteratorListIteratorEnumerationforeach;

    Iterator迭代输出:

    迭代输出:依次判断每个元素,判断其是否有内容,如果有内容就输出。

    Iterator接口依靠Iterable接口中的iterate()方法实例化的;

    Iterator常用方法:

    boolean hasNext() Returns true if the iteration has more elements.
    E next() 返回迭代中的下一个元素。
    default void remove() 从基础集合中移除该迭代器返回的最后一个元素(可选操作)。
    boolean hasNext() 如果迭代有更多元素则返回true。

    实例:

    package Java从入门到项目实战.Java类集框架.集合输出;
    import java.util.Iterator;
    import java.util.Set;
    public class Iterator输出Set集合 {
        public static void main(String[] args) {
            Set<String> all = Set.of("Hello","World","xbhog");
            Iterator<String> iter = all.iterator();
            while(iter.hasNext()){
                String str = iter.next();
                System.out.print(str+"、");
            }       
        }
    }
    

    关于数据删除的问题:

    在Collection中与Iterator都有remove方法;那么应该选择什么呢:

    Collection不管三七二十一,就给你删除了,这样会造成Java.util.ConcurrentModificationException错误;

    而Iterator在迭代的时候;都会需要依据存储的数据内容进行判断;

    所以只有Iterator接口中的remove才是实现删除数据的正确方法。

    如:

    package cn.mldn.demo;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Set;
    public class JavaCollectDemo {
    	public static void main(String[] args) {
    		// 如果使用Set.of()或List.of()创建的集合不支持删除操作
    		Set<String> all = new HashSet<String>();
    		all.add("小白");					// 保存数据
    		all.add("Java");					// 保存数据
    		all.add("Java"); 					// 保存重复数据
    		all.add("xbhog");				// 保存数据
    		Iterator<String> iter = all.iterator(); 
    		while (iter.hasNext()) {				// 集合是否有数据
    			String str = iter.next();			// 获取数据
    			if ("Java".equals(str)) {
    				iter.remove() ; 				// 删除当前数据
    			} else {
    				System.out.print(str + "、");
    			}
    		}
    	}
    }
    

    ListIterator双向迭代:

    首先区别Iterator的作用:

    Iterator完成的是从前向后单向输出

    ListIterator完成的是从后向前输出

    但是只有实现Iterator从前向后的输出后才能实现ListIterator从后向前的输出(注意先后顺序);

    因为只有实现从前向后输出结束后,指针才执行最后;

    如果顺序相反则会输出为空。

    其下的扩充方法

    Modifier and Type Method Description
    boolean hasPrevious() 如果该列表迭代器在反向遍历列表时拥有更多元素,则返回true。
    E previous() 返回列表中的前一个元素,并向后移动光标位置。

    实例:执行双向迭代

    package Java从入门到项目实战.Java类集框架.集合输出;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.ListIterator;
    public class 双向迭代输出ListIterator {
        public static void main(String[] args) {
            List<String> all = new ArrayList<String>();
            all.add("小李");
            all.add("小宋");
            all.add("xbhog");
            ListIterator<String> iter = all.listIterator();
            System.out.println("执行从后往前的操作的话必须需要先执行从前往后的操作,这样才能是指针指向后面");
            //如果不按照相关操作进行,则从后往前的操作输出为空;
            System.out.println("从前往后输出:-----------");
            while(iter.hasNext()){
                System.out.println(iter.next());
            }
            System.out.println("从后往前输出:------------");
            while(iter.hasPrevious()){  //判断是否有前一个元素
                System.out.println(iter.previous());  //有的输出前一个元素
            }
        }
    }
    

    ListIterator接口实现了List集合的双向迭代操作。

    Enumeration枚举输出:

    该类型的输出是建立在Vector集合上的;相当于依附产品;

    其接口常用方法:

    Modifier and Type Method Description
    boolean hasMoreElements() 测试此枚举是否包含更多元素。
    E nextElement() 如果该枚举对象至少还有一个要提供的元素,则返回该枚举的下一个元素。

    实例:输出vector集合数据

    package cn.mldn.demo;
    import java.util.Enumeration;
    import java.util.Vector;
    public class JavaCollectDemo {
    	public static void main(String[] args) {
    		Vector<String> all = new Vector<String>(); 		// 实例化Vector
    		all.add("小黄");								// 保存数据
    		all.add("Java");								// 保存数据
    		all.add("xbhog");							// 保存数据
    		Enumeration<String> enu = all.elements() ;			// 获取Enumeration实例
    		while (enu.hasMoreElements()) {
    			String str = enu.nextElement() ;
    			System.out.print(str + "、");
    		}
    	}
    }
    

    注意Enumeration只有输出操作没有删除操作。

    foreach输出:

    没啥好说的,既可以实现数组输出外,也支持集合的输出;

    package cn.mldn.demo;
    import java.util.HashSet;
    import java.util.Set;
    public class JavaCollectDemo {
    	public static void main(String[] args) {
    		Set<String> all = new HashSet<String>(); 			// 实例化Set
    		all.add("小黄");								// 保存数据
    		all.add("Java");								// 保存数据
    		all.add("xbhog");							// 保存数据
    		for (String str : all) {
    			System.out.print(str + "、"); 
    		}
    	}
    }
    

    实现自定义foreach输出:

    首先需要知道实现foreach需要Iterator接口的支持;所以在Set与List集合才可以通过foreach实现输出;

    如果我们自己要实现自定义类的输出,那么我们就需要实例化Iterable接口完成iterator的功能;

    package cn.mldn.demo;
    import java.util.Iterator;
    class Message implements Iterable<String> {// 支持foreach输出
    	private String[] content = { "Java", "Python", "Ubuntu" }; 	// 信息内容
    	private int foot; 					// 操作脚标
    	@Override
    	public Iterator<String> iterator() {	// 获取Iterator实例
    		return new MessageIterator();
    	}
    	private class MessageIterator implements Iterator<String> {
    		@Override
    		public boolean hasNext() {		// 判断是否存在内容
    			return Message.this.foot < 
    				Message.this.content.length;
    		}
    		@Override
    		public String next() {			// 获取数据
    			return Message.this.content[Message.this.foot++];
    		}
    	}
    }
    public class JavaCollectDemo {
    	public static void main(String[] args) {
    		Message message = new Message();	// Iterable接口实例
    		for (String msg : message) {		// foreach输出
    			System.out.print(msg + "、");
    		}
    	}
    }
    

    Map集合

    map的集合形式是键值对的方式;

    其常用方法:

    Modifier and Type Method Description
    V get(Object key) Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
    V put(K key, V value) Associates the specified value with the specified key in this map (optional operation).
    static <K,V> Map<K,V> of() Returns an unmodifiable map containing zero mappings.

    其中Map.of()可以将每一组数据转为map进行保存;

    使用Map保存Key-Value数据:

    package Java从入门到项目实战.Java类集框架.Map集合;
    import java.util.Collection;
    import java.util.Map;
    import java.util.Set;
    public class 保存Key_Value数据 {
        public static void main(String[] args) {
    //        如果Key重复  则会报错:java.lang.IllegalArgumentException
    //        如果Value与Key设置为null,则会报错:java.lang.NullPointerException
            Map<String,Integer> map = Map.of("one",1,"two",null);
            System.out.println(map);
    //        System.out.println(map.get("one"));
    //        Put只能在map子类中使用
        }
    }
    

    注意点:

    1. 如果Key重复 则会报错:java.lang.IllegalArgumentException
    2. 如果Value与Key设置为null,则会报错:java.lang.NullPointerException

    HashMap子类:

    特点:采用散列方式保存数据,即无序排列

    继承结构:

    public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
    

    HashMap进行map集合的操作:

    package Java从入门到项目实战.Java类集框架.Map集合;
    import java.util.HashMap;
    import java.util.Map;
    public class HahMap子类 {
        public static void main(String[] args) {
            Map<String,Integer> map =  new HashMap<String,Integer>();
    //        进行Map的集合操作
    //        map.put("One",1);
    //        map.put("Two",2);
    //        map.put("Three",3);
    //        System.out.println(map);
    
    //        Map数据的保存方法:
            System.out.println(map.put("One",1)); //保存数据,但是保存的数据的key不存在,返回null
            System.out.println(map.put("One",101));  // 返回1,也就是返回的是覆盖掉的值
    
            map.put("Three",3);
            map.put("demo1",4);
            map.put("demo2",null);
            map.put("Te",6);
            map.put(null,7);
            /*结果
            * null、7
            * */
            System.out.println(map.get("demo2"));
            System.out.println(map.get(null));
        }
    }
    
    

    注意两个输出的注释:

    //Map数据的保存方法:
    System.out.println(map.put("One",1)); //保存数据,但是保存的数据的key不存在,返回null
    System.out.println(map.put("One",101));  // 返回1,也就是返回的是覆盖掉的值
    

    在使用Map保存数据的时候Key与Value都不能使用null,但是使用HashMap进行保存数据可以将Key或Value设置为null,当然也可以Key=Value=null,但是这样的实现保存毫无意义。

    put方法在发生覆盖钱都可以返回原始内容,这样就可以依据返回结果来判断所设置的key是否存在;

    HashMap数据扩充操作原理分析:

    1) 首先观察构造方法:

    设置数据扩充阈值;

    public HashMap() {
        // all other fields defaulted
        this.loadFactor = DEFAULT_LOAD_FACTOR; 
    }
    

    然后跳转DEFAULT_LOAD_FACTOR查看:

    作用:容量的扩充阈值

    /**
         * The load factor used when none specified in constructor.
         */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    

    通过源码可以发现,每个Hashmap在对象实例化的时候都已经考虑到了数据存储的扩充问题;

    2) 观察HashMap中的put方法

    public V put(K key,V value){
        return putVal(hash(key),key,value,false,true);
    }
    

    在使用put方法进行数据保存的时候会调用putVal方法,同时会将key进行哈希处理(生成hash码)

    而putVal()方法为了方便数据保存会将数据封装为一个Node节点类对象,而在使用putVal()方法的操作过程会调用reasize()方法进行扩容;

    3)容量扩充

    当保存的数据超过既定的存储容量则会进行扩容,原则如下:

    常量地址:DEFAULT_INITIAL_CAPACITY;作为初始化的容量配置,而后1向左移4为-》16;

    常量的默认大小为16个元素,也就是说默认可以保存的最大的内容是16;

    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    

    当保存的数据内容超过了设置的阈值DEFAULT_LOAD_FACTOR = 0.75f

    相当于容量x阈值 = 16*0.75 = 12;即保存到12个元素的时候就会进行容量扩充;

    扩充的模式是2倍扩充:即每一次扩充2倍的容量。

    4)大数据下的数据存储方式:

    在JDK1.8之后来到大数据时代,这就触发了HashMap在大数据量中的访问效率问题;

    其中提供了一个重要的常量:TREEIFY_THRESHOLD

    static final int TREEIFY_THRESHOLD = 8;
    

    在使用HashMap进行保存的时候,如果保存的数据个数没有超过阈值8,那么会按照链表的形式进行数据的存储;而超过了这个阈值,则会将链表转为红黑树以实现树的平衡;并且利用左旋与右旋保证数据的查询性能。

    LinkedHashMap子类:

    特点:基于链表形式实现偶对的存储,可以保证存储顺序与数据增加的顺序相同;

    继承结构:

    public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
    

    使用LinkedHashMao子类存储数据:

    package Java从入门到项目实战.Java类集框架.Map集合;
    import java.util.LinkedHashMap;
    import java.util.Map;
    public class LinkedHashMap子类存储数据 {
        public static void main(String[] args) {
            Map<String,Integer> map = new LinkedHashMap<String,Integer>();
            map.put("张三",1);
            map.put("李四",2);
            map.put("王五",3);
            map.put(null,3);
            map.put("赵六",null);
            System.out.println(map);
        }
    }
    

    运行可以发现集合的保存的顺序与数据增加顺序相同;同时LinkedHashMap子类允许保存的Key或value内容为null;

    Hashtable子类:

    其继承结构如下:

    public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable
    

    使用Hashtable子类保存数据:

    package Java从入门到项目实战.Java类集框架.Map集合;
    import java.util.LinkedHashMap;
    import java.util.Map;
    public class LinkedHashMap子类存储数据 {
        public static void main(String[] args) {
            Map<String,Integer> map = new Hashtable<String,Integer>();
            map.put("张三",1);
            map.put("李四",2);
            System.out.println(map);
        }
    }
    

    HashMap与Hashtable的区别:

    HashMap中的 方法都是异步操作(非线程安全),HashMap中允许保存null数据

    Hashtable中的方法都是同步操作(线程安全),但是效率慢,Hashtable不允许保存Null数据;否则会出现NUllpointException;

    TreeMap子类:

    特点:TreeMap属于有序的Map集合类型;它可以按照key进行排序;所以需要Comaprable接口配合;

    继承结构如下:

    public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable
    

    TreeMap子类进行数据Key的排序:

    package Java从入门到项目实战.Java类集框架.Map集合;
    import java.util.Map;
    import java.util.TreeMap;
    public class TreeMap子类进行数据Key的排序 {
        public static void main(String[] args) {
    //        因为该程序保存的Key属于String类型,由于String中实现了Comparable接口,所以
    //        可以根据保存的字符的编码进行由低到高的进行排序
            /*
            * public final class String
                implements java.io.Serializable, Comparable<String>, CharSequence
            *
            *
            * */
            Map<String,Integer> map = new TreeMap<String,Integer>();
            map.put("C",3);
            map.put("B",2);
            map.put("A",1);
            System.out.println(map);
    
        }
    }
    

    Map.Entry内部接口:

    在JDK1.9开始可以利用Map接口中创建Map.entry内部接口实例;

    package Java从入门到项目实战.Java类集框架.Map集合;
    import java.util.Map;
    public class Map_Entry内部接口 {
        public static void main(String[] args) {
            Map.Entry<String,Integer> entry = Map.entry("One",1);
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
            //观察使用的子类
            System.out.println(entry.getClass().getName());
        }
    }
    

    在程序继续宁Map.Entry对象构建的时,只传入Key和Value就会自动利用KeyValueHolder子类实例化Map.Entry接口对象。

    Iterator输出Map集合:

    集合数据输出的标准形式是基于Iterator接口完成的;Collection接口直接提供iterator方法可以获得iterator接口实例;但由于Map接口中保存的数据是多个Map.Entry接口封装的二元偶对象,所以就必须采用Map集合的迭代输出;

    1. 使得Map接口中的entrySet(),将Map集合变为Set集合;
    2. 取得Set接口实例后就可以利用iterator方法取得iterator的实例化对象;
    3. 使用iterator迭代找到每一个Map.Entry对象,并进行Key与Value的分。

    Iterator和foreach输出Map集合:

    package Java从入门到项目实战.Java类集框架.Map集合;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    public class Map集合的输出问题 {
        public static void main(String[] args) {
            Map<String,Integer> map = new HashMap<String,Integer>();
            map.put("One",1);
            map.put("two",2);
    //        输出的map中的Key与Value的值
            Set<Map.Entry<String, Integer>> set = map.entrySet();//将Map集合转变为Set集合
            Iterator<Map.Entry<String,Integer>> iter = set.iterator();  //获取Iterator接口
            while(iter.hasNext()){
    //          Set中存的都是Map.Entry()对象
                Map.Entry<String, Integer> me = iter.next();
                System.out.print(me.getKey()+" "+me.getValue());
            }
    //        System.out.println("
    ");
            //foreach循环输出Map集合:
            for(Map.Entry<String,Integer> entry:set){
                System.out.print(entry.getKey()+" "+entry.getValue());
            }
        }
    }
    

    自定义Key类型:

    采用自定义类的形式实现,但是作为Key类型的类由于存在数据查找的需求,所以必须在类中覆写hashcode()与equals()方法。

    package Java从入门到项目实战.Java类集框架.Map集合;
    import java.util.HashMap;
    import java.util.Map;
    class Member{
        private String name;
        private int age;
    
        public Member(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 boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Member)) return false;
    
            Member member = (Member) o;
    
            if (age != member.age) return false;
            if (name != null ? !name.equals(member.name) : member.name != null) return false;
    
            return true;
        }
    
        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            return result;
        }
    }
    /*
    * Key进行自定义;作为Key类型的类由于存在数据查找的需求,所以应该在类中覆写HashCode() 和equals();
    * */
    public class 自定义Key的值 {
        public static void main(String[] args) {
            Map<Member,String> map = new HashMap<Member,String>();
            map.put(new Member("张三",22),"xbhog");
            map.put(new Member("李四",23),"博客");
            map.put(new Member("王五",26),"welcome");
            System.out.println(map.get(new Member("张三",22)));
    
        }
    }
    

    在存储大量的数据中,key还是可能出现重复的问题,这个问题叫Hash冲突;

    解决的方式:

    链地址法(拉链法)、开放寻址、再哈希、建立公共溢出区;

    完结:

    该博客字数6651,是迄今为止个人整理博客篇幅最长,字数最多的;希望读者能有所收获,也希望审核能过,选择博客园第一感觉,园子里很舒服,学术氛围很好,博文质量很高;不敢说自己的每篇文章写的都可以,但是也注入了自己的心血,希望能上首页!

  • 相关阅读:
    Python学习第42天(ftp习题实现day4)
    Python学习第41天(ftp习题实现day3)
    Python学习第40天(ftp习题实现day2)
    Python学习第39天(ftp习题实现day1)
    Python学习第38天(文件处理,os、sys、pickle模块复习)
    Python学习第37天(socketserver继承原理、通讯加密)
    Python学习第36天(socketserver模块导入)
    Python学习第35天(粘包)
    个人读书笔记04—业务用例图
    命令模式
  • 原文地址:https://www.cnblogs.com/xbhog/p/14601323.html
Copyright © 2020-2023  润新知