集合类框架的基本接口使用及讲述

集合用法

一、集合概述

  • 集合用于存储数量的不确定的对象

  • 集合和数组是不一样的(a、数组既可以存储基本类型的值,也可以是对象,而集合里只能保存对象

    数组长度不可更改,集合长度可以动态 )

  • 集合比数组存储数据更加灵活

  • 集合的图形表

    在这里插入图片描述

  • List的实现类
    Vector
    ArrayList
    LinkedList
    Stack

  • Set的实现类
    HashSet
    TreeSet
    LinkedHashSet

  • Map的实现类
    HashMap
    TreeMap
    LinkedHashMap
    HashTable

  • 通过Map接口,可以产生Collection接口对象

  • Iterator
    迭代器
    通过Collection,获取迭代器对象

  • ListIterator()

1、集合分类

集合可以分为(Set、List、Map)其实也是集合接口

  • List集合(能存储可重复的对象,且也是有序的)这个指的是,你怎么存发数值,他就是怎么输出
一开始给的值: 1,2,3,4,6,5
遍历出来的指: 1,2,3,4,6,5
package demo.cn.com;

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

class Dog{
	private String name;

	public Dog(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

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

public class DemoTest {

	public static void main(String[] args) {
		ArrayList<Dog> arrayList = new ArrayList<Dog>();
		arrayList.add(new Dog("bb"));
		arrayList.add(new Dog("dd"));
		arrayList.add(new Dog("aa"));
		arrayList.add(new Dog("ee"));
		arrayList.add(new Dog("ff"));
		
		Iterator<Dog> iterator = arrayList.iterator();
		while (iterator.hasNext()) {
			Dog dog = (Dog) iterator.next();
			System.out.println(dog);
		}
        
	   //遍历输出可以是foreach 或者是Iterator
		
//		for (Dog dog : arrayList) {
//			System.out.println(dog);
//		}

	}

}

在这里插入图片描述

  • Set集合(不能存储可重复的对象,无序)这个指的是,你怎么存发数值,它随机打印输出
一开始给的值: A,B,C,D,E,F
遍历出来的指: D,B,A,C,F,E
HashSet<Integer> hashSet1 = new HashSet<Integer>();
		hashSet1.add(11);
		hashSet1.add(22);
		hashSet1.add(11);
		hashSet1.add(22);
		hashSet1.add(33);
		hashSet1.add(99);
		hashSet1.add(44);
		hashSet1.add(55);
		hashSet1.add(66);
		hashSet1.add(77);
		hashSet1.add(88);
		for (Integer integer : hashSet1) {
			System.out.println(integer);
		}

在这里插入图片描述

  • Queue集合(队列结合)FIFO 双 端队列
  • Map集合 (它能够存储俩个对象)key-value 映射关系
2、contains的用法
  • 底层源码是调用了equals,且 用来比较两个对象的内容是否相等

二、Collection和Iterator接口

1、Collection的简介
  • Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法即可用于操作Set集合,也可用于操作List和Queue集合

  • boolean add(Object o):添加对象到集合中

    Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素。

    Object[] toArray():该方法把集合转换成一个数组,所有的集合元素变成对应的数组元素。

    • 注意!!!! 当collcetion没有重写equalis方法时,比较的是内存地址,当重写的时候,比较的是内容
2、collection的主要方法
package day23.demo.cn.homework;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

public class MainTest3 {
    public static void main(String[] args) {
        Collection collection = new HashSet();
        collection.add("a");
        collection.add("b");
        collection.add("c");
        collection.add("d");
        collection.add("e");
        collection.add("f");
        collection.add("g");
        collection.add("h");
        collection.add("i");
        collection.add("j");

        Iterator iterator = collection.iterator();
        iterator.forEachRemaining(s-> System.out.println(s));
    }

}

3、Iterator的简介
  • Iterator接口也是Java集合框架的成员,但它与Collection系列、Map系列的集合不一样,它主要用于遍历集合中的元素。
  • boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true。
  • Object next():返回集合里的下一个元素
  • void remove():删除集合里上一次next方法返回的元素

三、Set集合用法

1、概述
  • Set集合复用了Collection的方法,同时也扩展自身的特点,它是基于 HashMap 实现的,底层采用 HashMap 来存储元素
    • 不能添加相同的对象数据
    • 数据是无序
2、HashSet的用法
  • 以哈希码方式存储对象

  • HashSet是线程不安全

  • 不能存储相同的对象

    • 判断对象是否相同的标准:根据 hasCode方法 的返回值及 equals方法 来决定对象是否相同的标准
      • 如果hasCode方法的返回值相同,还有equals方法的返回值为
        true,则认为对象相同
package demo.cn.com;

import java.util.HashSet;

class Student1{
	private int count;
	

	public Student1() {
		
	}

	public Student1(int count) {
		
		this.count = count;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

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

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + count;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Student1) {
			Student1 student = (Student1) obj;
			if (this.getCount() == student.getCount()) {
				return true;
			}
		}
		return false;	
	}
}

public class HashSetTest {

