集合框架

集合框架

软件包 java.util

包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。

1.集合

探究集合的特点:

  • 是否有序: [2,4,5,3] -> [2,4,5,3]
  • 是否唯一
  • 是否排序:[2,4,5,3] -> [2,3,4,5]
  • 效率
  • 线程安全问题
  • 能否存储null值

1.1集合的特点

  1. 能够存储任意数据类型
  2. 集合能够动态扩容,长度可以改变
  3. 集合只能够存储引用数据类型,但是可以存储基本数据类型对应的包装类类型
  4. 集合针对元素的操作提供了(增删查改)API
  5. 集合是一套框架,针对不同的容器提供不同的类

1.2集合容器的底层依赖数据结构

  • 常用的和集合相关的数据结构有: 栈, 队列, 链表, 哈希表(hashCode), 数组, 二叉树;
  • 效率的决定因素: 数据结构 + 算法 + 硬件

1.3数组和集合的区别

数组集合
长度长度是固定不变根据实际需求改变
内容存储的是同一种类型的元素存储不同类型的元素
数据类型数组可以存储基本数据类型,也可以存储引用数据类型集合只能存储引用类型,但是可以存储基本数据类型的包装类类型

2.Collection

单列集合 :List 有序 可重复; Set 无序 唯一

2.1Collection的方法

  • 增加

    说明
    boolean add(Object e)增加一个Object对象
    boolean addAll(Collection<? extends E> c)增加一个集合
  • 删除

    说明
    boolean remove(Object o)删除一个Object对象
    boolean removeAll(Collection<?> c)删除一个集合
    void clear()清除集合内所有内容
  • 修改(Collection没有修改的方法,因为不知道子类是什么数据结构类型,没办法决定修改的方式)

  • 查询【遍历】

    说明
    Object[] toArray()
    T[] toArray(T[] a)
    Iterator iterator()迭代器
  • 判断

    说明
    boolean contains(Object o)是否包含一个Object对象
    boolean containsAll(Collection<?> c)是否包含一个集合
    boolean isEmpty()是否为空
  • 获取

    说明
    int size()集合长度
  • 其他

    说明
    boolean retainAll(Collection<?> c) 【交集】返回原集合在求交集之后是否发生改变没有改变返回false; 发生改变返回 true

2.2Collection的遍历方法

Collection有4种,遍历方式:

  1. 迭代器
  2. foreach
  3. toArrays
  4. for循环(利用迭代器)
2.2.1ArrayList的toString()源码
	public String toString() {
        Iterator<E> it = c.iterator();
        //判断是否为空,提高效率
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('['); 
        for (;;) {
            Object e = it.next(); 
            //this是调用者c,正常情况下,e不等于c的,
            //因为e是对象,c是集合
            //当在add一个集合时就会出现e == this,
            //打印“this Collection”
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }
2.2.2为什么需要迭代器这个玩意?
  • 因为Collection是无序的,而使用for循环遍历是需要索引(index),除非向下转型为List,利用List有序的特性,才能使用for循环,所以需要迭代器来对Collection集合进行遍历
2.2.3迭代器的异常问题
  • java.util.NoSuchElementException:迭代器中没有元素了,如果再取之抛出该异常,如果希望解决,在迭代之前判断
  • java.util.ConcurrentModificationException
    • 异常名称:并发修改异常
    • 产生原因:在使用迭代器对象迭代的同时,使用原集合就该了元素
		//AB是同一个集合的元素
		while (it.hasNext()) {
			Object obj = it.next();
			if (obj.equals("A")) {
				c.remove("B");
			}
		}
		//系统会报异常:java.util.ConcurrentModificationException
		//因为obj.equals("A")是迭代器线程的
		//而c.remove("B");则在调用原集合
		//会导致同时执行两个不同的集合从而报错
2.2.3.1迭代器的内存原理解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uvvdOs3E-1574599617946)(E:\尚学堂\TYPORA_NOTES\14.1 集合框架.assets\迭代器内存解析.png)]

  • 迭代器和原集合不是同一个集合,迭代器包括cursor(指针)
  • 同时运行两个集合就会出并发修改异常
