Collection和Map主要实现类

一,Collection和Map

练习1:

•	键盘录入学生信息,保存到集合中。
o	循环录入的方式,1:表示继续录入,0:表示结束录入。
o	定义学生类,属性为姓名,年龄,使用学生对象保存录入数据。
o	使用ArrayList集合,保存学生对象,录入结束后,用foreach遍历集合。
•	代码实现,效果如图所示:

在这里插入图片描述

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

        Scanner scanner = new Scanner(System.in);
        ArrayList stuList = new ArrayList();

        for (;;) {

            System.out.println("选择(录入 1 ;结束 0)");
            int x = scanner.nextInt();//根据x的值,判断是否需要继续循环

            if (x == 1) {
                System.out.println("姓名");
                String name = scanner.next();
                System.out.println("年龄");
                int age = scanner.nextInt();
                Student stu = new Student(age, name);
                stuList.add(stu);

            } else if (x == 0) {
                break;

            } else {

                System.out.println("输入有误,请重新输入");
            }
        }

        for (Object stu : stuList) {
            System.out.println(stu);
        }
    }
}

练习2:

•	随机生成30个数,范围2-100,获取其中的质数。
•	代码实现,效果如图所示:

在这里插入图片描述

写法一:

public class PrimeNumberPrintTest1 {

    public static void main(String[] args) {

        ArrayList numberList = new ArrayList();
        int num = 0;
        for (int i = 0; i < 30; i++) {
        //随机数random*(b-a+1)+a
            num = (int)(Math.random() * (100-2+1) ) + 2;
            if(!numberList.contains(num)){
            	 numberList.add(num);
            }else{
            	i--;
            }
        }

        System.out.println(numberList);
        
        
        
        ArrayList primeList = new ArrayList();//用于存储所有的质数
		
        int j;
        boolean flag;
        for (int i = 0; i < numberList.size(); i++) {
            
            flag = false;
            int number = (int) numberList.get(i);
            for (j = 2; j <= Math.sqrt(number); j++) {
                if (number % j == 0){
                    flag = true;
                    break;
                }
            }
            if (!flag){
            	primeList.add(number);
            }
        }
        
        System.out.println("当前随机数中的质数有:");
        for(Object obj : primeList){
        	System.out.println(obj);
        }
    }

}

写法二:

public class PrimeNumberPrintTest2 {

	public static void main(String[] args) {

		List numberList = generateRandomNumber(30, 2, 100);

		System.out.println(numberList);

		
		List primeList = getPrimeNumber(numberList);

		System.out.println("当前随机数中的质数有:");
		System.out.println(primeList);
	}

	/**
	 * 获取30个随机数,范围为2-100
	 * 
	 * @Description
	 * @author shkstart
	 * @date 2020年3月16日上午9:34:37
	 * @param size
	 * @param startNumber
	 * @param endNumber
	 * @return
	 */
	private static List generateRandomNumber(int size, int startNumber, int endNumber) {
		ArrayList numberList = new ArrayList();
		for (int i = 0; i < size; i++) {
			int num = (int) (Math.random() * (endNumber - startNumber + 1)) + startNumber;
			if (!numberList.contains(num)) {
				numberList.add(num);
			} else {
				i--;
			}
		}

		return numberList;

	}
	/**
	 *  获取指定List中所有数值中的质数
	 * @Description 
	 * @author shkstart
	 * @date 2020年3月16日上午9:37:08
	 * @param numberList
	 * @return
	 */
	private static List getPrimeNumber(List numberList) {
		ArrayList primeList = new ArrayList();// 用于存储所有的质数

		for (int i = 0; i < numberList.size(); i++) {

			boolean flag = false;
			int number = (int) numberList.get(i);
			for (int j = 2; j <= Math.sqrt(number); j++) {
				if (number % j == 0) {
					flag = true;
					break;
				}
			}
			if (!flag) {
				primeList.add(number);
			}
		}
		return primeList;
	}
}