	public static void main(String[] args) {
		HashSet<Student1> set=new HashSet<>();
		set.add(new Student1(10));
		set.add(new Student1(2));
		set.add(new Student1(50));
		set.add(new Student1(40));
		set.add(new Student1(90));
		set.add(new Student1(90));
		Student1 r100=new Student1(100);
		set.add(r100);
		set.add(r100);
		
		for (Student1 student : set) {
			System.out.println(student);
		}
	
	}

}

  • 这个是当没有重写Hashcode跟equals方法的,不是说是HashSet不是无序跟无重复的吗?为什么会出现重复
    因为 set.add(new Student1(90)); 新建了一个对象,而这个对象的hash值是不同的,就会出现如下的情况
Student[count=10]       Student[count=40]     Student[count=90]      Student[count=50] 

Student[count=100]      Student[count=2]      Student[count=90]    
  • 重写了hashcode 跟 equals(进行位置跟内容判断)就不会出现数值相同的情况了

    Student [count=2]     Student [count=50]    Student [count=100]
    Student [count=40]    Student [count=10]    Student [count=90]
                    
    
3、HashSet的存储方式
  • HashSet存储对象以哈希码存储
  • 每个hascode的值,分配一个槽(slot),同一个槽的对象,以链表方式存储
  • 存储方式如下:
    在这里插入图片描述
4 、TreeSet的用法
  • TreeSet可以确保集合元素处于排序状态
  • TreeSet也是属于Set集合类别一种
  • 不能存储相同的对象
  • 保证集合对象以排序状态显示
    • 自然排序实现(TreeSet的默认排序)
    • 定制排序实现
  • TreeSet 默认调用的是TreeMap方法:底层是二叉树。TreeMap集合的key可以自动按照大小顺序排序
a、自然排序
  • 自然排序是TreeSet排序的默认规则
  • 此类必须要实现Comparable接口
    • 重写compareTo方法
    • 如果该方法返回0,则表明这两个对象相等;如果该方法返回
      一个正整数,则表明obj1大于obj2;如果该方法返回一个
      负整数,则表明obj1小于obj2
package demo.cn.com;
//这个是使用自然排序实现

import java.util.TreeSet;

class Student1{
	private int count;

	public Student1() {
		
	}