2.2.3.2并发修改异常的解决方式
  • Object[] toArray()

    		//AB是同一个集合的元素		
    		for (Object obj : objs) {
    		System.out.println(object);
    			if (obj.equals("A")) {
    				c.remove("B");
    			}
    		}
    		//因为toArray() 把原集合转化为数组了,所以遍历和移除都在同一个数组上
    		//所以不存在并发
    		//缺陷:把集合装换成数组了
    
2.2.4迭代器的理解易错点
  • iterator.next( )在循环中每一次出现都会迭代一次

    Iterator<String> iterator = map.keySet().iterator();
    		while (iterator.hasNext()) {
    			//key是键,所以用map.put(key)获取value
    			String key = iterator.next();
                String value = map.get(key);
    			System.out.println(key + "=" + value);
                //结果:Tom=John
    			//Tom2=John2
    			//Tom1=John1
                
    			//String value = map.get(iterator.next());
    			//System.out.println(iterator.next() + "=" + value);
                //结果:Tom=John
    			//因为代码中出现了三次的iterator.next(),使一次循环多迭代了2次
    		}
    
2.2.5 4种遍历方式
  1. 迭代器

    		Iterator it = c.iterator();
    		while (it.hasNext()) {
    			Object obj = it.next();
    			System.out.println(obj);
    		}
    
  2. foreach:数组和集合的专有遍历的方式

    		for (Object obj : c) {
    			System.out.println(obj);
    		}
    
  3. toArrays

    		Object[] objs = c.toArray();
    		for (Object obj : objs) {   //for (Object obj : c.toArray())
    			System.out.println(obj);
    		}
    
  4. for循环(利用迭代器)

            for (Iterator iterator = c.iterator(); iterator.hasNext();) {
                    Object obj = iterator.next();
                }
                //简便写法
                for(Iterator iterator = c.iterator(); it.hasNext(); 				System.out.println(it.next()));
    

2.3去重复

  • 方式一: 针对不确定是否有序的元素 【创建新的集合,地址传递】
  • 方式二: 针对有序的元素可以使用选择排序思想去除重复元素
  1. 方式一: 针对不确定是否有序的元素 【创建新的集合,地址传递】

            public class CollectionDemo04 {
    
                public static Collection c;
    
                public static void main(String[] args) {
                    c = new ArrayList();
                    c.add("孙悟空");
                    c.add("孙悟空");
                    c.add("猪八戒");
                    c.add("嫦娥");
                    c.add("嫦娥");
                    c.add("唐僧");
    
                    removeRepeatElement();
                    System.out.println(c);
    
                }
    
                private static void removeRepeatElement() {
                    // 1.创建一个新的集合
                    Collection newC = new ArrayList();
    
                    // 2.遍历旧集合
                    for (Object obj : c) {
                        // 3.判断新集合是否存在该遍历的元素
                        if (!newC.contains(obj)) {
                            // 4.如果新集合中不存在该元素,就添加到集合中
                            newC.add(obj);
                        }
                    }
                    // 5.地址传递
                    c = newC;
                }
            }
    
  2. 方式二: 针对有序的元素可以使用选择排序思想去除重复元素

            // 2.去除重复的学生
                    for (int i = 0; i < list.size() - 1; i++) {
                        for (int j = i+1; j < list.size(); j++) {
                            if (list.get(i).equals(list.get(j))) {
                                list.remove(j);
                                j--;
                            }
                        }
                    }
    
2.3.1contains的源码解析

Object的equals默认比较的是地址,需要重写

        c2.contains(obj);
        Object[] elementData; 1001 1002 1003 1004 
                                1001
        public boolean contains(Object o) {
            return c2.indexOf(o) >= 0;
        }
                            1001
        public int indexOf(Object o) {
          	//判断是否是空,提高效率
            if (o == null) {
                for (int i = 0; i < size; i++)
                    if (elementData[i]==null)
                        return i;
            } else {
                //如果o和数组 Object[] elementData内元素相等
                //就返回正数,contains为true,则表示重复
                //不相等就返回-1,contains为false,则表示不重复
                for (int i = 0; i < size; i++)
                    if (o.equals(elementData[i]))
                        return i;
            }
            return -1;
        }