二、复习

  • 字符串相关的类:String\StringBuffer\StringBuilder (重点)

  • 比较器:Comparable \ Comparator (可以实现java中的对象比较大小、排序) (重点)

  • 日期时间的API

    • jdk 8之前的API:java.util.Date (子类:java.sql.Date) 、Calendar、SimpleSDateFormat(格式化、解析)
    • jdk8中API:Instant、LocalDate\LocalTime\ LocalDateTimeDateTimeFormater
  • 其他的常用类:BigInteger、BigDecimal

  • Collection\List\Set\Map存储数据的特点

  • Collection中的常用方法(掌握)

    • 遍历时,使用迭代器(Iterator)
  • List

    • List中的常用方法: 增、删、改、查、插、长度、遍历
    • 三个实现类的对比—>面试题
    • ArrayList、LinkedList的源码分析

三、List的源码分析

3.1 ArrayList的源码分析

3.1 jdk7版本:
 * 	  ArrayList list = new ArrayList();//初始化底层的elementDate的Object[]数组,长度为10.
 *    list.add(123);//elementDate[0] = new Integer(123);
 *    ...
 *    list.add("AA");//一旦添加的数据的个数超出了底层数组的长度10,需要考虑扩容。
 *    默认容量扩容扩容为原来的1.5倍,同时将旧数组中的数据都复制到新的数组中。
 *       
 

源码情况:

 /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this(10);
    }
   
  #######################
  /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
    
  #######################
   public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
  在添加数据之前,需要先判断底层数组的容量是否够:ensureCapacityInternal()
  ####################### 
  private void ensureCapacityInternal(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity); //满足条件的情况,底层进行扩容
    }
 ####################### 
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//默认扩容比例:1.5倍
        //特殊情况的扩容比例:
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //创建新长度的数组,并将原有数组中的数据copy到新的数组中。
        elementData = Arrays.copyOf(elementData, newCapacity);
    } 
*    3.2 jdk8版本:  
 *    ArrayList list = new ArrayList();//初始化底层elementData的Object[]数组为{}
 *    list.add(123);//此时底层创建长度为10的elementData数组,并将new Integer(123)存放到角标0的索引位置。
 *    。。。
 *    list.add("AA");//一旦添加的数据的个数超出了底层数组的长度10,需要考虑扩容。
 *    默认容量扩容扩容为原来的1.5倍,同时将旧数组中的数据都复制到新的数组中。

源码:

/**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
 ####################### 
 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 
 ####################### 
  public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
 ####################### 
 private void ensureCapacityInternal(int minCapacity) {
 		//如果是首次调用add(),则此时的minCapacity赋值为10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
		//如果是首次添加,调用此方法,将原有的{}替换为长度为10的Object[]数组
        ensureExplicitCapacity(minCapacity);
    }
 ####################### 
 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
 ####################### 
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
*    3.3 说明:
 *    jdk7中ArrayList底层的数组的创建类似于单例模式中的饿汉式,jdk8中ArrayList底层的数组的创建类似于单例模式中的懒汉式
 *    

3.2 Vector的源码分析

 * 		Vector v = new Vector();
 * 		v.add(123);
 * 		。。。
 *    > jdk7和jdk8中在创建对象时,底层的操作相同,都是创建长度为10的Object[]。
 *    > 当底层容量不足时,默认扩容为原来的2倍	

源码:

public Vector() {
        this(10);
}
public Vector(int initialCapacity) {
        this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
}
private void grow(int minCapacity) {
        // overflow-conscious code
        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);
    }

3.3 LinkecList的源码分析

 * 	 LinkedList list = new LinkedList();
 *   list.add(123); 
 *   ....
 *   
 *   底层的Node为:记录了当前元素的前一个元素和后一个元素。证明:LinkedList中是双向链表
 *   private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;//前向指针的产生
        }
    }
 * 
 *   LinkedList在添加数据时,元素封装在Node对象中,并指明其前一个和后一个元素。
 *   LinkedList不存在添加时,考虑扩容的问题。因为底层不是使用的数组,在内存中多个元素也不是连续存放的。

源码:

/**
* Constructs an empty list.
 */
 public LinkedList() {
 }
 #########################
 public boolean add(E e) {
        linkLast(e);
        return true;
    }
 #########################
transient Node<E> first;
transient Node<E> last;

 void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)  //返回true,表示是首次添加
            first = newNode;
        else  //表示不是首次添加
            l.next = newNode;//后向指针的产生
        size++;
        modCount++;
    }
 #########################
     private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;//前向指针的产生
        }
    }

 
 

在这里插入图片描述