	public Student1(int count) {	
		this.count = count;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

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

public class HashSetTest {
	public static void main(String[] args) {
		TreeSet<Student1> set=new TreeSet<>();
		set.add(new Student1(10));
		set.add(new Student1(2));
		set.add(new Student1(50));
		set.add(new Student1(40));
		set.add(new Student1(90));
		set.add(new Student1(90));
		Student1 r100=new Student1(100);
		set.add(r100);
		set.add(r100);
		
		for (Student1 student : set) {
			System.out.println(student);
		}
	}
}

点击运行的代码时候报错,这个里面的报错指的TreeSet的接口是SortsTree,也是实现排序,也就是说这个接口需要实现Comparable来重新compareTo就行数值比较,而实现排序

在这里插入图片描述

修改的代码:

在这里插入图片描述

在这里插入图片描述

b、定制排序
  • 是通过实现Comparator接口重现compare接口,判断里面的值是返回-1、0、1

  • 此类不需要实现Comparable接口

    • 如果需要实现定制排序,则需要在创建TreeSet集合对象时,提供一个
      Comparator对象与该TreeSet集合关联,由该Comparator对象负责
      集合元素的排序逻辑。
  • package demo.cn.com;
    import java.util.Comparator;
    import java.util.TreeSet;
    
    
    class Phone{
    	private String phoneName;
    	private int score;
    	private int weight;
    	
    	
    	public Phone() {
    		
    	}
    	
    	
    	
    	public Phone(String phoneName, int score, int weight) {
    		super();
    		this.phoneName = phoneName;
    		this.score = score;
    		this.weight = weight;
    	}
    
    
    
    	@Override
    	public String toString() {
    		return "Phone [phoneName=" + phoneName + ", score=" + score + ", weight=" + weight + "]";
    	}
    
    
    	public String getPhoneName() {
    		return phoneName;
    	}
    	public void setPhoneName(String phoneName) {
    		this.phoneName = phoneName;
    	}
    	public int getScore() {
    		return score;
    	}
    	public void setScore(int score) {
    		this.score = score;
    	}
    	public int getWeight() {
    		return weight;
    	}
    	public void setWeight(int weight) {
    		this.weight = weight;
    	}	
    }
    public class PhoneTest {
    	
    	public static void main(String[] args) {
    		
    		TreeSet<Phone> set = new TreeSet<Phone>(new Comparator<Phone>() {
    			@Override
    			public int compare(Phone o1, Phone o2) {
    				if (o1.getScore()>o2.getScore()) {
    					  return 1; 
    					  }
    				else if (o1.getScore()<o2.getScore()){
    						  return -1;			  
    					  }else {
    						  return 0; 
    					}
    				
    				}
    		});
    		
    		for (int i = 0; i < 10; i++) {
    			int score = (int) (Math.random()*1000);
    			int weight = (int) (Math.random()*10);
    			    Phone phone = new Phone();
    				phone.setPhoneName("型号"+i);
    				phone.setScore(score);
    				phone.setWeight(weight);
    				set.add(phone);
    		}	
    		for (Phone phone : set) {
    			System.out.println(phone);
    		}	
    	}
    }
    
    c、实现TreeSet低层算法
    • 实现红黑树数据结构实现排序功能

    在这里插入图片描述

    1.节点是红色或黑色。

    2.根节点是黑色。

    3.每个叶子节点都是黑色的空节点(NIL节点)。

    4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

    5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

5、Comparable的描述
  • Java的一些常用类已经实现了Comparable接口,并提供了比较大小的标准。下面是实现了Comparable接口的常用类:

  • BigDecimal、BigInteger以及所有的数值型对应的包装类;按它们对应的数值大小进行比较。

  • Character:按字符的UNICODE值进行比较。

  • Boolean:true对应的包装类实例大于false对应的包装类实例。

  • String:按字符串中字符的UNICODE值进行比较。

  • Date、Time:后面的时间、日期比前面的时间、日期大

四、LinkedHashSet集合用法

1、简介
  • LinkedHashSet 是 HashSet 的子类
  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
  • LinkedHashSet 不允许集合元素重复。
2、分析存储数据原理
  • 链表形式存在

在这里插入图片描述

  • 以哈希码存储数据
  • 数据与数据之间使用链表维护,因此数据是有序输出
package demo.cn.com;

import java.util.LinkedHashSet;

class R{
	private int count;
	
	public R() {
	}

	public R(int count) {
		this.count = count;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

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

public class LinkedHashSetTest {

	public static void main(String[] args) {
        LinkedHashSet<R> set2=new LinkedHashSet<>();
		
		set2.add(new R(100));
		set2.add(new R(50));
		set2.add(new R(80));
		set2.add(new R(90));
		set2.add(new R(20));
		set2.add(new R(10));
		
        //有序输出
		set2.forEach(System.out::println);
		

	}

}

五、Set集合的性能分析

  • 对比HashSet与TreeSet
    • 从性能角色:HashSet>TreeSet
      • 随机读取能力
      • 添加数据能力
      • 遍历数据能力
  • 对比HashSet与LinkedHashSet
    • 针对读取、写入HashSet要比LinkedHashSet性能要好
    • 针对整体遍历,LinkedHashSet要比HashSet性能要好

六、List集合用法

1、简介
  • List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引,例如第一个添加的元素索引为0,第二个添加的元素索引为1**(ArrayList是线程不安全)**
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

public class MainTest {

	public static void main(String[] args) {
		
		//创建ArrayList对象
		ArrayList<String> books=new ArrayList<>();
		
		books.add("thinking in java");
		books.add("thinking in c++");
		books.add("thinking in python");
		books.add("thinking in c");
		
		System.out.println("写法一:如何遍历list集合");
		//如何遍历list集合
		books.forEach(System.out::println);
		
		System.out.println("写法二:如何遍历list集合");
		for(String v:books){
			System.out.println(v);
		}
		
		System.out.println("写法三:如何遍历list集合");
		Iterator<String> iter=books.iterator();
		while(iter.hasNext()){
			String value=iter.next();
			System.out.println(value);
		}
		
		System.out.println("写法四:如何遍历listIterator集合");
		ListIterator<String> iterList=books.listIterator();
		while(iterList.hasNext()){
			String value=iterList.next();
			System.out.println(value);
		}
		
		System.out.println("listIterator支持反向遍历输出");
		while(iterList.hasPrevious()){
			String value=iterList.previous();
			System.out.println(value);
		}
		
		System.out.println("写法五:通过下标值遍历list集合");
		for(int index=0;index<books.size();index++){
			//根据index,获取对象数据
			String book=books.get(index);
			System.out.println(book);
		}
		
		System.out.println("写法六:将ArrayList转换成数组");
		Object []objectArrays=books.toArray();
		for(Object o:objectArrays){
			if(o instanceof String)
				System.out.println((String)o);
		}
		
		System.out.println("删除index=2");
		books.remove(2);
		books.forEach(System.out::println);
	}
}

2、List集合的特有迭代器ListIterator
  • 与Set只提供了一个iterator()方法不同,List还额外提供了一个listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,在List方法的基础上,还增加了如下方法:

  • boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素

  • Object previous():返回该迭代器的上一个元素

  • void add():在指定位置插入一个元素

3、ArrayList 源码分析
  • 针对jdk1.8版本ArrayList

  • 底层是使用对象类型的数组存储

  • 当第一次add方法调用,才实现数组长度的初始化,默认长度10

  • 当调用add方法,首先判断 size+1与数组当前的长度的大小,来决定是否扩容

    if( size+1>数组.length())
    {
        //数组扩容处理
        grow();
    }
    
    • 如何扩容数组

      • 扩容数组后的数组长度=原先数组的长度*1.5倍

        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);
            }
        
  • 如何实现删除

在这里插入图片描述

  public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
  • 根据下标,添加数据

在这里插入图片描述

public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
package 第三题;

import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.Objects;
import java.util.function.Consumer;

public class MyList<E> {
	
    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    transient Object[] elementData; // non-private to simplify nested class access
    private int size;
    
    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;
    
    
	
	public MyList()
	{
		this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
	}
	
    public boolean add(E e) {
    	//计算数组的长度容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    //主要计算数组的长度
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    	//当第一次调用add,数组未初始化,默认长度为DEFAULT_CAPACITY常量 
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        
        //如果此数组已经存在数据,则直接返回minCapacity值
        //minCapacity值=size+1
        //size:就是数组的下标
        return minCapacity;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    private void ensureExplicitCapacity(int minCapacity) {

        // overflow-conscious code
    	//判断是否数组扩容
    	//size+1>elementData.length,则进行扩容处理
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    
    private void grow(int minCapacity) {
        // overflow-conscious code
    	//获取扩容之前的数组长度
        int oldCapacity = elementData.length;
        //扩容之后的数组长度=1.5*扩容之前的数组长度;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //minCapacity>newCapacity,则newCapacity=minCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // minCapacity is usually close to size, so this is a win:
        //针对数组进行扩容,扩容长度为newCapacity
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    E elementData(int index) {
        return (E) elementData[index];
    }
    
    public E remove(int index) {

        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
    
    public void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        for (int i=0;  i < size; i++) {
            action.accept(elementData[i]);
        }
    }
}

4、List的性能比较

在这里插入图片描述

七、Map集合

1、Map集合特点(HashMap、TreeMap、LinkedHashMa)
  • 存储key,value的具有映射有关系的集合对象

  • key、value都是对象类型,其实就是引用

  • key其实就是Set集合类型

    • key不能重复存储相同的对象
  • value就是Collection类型

    • 允许可重复的对象
  • key与value之间存在映射关系

    • 一个key,只能映射一个value对象
    • 但同一个value对象,可以被多个key映射
2、HashMap的具体用法
  • HashMap线程不安全
  • HashMap可以使用null作为key和value
  • HashMap与HashSet一样,判断key的存储对象是否相同的标准:根据hasCode方法的返回值和equals方法的返回值决定此对象是否相同
package demo.cn.com;

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

class Shop{
	private int shopId;
	private String shopName;
	public Shop() {
	}

	public Shop(int shopId, String shopName) {
		this.shopId = shopId;
		this.shopName = shopName;
	}

	public int getShopId() {
		return shopId;
	}
	
	public void setShopId(int shopId) {
		this.shopId = shopId;
	}
	
	public String getShopName() {
		return shopName;
	}
	
	public void setShopName(String shopName) {
		this.shopName = shopName;
	}
	
	@Override
	public String toString() {
		return "Shop [shopId=" + shopId + ", shopName=" + shopName + "]";
	}

	@Override
	public int hashCode() {
		return getShopId();
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Shop) {
			Shop oteShop = (Shop) obj;
			if (oteShop.getShopId() == this.getShopId()) {
				return true;
			}
		}
		return false;
	}		
}

class Person{
	private String personName;
	private int personAge;
	
	
	
	
	public Person() {
		
	}
	public Person(String personName, int personAge) {
		this.personName = personName;
		this.personAge = personAge;
	}
	public String getPersonName() {
		return personName;
	}
	public void setPersonName(String personName) {
		this.personName = personName;
	}
	public int getPersonAge() {
		return personAge;
	}
	public void setPersonAge(int personAge) {
		this.personAge = personAge;
	}
	@Override
	public String toString() {
		return "Person [personName=" + personName + ", personAge=" + personAge + "]";
	}	
}

public class HashMapTest {
	public static void main(String[] args) {
		HashMap<Shop, Person> map = new HashMap<>();
		map.put(new Shop(1, "aa"), new Person("张一", 18));
		map.put(new Shop(2, "bb"), new Person("张二", 19));
		map.put(new Shop(3, "cc"), new Person("张三", 20));
		map.put(new Shop(4, "dd"), new Person("张四", 21));
		map.put(new Shop(5, "ee"), new Person("张五", 22));
		map.remove(new Shop(3, "cc"));
		
//		for (Map.Entry<Shop,Person> entry : map.entrySet()) { //这个是普通的排序方式
//			Shop KeyShop = entry.getKey();
//			Person ValuePerson = entry.getValue();
//			System.out.println(KeyShop);
//			System.out.println(ValuePerson);
//			
//		}
		
		map.forEach((k,v)->System.out.println("key"+v+"value"+v));
	}
}

3、TreeMap的具体使用
  • TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。

  • TreeMap两种排序方式(跟TreeMap使用是一样的)

  • 自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则将会抛出ClassCastException异常。

  • 定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。采用定制排序时不要求Map的key实现Comparable接口中。

package demo.cn.com;

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

public class TreeMapTest {

	public static void main(String[] args) {
		//其实跟TreeSet是类似的也是有自然排序跟定制排序
		//自然排序
		
		TreeMap<Integer, String> treeMap= new TreeMap<Integer, String>();
		treeMap.put(1, "a");
		treeMap.put(12, "b");
		treeMap.put(3, "c");
		treeMap.put(4, "d");
		treeMap.put(2, "e");
		
		System.out.println("普通输出");
		for (Map.Entry<Integer, String> sEntry: treeMap.entrySet()) {
			System.out.println(sEntry);
		}
		
		System.out.println("Lambda输出");
		treeMap.forEach((k,v)->{
			System.out.println(k+"\t"+v);
		});
		
		
		
		//用匿名内部类来实现数Comparator,不然会爆错,可以参考上面的笔记
		TreeMap<Integer, String> treeMap1= new TreeMap<Integer, String>(new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				// TODO Auto-generated method stub
				return o1>o2?-1:o1<o2?1:0;
			}
		});
		treeMap1.put(1, "a");
		treeMap1.put(12, "b");
		treeMap1.put(3, "c");
		treeMap1.put(4, "d");
		treeMap1.put(2, "e");
		
		System.out.println("定制输出+++普通输出");
		for (Map.Entry<Integer, String> sEntry: treeMap1.entrySet()) {
			System.out.println(sEntry);
		}
		
		System.out.println("定制输出+++Lambda输出");
		treeMap1.forEach((k,v)->{
			System.out.println(k+"\t"+v);
		});
	}
}

4、LinkedHashMap的具体使用
  • HashSet有一个子类是LinkedHashSet,HashMap也有一个LinkedHashMap子类; LinkedHashMap也使用双向链表来维护key-value对的次序(其实只需要考虑Key的次序),该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。(有序):有序指的是你怎么存入怎么输出是一样的
package demo.cn.com;

import java.util.HashSet;
import java.util.LinkedHashSet;

class R{
	private int count;
	
	public R() {
	}

	public R(int count) {
		this.count = count;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

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

class Person1{
	private int id;
	private String name;
	public Person1(int id, String name) {
		this.id = id;
		this.name = name;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

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

public class LinkedHashSetTest {

	public static void main(String[] args) {
        LinkedHashSet<R> set2=new LinkedHashSet<>();
		set2.add(new R(100));
		set2.add(new R(50));
		set2.add(new R(80));
		set2.add(new R(90));
		set2.add(new R(20));
		set2.add(new R(10));		
        //有序输出
		set2.forEach(System.out::println);

	}
}

5、HashMap的源码分析
  • https://www.cnblogs.com/9dragon/archive/2019/12/09/12005142.html介绍HashMap的源码信息
a、HashMap的无参构造
public HashMap() {
        //负载因子=0.75
    	//当数据达到3/4,则进行扩容处理
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
b、put方法中hash值
  • 首先是扰动算法,主要是获取一个均衡的hash指

     static final int hash(Object key) {
            int h;
            //取hashcode方法数据值的高16位与低16位异或操作:就会生成一个均衡的hash值
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    
c、put方法
  • 参数一:hash:通过上述方法生成(均衡的hash值)
  • 参数二:key:添加的key值
  • 参数三:value:添加的value值
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
       //定义一个数组,数组类型:Node(链表的对象)
        Node<K,V>[] tab; Node<K,V> p; int n, i;
       //如果数组为空,则进行扩容
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //通过hash值获取一个数组单元(桶)
        if ((p = tab[i = (n - 1) & hash]) == null)
            //如何此数组单元为空对象,则直接添加一个节点对象进来
            tab[i] = newNode(hash, key, value, null);
        else {
            //数组单元已经存在节点对象
            Node<K,V> e; K k;
            //就比较数组单元已经存在的节点对象与将要添加的key的equals方法和hash值,如果返回true及hash值相同,则认为两者的对象相同
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //将数组单元已经存在的节点对象赋值给e
                e = p;
            else if (p instanceof TreeNode)
                //数组单元已经存在的节点对象如果是树节点,则将节点添加到红黑树
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //遍历已经存在的链表节点,将节点添加到链接
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //如果此链接节点的个数>=7,则构建成一颗红黑树
                        if (binCount >= TREEIFY_THRESHOLD - 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;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值