3.List

特点

  • 有序

  • 可重复

3.1List的方法

List 是Colletion的子集,所以继承Collection的所有方法,只需要探究List特有方法

  • 添加

    说明
    void add(int index, E element)根据索引添加一个对象
    boolean addAll(int index, Collection<? extends E> c)根据索引添加一个集合
  • 删除

    说明
    E remove(int index)根据索引移除一个对象
  • 修改

    说明
    E set(int index, E element)根据索引修改一个对象
  • 查询【遍历】

    说明
    E get(int index)根据索引获取一个对象
    ListIterator listIterator()迭代器
    ListIterator listIterator(int index)List的迭代器
  • 判断

  • 获取

    说明
    E get(int index)根据索引获取一个对象
    int indexOf(Object o)从左到右,根据对象,获取相应的索引
    int lastIndexOf(Object o)从右到左,根据对象,获取相应的索引
    List subList(int fromIndex, int toIndex)截取,不影响原集合左开右闭原则
    		//indexof获取索引的定位思维		
    		System.out.println(list.get(list.size() - 1));
    		System.out.println(list.indexOf("A"));	
    

3.2List的遍历

List有5种遍历方式

  1. 普通for

  2. ListIterator listIterator() / ListIterator listIterator(int index)

  3. toArray

  4. 迭代器

  5. foreach

3.2.1List的遍历方式
  • 普通for

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

    • 想比较与Collection的迭代器,ListIterator可以通过索引,选择遍历开始的地方
    • 有逆向遍历的功能
    • remove()没有被defalut,throw异常
3.2.2 解决并发修改异常
3.2.2.1 解决思路

迭代迭代器元素的时候同时使用了原集合修改元素

  • 不用迭代器,就使用原集合遍历,原集合修改 普通for
  • 迭代器遍历,迭代器修改 列表迭代器

3.3ArraryList的源码解析

3.3.1rrayList动态扩容原理 【了解】
  • 1.扩容倍数1.5倍
  • 2.ArrayList动态扩容底层还是依赖 Arrays.copyOf方法,创建了一个新的数组,然后利用System.arraycopy()方法进行拷贝的

扩容原理:

  • 1.计算最小容量:如果内部数组的长度超过了10,则最小容量则最小容量为为数组的实际长度,否则最小长度就是10

    		if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    	        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //10
    	    }
    
  • 2.扩容时机:计算最小容量,如果最小容量超过了内部数组的长度,则进行

    		if (minCapacity > elementData.length)
                grow(minCapacity);
    
  • 3.扩容原理: 扩容的实际大小等于内部数组长度的1.5倍

    • a.如果扩容后长度还比最小长度小,那么就使用最小长度作为新容量
    • b.如果扩容后长度比最小长度大,那么就是用原来长度的1.5倍作为新容量
    • c.如果扩容长度比最大长度Integer.MAX_VALUE-8还要大
      • (1)如果最小长度超出了Integer的范围导致变成负数溢出,则抛出内存溢出
      • (2)如果最小长度比最大数组长度大,但是比Integer范围小,
      • 就设置Integer范围的最大值为新的容量,否则设置最大数组长度为新容量
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");

class ArrayList {

	private int size; //开始集合size为0
	transient Object[] elementData; //数组为空数组
	private static final int DEFAULT_CAPACITY = 10; //默认容量为10
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    //最大数组容量 = Integer最大值 - 8
	