3.4 结论

 * 小结
 * 	 1. 建议开发中,如果基本确定底层数组的容量,建议使用带参数的构造器
 *   ArrayList list =  new ArrayList(int initialCapacity);//new Object[initialCapacity];
 *   避免底层不断的扩容和复制操作
 *   2. 对于数组来说,查找操作的复杂度是O(1),插入或删除操作的复杂度是O(n)
 *      对于链表来说,查找操作的复杂度是O(n),插入或删除操作的复杂度是O(1)
 *      
 *   开发中,如果很少执行插入或删除操作,建议使用ArrayList
 *   如果频繁的使用插入或删除操作,建议使用LinkedList

四、Set的使用

4.1 主要实现类和方法

  • 主要实现类就是:HashSet
  • 常用方法:Set接口中声明的方法都是Collection接口声明过的。HashSet能使用的就是Collection中定义的方法。
@Test
	public void test1(){
		Set set = new HashSet();
		
		set.add(223);
		set.add(new String("AA"));
		set.add("CC");
		set.add(223);
		set.add(new String("AA"));
		set.add(new Person("Tom",12));
		set.add(new Person("Tom",12));
		set.add(null);
		
		
		for(Object obj : set){
			System.out.println(obj);
		}
		
	}

4.2 Set的特性:无序性、不可重复性

① 无序性:不等同于随机性!元素在底层储存的位置不是像数组一样是依次紧密排列的,而是参考其hashCode值决定的存储位置。理解为无序性

② 不可重复性:根据对象的equals()进行判断。如果返回true,则添加失败。保证了不可重复性。

4.3 向HashSet中添加数据的过程

如何将元素添加到HashSet中的呢?
 *      前提:HashSet底层也是使用数组+链表+(jdk8:红黑树)存储
 *  	① 将元素e1添加到HashSet中,首先调用e1所在类的hashCode(),获取e1对象的哈希值。
 *      ② 此哈希值,经过某种算法以后,获取其在HashSet底层数组中的存放位置。
 *      ③ 如果此位置上,没有其他任何元素,则e1添加成功  --->情况1
 *        如果此位置上,已经存在某个或某几个元素e2,则继续判断。
 *        		④ 比较e1和e2的哈希值,如果两个哈希值不相同。则e1添加成功。   --->情况2
 *        		  比较e1和e2的哈希值,如果两个哈希值相同,则调用e1所在类的equals()方法
 *        				⑤ equals()方法返回false,则e1添加成功。    --->情况3
 *                        equals()方法返回true,则e1添加失败。       
 *     
 *     情况1:将e1直接保存在数组的指定位置
 *     情况2、情况3:此时e1与现有索引位置上的元素,以链表的方式进行保存。
 *     		> jdk7:新的元素e1方法到数组中,指向原有的元素
 *     		> jdk8:已有的元素的末尾指向新的元素e1.
 *          总结:"七上八下"

在这里插入图片描述

4.4 向HashSet中添加的元素所在类的要求

 1. 针对于HashSet或者LinkedHashSet来说,如果多个对象需要存储到上述两个Set中时,为了保证不可重复性,
 *   必须要求对象所属的类要重写hashCode()和equals()
 2. 重写hashCode()和equals()要保证一致性!相等的对象必须具有相等的散列码

4.5 Set的不同实现类的对比

|-----Collection:存储一个一个的数据
 * 		|-----Set:存储无序的、不可重复的数据: 高中的集合
 * 			|-----HashSet:是Set的主要实现类;线程不安全的;可以存储null值
				|-----LinkedHashSet:是HashSet的子类;在添加数据之外,还通过一对指针记录先后添加						的顺序,使得遍历Set元素时,较HashSet效率更高。
			
			|-----TreeSet:可以按照添加的元素的指定的属性的大小进行遍历;底层使用的是红黑树(排序二						叉树的一种)
			

4.6 LinkedHashSet的测试 (了解)

    @Test
	public void test2(){
		Set set = new LinkedHashSet();
		
		set.add(223);
		set.add(new String("AA"));
		set.add("CC");
		set.add(223);
		set.add(new String("AA"));
		set.add(new Person("Tom",12));
		set.add(new Person("Tom",12));
		set.add(null);
		
		for(Object obj : set){
			System.out.println(obj);
		}
		
	}

对应的图示:

在这里插入图片描述

4.7 TreeSet添加数据的情况 (了解)