	public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    				"hello"
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    								//size + 1 = 0 + 1 = 1
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 10
        }
        //当elementData等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA,
        //比较默认容量和最小容量,较大值赋值给minCapacity

        ensureExplicitCapacity(minCapacity);
    }
    									//默认容量为10,所以除了自己定义的,一般最小为10
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; // 对容器修改了一次

        // overflow-conscious code
        if (minCapacity > elementData.length)
            grow(minCapacity);
    }
    					10
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length; 7
        int newCapacity = oldCapacity + (oldCapacity >> 1);//(>> 1)除2的意思
        // oldCapacity * 1.5  Integer
                            
        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);
    }
    
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow内存溢出,抛出错误
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }//minCapacity >  MAX_ARRAY_SIZE.就赋值为 Integer.MAX_VALUE
}

ArrayList的排序

  • ArrayList默认是不能排序的,但是可以通过Collections工具类进行排序

    • 使用Collections对Comparator进行重写

      Collections.sort(list, new Comparator<Student>() {
      
      			@Override//比较器重写
      			public int compare(Student s1, Student s2) {
      				Collator c = Collator.getInstance(Locale.CHINA);
      				// 先按照总分排序
      				int cmp = s1.getTotalScore().compareTo(s2.getTotalScore());
      				int cmp2 = (cmp == 0) ? s1.getChinesScore().compareTo(s2.getChinesScore()) : cmp;
      				int cmp3 = (cmp2 == 0) ? s1.getEnglishScore().compareTo(s2.getEnglishScore()) : cmp2;
      				int cmp4 = (cmp3 == 0) ? s1.getMathScore().compareTo(s2.getMathScore()) : cmp3;
      				int cmp5 = (cmp4 == 0) ? s1.getAge().compareTo(s2.getAge()) : cmp4;
      				int cmp6 = (cmp5 == 0) ? c.compare(s1.getName(), s2.getName()) : cmp5;
      				return cmp6;
      			}
      		});
      

4.泛型

泛型的由来: 模仿了数组方法。数组: 编译时期确定元素类型;方法: 参数化类型

Java提供了泛型是JDK1.5之后引入的新特性,一种将类型提前在编译时期确定的参数化类型

		//格式
			<T,E> 
    		//这个T必须是引用数据类型,这里可以叫T,E,H,26个字母都可以,这里可以有多个泛型类型

4.1泛型的优点

  • 提高了可读性

  • 2.提高了维护性

  • 3.提高了扩展性

  • 4.消除了黄色警告线,提高了安全性

  • 5.简化了代码

  • 6.去除了类型判断,提高了效率

4.2泛型类

泛型类是一项独立的技术,和集合无关,是因为Object

4.2.1泛型类特点
  • 泛型具有传递性

  • 泛型可以定义多个

  • 泛型将定义类的时候确定元素类型转而交给了调用者调用或者new的时候确定类型

        class GenericClass<E,T> {//可以定义多个
            private E e;
            private T t;


            public E getE() {
                return e;
            }

            public void setE(E e) {
                this.e = e;
            }

            public T getT() {
                return t;
            }

            public void setT(T t) {
                this.t = t;
            }

        }

4.3泛型接口

4.3.1泛型接口的特点
  • 实现类时候确定泛型

  • 创建接口的子类对象的时候确定泛型(new)

  • 匿名内部类确定泛型

    		IGenericInterface<String, Boolean> gi = new IGenericInterface<String, Boolean>() {
    
    			@Override
    			public void show(String e) {
    				System.out.println(e);
    			}
    
    			@Override
    			public Boolean get(Boolean t) {
    				return t;
    			}
    		};
    
    
4.3.2compareTo的重写
		Student s1 = new Student("阿三", 30, 98);
		Student s2 = new Student("钱八", 30, 98);
		int cmp = s1.compareTo(s2);
		System.out.println((cmp > 0) ? "张三大" : (cmp < 0 ? "李四大": "一样大"));

class Student implements Comparable<Student>{
	private String name;
	private int age;
	private double score;
	//省略构造方法、set、get
    //compareTo的重写
	@Override
	public int compareTo(Student s) {
		Collator c = Collator.getInstance(Locale.CHINA);
		int cmp = this.getAge() - s.getAge();
		int cmp2 = (cmp == 0) ? c.compare(this.getName(), s.getName()): cmp;
		double cmp3 = (cmp2 == 0) ? this.getScore() - s.getScore() : cmp2;	
		int cmp4 = cmp3 > 0 ? 1 : (cmp3 < 0 ? -1 : 0);
		return cmp4; 
	}	
}

4.4泛型方法

4.4.1泛型方法的特点
  • 泛型方法也称为局部泛型

  • 泛型方法在方法调用的并且传参的时候确定类型

		//在调用方法的时候,且把实参传给形参时确定数据类型
		gm.method("good", 'a');//string,char 
		gm.method(2.5, false); //double,boolean      

		class GenericMethod<E, T> {
            public <K, V> void method(K k, V v) {
                System.out.println(k);
                System.out.println(k.getClass().getName());
                System.out.println(v);
                System.out.println(v.getClass().getName());
            }
        }

4.4.2 T[] toArray(T[] a) 的作用

有了 Object[] toArray() 方法为什么还需要 T[] toArray(T[] a) 方法?

		//可以在遍历前就确定数据类型,避免了向下转型所带来的弊端
		//向下转型的弊端,需要不不断添加else if(多态的弊端改善的缺陷)
		Collection<Integer> c2 = new ArrayList<Integer>();
		c2.add(100);
		c2.add(200);
		c2.add(300);
		Integer[] iis = c2.toArray(new Integer[] {});
		for (Integer integer : iis) {
			System.out.println(integer);
		}

5.HashSet类

  • 为什么是无序的?

    • 因为哈希值的计算和对象本身的hashCode有关,而hashCode在默认不重写的情况下返回的是地址的转换值
  • 为什么可以去重复? --> 底层依赖的是hashCode和equals方法

    • 首先比较hashCode值
    • 如果hashCode值一样,比较equals
  • 为什么从第二次开始每次都是一样的?

    • 因为hashCode和对象本身有关,所以同一个对象的hashCode方法返回的值一样,所以哈希值一样

5.HashSet的源码解析

HashSet<String> hs = new HashSet<>();
hs.add("ab");
hs.add("ac");

class HashSet {

	private transient HashMap<E,Object> map;
	private static final Object PRESENT = new Object(); // 0x0001

	public HashSet() {
        map = new HashMap<>();
    }
    				"ab"
    public boolean add(E e) {
    				"ab"	
        return map.put(e, 0x0001)==null;
    }
}

1.HashSet底层实现是 HashMap,HashMap底层数据结构是 哈希表
2.数据存储到哈希表结构当中是通过哈希值来存储
3.哈希值和对象本身有关,并且和对象的hashCode方法返回的值有关,并且是随机的,但是每次运行又是一样的
4. if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
   哈希表去除重复元素,依赖的是哈希值和equals方法
   逻辑: 如果哈希值【对象本身的hashCode】不同,肯定可以保证元素不同
   	  如果哈希值相同,不能完全保证元素相同,还需要比较equals
   	 可以直接比较两个对象equals方法,但是每次比较两个对象的equals方法效率很低
   	 先比较对象的hashCode,hashCode是int类型,int类型的比较肯定非常快

class HashMap {

	final float loadFactor;

	static final float DEFAULT_LOAD_FACTOR = 0.75f;

	public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    			 "ab"	0x0001
    public V put(K key, V value) {
    					6		"ab"	0x0001
        return putVal(hash(key), key, value, false, true);
    }
    						"ab"
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                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);
                        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;
    }

}

6.comparable & comparator

6.1comparable & comparator 的区别

comparablecomparator
类型接口接口
方法只有compareTo一个方法比较器Comparator是其中一个
参数列表一个,compareTo(T o)两个,compare(T o1, T o2)
重写常用类数据类型都实现重写作为后期扩展,实现类少,需要自行实现

6.2TreeSet底层源码解析