//1. TreeSet可以按照添加的元素的指定的属性的大小进行遍历;
//	排序的方式有:自然排序,定制排序
//2. TreeSet底层使用的是红黑树(排序二叉树的一种)
//3. 要求:向TreeSet中添加的元素必须是同一个类型的对象。
//4. 说明:TreeSet中不能存放相同的元素。判断的标准不再是元素所在类的hashCode()和equals()了。而是按照自然
//     排序或定制排序中重写的compareTo()或compare()进行比较。
  • 自然排序

    举例1:

@Test
	public void test1(){
		
		TreeSet set = new TreeSet();
		set.add("CC");
		set.add("MM");
		set.add("GG");
		set.add("TT");
		set.add("JJ");
		set.add("KK");
//		set.add(123);//报ClassCastException
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
		
	}

​ 举例2:

@Test
	public void test2(){
		TreeSet set = new TreeSet();
		
		Person p1 = new Person("Tom",12);
		Person p2 = new Person("Jim",32);
		Person p3 = new Person("Jerry",26);
		Person p4 = new Person("Mike",43);
		Person p5 = new Person("Lily",26);
		
		set.add(p1);
		set.add(p2);
		set.add(p3);
		set.add(p4);
		set.add(p5);
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}

其中,Person类定义如下:

public class Person implements Comparable{
	private String name;
	private int age;
	public Person() {
		super();
	}
	public Person(String name, int age) {
		super();
		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 "Person [name=" + name + ", age=" + age + "]";
	}
	/*
	@Override
	public int hashCode() { //return age + name.hashCode();
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		System.out.println("Person equals....");
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	*/
	//先按照年龄从小到大排列,再按照姓名从小到大排
	@Override
	public int compareTo(Object o) {
			
		if(o instanceof Person){
			Person p = (Person)o;
			int value = this.age - p.age;
			if(value != 0){
				return value;
			}else{
				return this.name.compareTo(p.name);
			}
		}
		
		throw new RuntimeException("输入的类型不匹配");
	}
	
	
}
  • 定制排序
@Test
	public void test3(){
		Comparator com = new Comparator(){

			@Override
			public int compare(Object o1, Object o2) {//o1,o2应该为Person的实例
				if(o1 instanceof Person && o2 instanceof Person){
					Person p1 = (Person)o1;
					Person p2 = (Person)o2;
					return -p1.getName().compareTo(p2.getName());
				}
				
				return 0;
			}
			
		};
		TreeSet set = new TreeSet(com);
		
		Person p1 = new Person("Tom",12);
		Person p2 = new Person("Jim",32);
		Person p3 = new Person("Jerry",26);
		Person p4 = new Person("Mike",43);
		Person p5 = new Person("Lily",26);
		
		set.add(p1);
		set.add(p2);
		set.add(p3);
		set.add(p4);
		set.add(p5);
		
		Iterator iterator = set.iterator();
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}

4.8 课后练习

练习1:

HashSet set = new HashSet();
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");

set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);
System.out.println(set);
set.add(new Person(1001,"CC"));
System.out.println(set);
set.add(new Person(1001,"AA"));
System.out.println(set);

其中Person类中重写了hashCode()和equal()方法

练习2:

练习:在List内去除重复数字值,要求尽量简单

答案:
public static List duplicateList(List list) {
      HashSet set = new HashSet();
      set.addAll(list);
      return new ArrayList(set);
}
public static void main(String[] args) {
      List list = new ArrayList();
      list.add(new Integer(1));
      list.add(new Integer(2));
      list.add(new Integer(2));
      list.add(new Integer(4));
      list.add(new Integer(4));
      List list2 = duplicateList(list);
      for (Object integer : list2) {
          System.out.println(integer);
      }
}

练习3:

1. 定义一个Employee类。
该类包含:private成员变量name,age,birthday,其中 birthday 为 MyDate 类的对象;
并为每一个属性定义 getter, setter 方法;
并重写 toString 方法输出 name, age, birthday

MyDate类包含:
private成员变量year,month,day;并为每一个属性定义 getter, setter 方法;

创建该类的 5 个对象,并把这些对象放入 TreeSet 集合中(下一章:TreeSet 需使用泛型来定义)
分别按以下两种方式对集合中的元素进行排序,并遍历输出:

1). 使Employee 实现 Comparable 接口,并按 name 排序
2). 创建 TreeSet 时传入 Comparator对象,按生日日期的先后排序。