//访问TreeSet的有参构造方法,重写Comparator的compare方法
// TreeSet<Integer> ts = new TreeSet<>();
TreeSet<Integer> ts = new TreeSet<>(new Comparator<Student>() {

			@Override
			public int compare(Student s1, Student s2) {
				int cmp = s1.getAge() - s2.getAge();
				int cmp2 = (cmp == 0) ? s1.getName().compareTo(s2.getName()) : cmp;
				return cmp2;
			}
		});


ts.add(40);
ts.add(43);

//TreeSet部分
class TreeSet{

	private transient NavigableMap<E,Object> m; //4. m = TreeMap,所以访问TreeMap
	private static final Object PRESENT = new Object(); // 0x0001
	
	public TreeSet() {//1.无参构造
        this(new TreeMap<E,Object>());//2.访问带参构造
    }
    
    
    Comparator<? super E> comparator = new Comparator<Student>() {

			@Override
			public int compare(Student s1, Student s2) {
				int cmp = s1.getAge() - s2.getAge();
				int cmp2 = (cmp == 0) ? s1.getName().compareTo(s2.getName()) : cmp;
				return cmp2;
			}
		}
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
    
    // 3.NavigableMap<E,Object> m = new TreeMap<E,Object>();
    TreeSet(NavigableMap<E,Object> m) {//NavigableMap
        this.m = m;
    }
    					40 43
    public boolean add(E e) {//6. 调用add添加40
    				40 43	0x0001
        return m.put(e, PRESENT)==null;//7. m = TreeMap,所以put是TreeMap的方法
    }
}

//看Integer类型是否实现了Comparable
public final class Integer extends Number implements Comparable<Integer> {
	public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
    
    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
}

//TreeMap部分,TreeSet底层就是以TreeMap实现的
public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable {
    
    private transient Entry<K,V> root;
    private final Comparator<? super K> comparator; // null 

	public TreeMap() {
        comparator = null;//5.TreeSet有参构造访问了TreeMap的无参构造,得comparator = null
    }
    
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
    
    final int compare(Object k1, Object k2) {
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            : comparator.compare((K)k1, (K)k2);
    }
    			40	43	0x0001
    public V put(K key, V value) {
        Entry<K,V> t = root;//8. 因为初始是没有值得,所以 t = root == null
        if (t == null) {//9.因为 t = root == null,所以执行这里
        	// 创建根节点
            this.compare(key, key); // type (and possibly null) check
            //用于判断是否实现了Comparable

            root = new Entry<>(key, value, null); // 40
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent; // 父节点
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator; 
             //10. 因为TreeSet访问的是无参构造,所以通过带参访问了TreeMap的无参构造
             //得comparator = null
             //所以 cpr == null
        if (cpr != null) {//13.访问的是TreeSet的带参构造时,就不会访问TreeMap的无参构造
            //所以cpr != null
        	// 比较器排序
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);//14.则使用的是comparator的compare方法
                //一般需要自行实现
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
        	// 自然排序
            if (key == null)//11. 所以执行这里(即只要是访问TreeSet的无参构造,都访问这里)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key; 43
            do {
                parent = t; 40 // 将根节点作为父节点
                cmp = k.compareTo(t.key); //43和40比较
                //12.使用的是comparable的唯一方法compareTo,
                //compareTo绝多数的常用实现类都实现了compareTo
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
	    
}

static final class Entry<K,V> implements Map.Entry<K,V> {
    K key;
    V value;
    Entry<K,V> left;
    Entry<K,V> right;
    Entry<K,V> parent;
    boolean color = BLACK;
}
6.2.1 compare & compareTo是怎么完成排序的
  • 当访问的是TreeSet的无参构造时,调用的是自然排序compareTo方法
  • 当访问的是TreeSet的带参构造时,调用的是比较器排序compare方法
  • 所以排序过程产生于执行add方法时,通过自然排序或者比较器排序在源码中进行了排序
  • 当在对自行创建的类的对象进行排序时,需要在类内部对compareTo进行重写,或者使用匿名内部类对compare进行重写
    • hashSet对equals和hashCode进行重写
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值