五、Map的使用

5.1 Map的架构

|-----Map:存储一对一对的数据(key-value):高中的函数。 y = f(x) (x1,y1),(x2,y2)
 * 		|-----HashMap:主要实现类;线程不安全的,效率高;存储null的key和value
 *			|-----LinkedHashMap:是HashMap的子类,可以按照添加key-value的顺序实现遍历。
 *		底层在HashMap结构的基础上,给前后添加的key-value额外添加了一对指针,记录添加的先后顺序。
 *		|-----TreeMap:可以按照key-value中的key的大小实现排序遍历。底层使用红黑树实现的
 *		|-----Hashtable:古老实现类;线程安全的,效率低;不可以存储null的key或value
 *			|-----Properties:是Hashtable的子类,key和value都是String类型,常用来处理属性文件
 * 

5.2 HashMap中元素的特点

 * Map中的key彼此不可重复、无序  ,使用HashSet存储 ---> key所在的类要重写hashCode()和equals()
 * Map中的value无序、可以重复的,使用Collection存储 ---> value所在的类重写equals()
 * Map中一个key-value对构成了一个Entry
 * Map中的Entry是此次不可重复、无序,使用HashSet存储

对应的图示:

在这里插入图片描述

5.3 HashMap的底层实现原理

  • 原理的描述

    3.1 jdk7中的实现过程:
     *    HashMap map = new HashMap();//底层创建长度为16的Entry数组table
     *    。。。
     *    map.put(key1,value1);
     *    
     *    。。。
     *    
     *    1. 当添加key1-vulue1时,首先通过key1所在类的hashCode()方法,计算key1的哈希值
     *    2. 此哈希值经过某种算法以后,确定其在table数组中的存放位置:i
     *    3. 如果table[i]位置为空,则key1-value1添加成功  --->情况1
     *       如果table[i]位置不为空,则比较key1与table[i]位置现有元素key2-value2进行对比
     *       	4.比较key1和key2的哈希值,如果哈希值不相同,则key1-value1添加成功  --->情况2
     *            比较key1和key2的哈希值,如果哈希值相同,调用key1所在类的equals(),将key2作为参数传入equals()
     *            		5. 如果equals()返回false,则key1-value1添加成功  --->情况3
     *                     如果equals()返回true,则用value1替换原有的value2
     * 
     * 		 情况1:将e1直接保存在数组的指定位置
     *      情况2、情况3:此时e1与现有索引位置上的元素,以链表的方式进行保存。
     *      
     * 	 3.2 jdk8相较于jdk7的不同:
     *new HashMap():底层并没有创建一个长度为16的数组
     *    ② 当首次调用put()时,底层才创建长度为16的数组
     *    ③ jdk8中底层的数组是Node[],而非Entry[]
     *    ④ jdk7:新的元素方法到数组中,指向原有的元素
     *     	jdk8:已有的元素的末尾指向新的元素.
     *    ⑤ 当某一个索引位置上的元素数操作8且数组table的长度超过64时,此索引位置上的所有元素要从链表结构改为红黑树结构
    
  • 源码的显示

    • jdk7中:初始化底层数组的源码:HashMap map = new HashMap()
static final int DEFAULT_INITIAL_CAPACITY = 16;//默认数组的容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认的加载因子
transient Entry<K,V>[] table;//保存Map添加的数据

public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}

public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity) //此循环确定底层数组的长度
            capacity <<= 1;

        this.loadFactor = loadFactor;
        threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        table = new Entry[capacity];//确定了底层数组在默认初始化时,长度为16
        useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        init();
    }

Entry定义如下:

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
}
  • put操作源码

                                    loadFactor);
    
      // Find a power of 2 >= initialCapacity
      int capacity = 1;
      while (capacity < initialCapacity) //此循环确定底层数组的长度
          capacity <<= 1;
    
      this.loadFactor = loadFactor;
      threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
      table = new Entry[capacity];//确定了底层数组在默认初始化时,长度为16
      useAltHashing = sun.misc.VM.isBooted() &&
              (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
      init();
    

    }


Entry定义如下:

static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;

    /**
     * Creates new entry.
     */
    Entry(int h, K k, V v, Entry<K,V> n) {
        value = v;
        next = n;
        key = k;
        hash = h;
    }

}


- put操作源码









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值