Java 集合源码详解

  • 本篇不适合初学者,及使用方法,适合 面试者 观看借鉴;
  • 本人在尚硅谷看的,整理的笔记。
  • 不正确的地方,多多指教。

Java 集合源码详解

集合和数组:

  • 数组声明了它容纳的元素的类型,而集合不声明存储Object类型 可以通过泛型进行规范!
  • 数组是静态的,一个数组实例具有固定的大小,一旦创建了就无法改变容量了。
    集合是可以动态扩展容量,可以根据需要动态改变大小,集合提供更多的成员方法,能满足更多的需求。
  • 集合: 和数组一样Java中用来存储数据的作用,弥补了数组长度固定的缺点更灵活

List接口

概述:

  • 鉴于Java中数组用来存储数据长度固定,我们通常使用List替代数组 动态数组

  • List集合类中元素有序、且可重复 集合中的每个元素都有其对应的顺序索引。
    List除了从Collection集合继承的方法外,List 集合里添加了一些 根据索引来操作集合元素的方法。
    void add(int index, Object ele):在index位置插入ele元素
    boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
    Object get(int index):获取指定index位置的元素

  • List接口的实现类常用的有:ArrayListLinkedListVector

ArrayList 源码分析

  • ArrayList 是 List 接口的典型实现类、主要实现类用的最多
  • 本质上,ArrayList是对象引用的一个”变长”数组 因为是数组,所以非常适合与进行遍历!

ArrayList的JDK1.8之前与之后的实现区别?

  • JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
  • JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组

建议可以自己深入底层查看效果更佳!

List list = new ArrayList();
按住Ctrl+鼠标右键 进入源码, 注意更改JDK版本!

JDK1.7

  • 对于 ArrayList 而言,它实现 List 接口、底层使用数组保存所有元素。
  • Ctrl+F 快速查找方法…
    ArrayList.Java
    在这里插入图片描述

底层使用数组实现

private transient Object[] elementData;

构造器

ArrayList 提供了三种方式的构造器

  • 可以构造一个默认初始容量为 10 的空列表
  • 构造一个指定初始容量的空列表
  • 构造一个指定初始容量的空列表
  • 对应着三个不同的构造方法…
    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

    public ArrayList() {
        this(10);
    }

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

add

// 将指定的元素添加到此列表的尾部。
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

add(int index, E element)

// 将指定的元素插入此列表中的指定位置
// 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加 1)。
    public void add(int index, E element) {
        rangeCheckForAdd(index);
// 如果数组长度不足,将进行扩容。
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将 elementData 中从 Index 位置开始、长度为 size-index 的元素
        // 拷贝到从下标为 index+1 位置开始的新的 elementData 数组中。
		// 即将当前位于该位置的元素以及所有后续元素右移一个位置。
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

扩容机制:

  • 从上面介绍的向 ArrayList 中存储元素的代码中,我们看到,每当向数组中添加元素时 都要去检查添加后元素的个数是否会超出当前数组的长度
  • 如果超出,数组将会进行扩容,以满足添加数据的需求。
  • 数组扩容通过一个公开的方法 ensureCapacity(int minCapacity) 来实现。
    public void ensureCapacity(int minCapacity) {
    //判断扩展值大于0
        if (minCapacity > 0)
            ensureCapacityInternal(minCapacity);
    }
	//开始扩容
    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;	//原长度(假设2)
        int newCapacity = oldCapacity + (oldCapacity >> 1); //新长度 = 旧 + 旧右移1位!(二级制右移)
        //<<  左移		3<<2 : 3左移两位 结果就是 3*2*2=12;		左移几位就是 *几次2; 注意数值移动太多数值会出问题;(二级制~ 最后会出现负数) 左移在一定范围内 相当于*2        
		//>>  右移		3>>1 : 3右移一位 结果就是 3/2=1;		右移几位就是 /几次2; 注意数值移动太多数值会出问题; 右移在一定范围内 相对于/2
        // x = 2+(2>>1)=3 及一点五倍!
        
        //如果还是小于扩容的长度,直接等于改长度
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;	
            
        //如果新的数组容量newCapacity大于数组能容纳的最大元素个数 MAX_ARRAY_SIZE 
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        
        //把旧数组放进新的扩容后的数组    
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    //那么再判断传入的参数minCapacity是否大于MAX_ARRAY_SIZE
    private static int hugeCapacity(int minCapacity) {
    	//传入的参数必须大于0,否者报错
        if (minCapacity < 0) 
            throw new OutOfMemoryError();
            
         //如果minCapacity大于MAX_ARRAY_SIZE
         //那么//newCapacity等于Integer.MAX_VALUE,否者newCapacity等于MAX_ARRAY_SIZE   
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

总结:

  • 当重新计算的容量(x1.5那个计算)小于传入要求容量参数,则新容量以传入的比较大的容量参数为准。
  • 当传入容量参数太大,大到超过了数组的容量限定值2^{31}-1-8却又小于整数限定值 2^{31}-1
  • 那么新的数组容量以整数限定值 2^{31}-1为准
  • 但是当传入的容量参数不大于数组的容量限定值时,以容量限定值2^{31}-1-8为准。

JDK1.8

  • 1.8 和 1.7 没有太大变化只是由原来的,饿汉更改为了懒汉
//存放元素的数组,从这可以发现 ArrayList 的底层实现就是一个 Object数组
transient Object[] elementData;
//数组中包含的元素个数
private int size;
//数组的最大上限
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造方法

public ArrayList() {
 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

public ArrayList(int initialCapacity) {
 if (initialCapacity > 0) {
 this.elementData = new Object[initialCapacity];
 } else if (initialCapacity == 0) {
 this.elementData = EMPTY_ELEMENTDATA;
 } else {
 throw new IllegalArgumentException("Illegal Capacit
y: "+initialCapacity);
  • elementData 是一个大小为 0 的空数组
  • 当我们指定了初始大小的时候,elementData 的初始大小就变成了我们所指定的初始大小了。

add 添加方法:

public boolean add(E e) {
 ensureCapacityInternal(size + 1); // Increments modCou
nt!!
 elementData[size++] = e;
 return true;
}
  • ArrayList 的 add 方法也很好理解,在插入元素之前,它会先检查是否需要扩容

  • 然后再把元素添加到数组中最后一个元素的后面。

  • 在 ensureCapacityInternal 方法中,
    我们可以看见,如果当 elementData 为空数组时,它会使用默认的大小去扩容。

  • 通过无参构造方法来创建 ArrayList 时,它的大小其实是为 0 的,
    只有在使用到的时候,才会通过 grow 方法去创建一个大小为 10 的数组。

LinkedList 源码分析

  • LinkedList 是通过一个双向链表来实现的,
  • 它允许插入所有元素,包括 null,同时,它是线程不同步的。
    在这里插入图片描述
  • 双向链表每个结点除了数据域之外,还有一个前指针next后指针prev
  • 分别指向前驱结点和后继结点(如果有前驱/后继的话)。
  • 双向链表还有一个 first 指针,指向头节点,和 last 指针,指向尾节点。
    即当前,LinkedList 最后一个节点, 和第一个节点!

LinkedList 中的属性:

//链表的节点个数
transient int size = 0;
//指向头节点的指针
//整个List集合中的第一个元素!
transient Node<E> first;
//指向尾节点的指针
//整个List集合中最后一个元素!
transient Node<E> last;
  • 当然如果集合中就一个元素,它即是first 也是 last

结点结构

  • Node 是在 LinkedList 里定义的一个静态内部类 该类只在LinkedList中使用到!
  • 它表示链表每个节点的结构,包括一个数据域 item,一个后置指针 next,一个前置指针 prev。
    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;
        }
    }
  • item 实际的元素
  • 因为LinkedList是一个双向链表,next prev 分布表示 item 的下一个元素 和 上一个元素!

add

//对外保留的添加方法!
public boolean add(E e) {
    linkLast(e);
    return true;
}

void linkLast(E e) {
	//获取当前最后一个节点
    final Node<E> l = last;			
	//创建e要添加的节点,因为新增是增在最后的,也不需要指定 next下一个元素位置... 
    final Node<E> newNode = new Node<>(l, e, null);
	//并把这个新增的设置为 最后一个节点!
    last = newNode;
    
    //判断最后一个是否为null 
    	//如果为null 就表示这个集合还没有一个元素!没有最后一个元素!
    	//那我就是第一个元素了,并把值赋值给first节点!
    	//else
    	//已经存在元素,把最后一个元素的下一个节点指向e. 因为现在e是才是最后一个!
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    //长度++
    size++;
    modCount++;
}

linkFirst 第一个位置添加元素

private void linkFirst(E e) {
	//获取当前集合中第一个元素
    final Node<E> f = first;
    //创建e 为第一个元素,因为是第一所有没有prev
    final Node<E> newNode = new Node<>(null, e, f);
    //e成为第一个元素
    first = newNode;
    //判断第一个元素是否为null,表示当前集合啥也没有!  e即是第一也是最后!
    //else f不在是第一,并指向e
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

linkLast 最后一个位置添加元素

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}
  • 将e 设置成为 last 并将上一个 last指向e…

ok.现在回头看发现,双向链表果然时候新增!

  • 在任何位置新增,只需要将 前后元素进行链接即可!

Vector 源码分析

Vector 是一个古老的集合,JDK1.0就有了。 已经淘汰很少有人使用了!官方已经停止更新了!

  • 大多数操作与ArrayList相同,区别之处在于Vector是线程安全的。
  • 7/8没变化, 底层默认数组长度10,每次扩容2倍!

ArrayList LinkedList Vector 异同

  • 三者都实现了List 接口 存储数据特点相同: 有序 可重复数据!

ArrayList和LinkedList的异同

  • 二者都线程不安全,相对线程安全的Vector,执行效率高。

  • ArrayList是实现了基于动态数组的数据结构
    LinkedList基于双向链表的数据结构
    对于随机访问get和set,ArrayList觉得优于LinkedList 因为LinkedList要移动指针
    对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势 因为ArrayList要移动数据。

ArrayList和Vector的区别

  • Vector和ArrayList几乎是完全相同的 底层都是动态数据结构
  • 唯一的区别在于Vector是同步类(synchronized) 因此开销就比ArrayList要大,访问要慢。
    大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
  • Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍
  • Vector还有一个子类Stack

Set接口

Set 概述:

Set接口是Collection的子接口 set接口没有提供额外的方法 存储无序, 唯一的数据

  • 无序:
    无序,不表示随机! 只是, 存入数据在 底层数据上的位置 有自己的一套规则

  • 唯一:
    通过, hashCode和 equals 方法实现,存储数据唯一!

  • 实现类:
    HashSet: set接口的主要实现类,线程不安全,可以存储null值;
     LinkedHashSet: 作为HashSet在子类,遍历内部数据时,可以按照添加时的顺序进行展示! 但不代表,它是有序!
    TreeSet
     set 接口的实现类,无序,唯一! 存储的数据都是统一类型的!
     可以对新增的元素 , 内部指定一个排序规则: 自然排序 定制排序 (Java类比较强)

HashSet

实例:

User.Java
自定义对象类型

public class User {
    private int id=0;
    private String name;
	//get/set/无参有参构造...
}

CSDemo
非常正常的一个 HashSet使用

public class CSDemo {
    public static void main(String[] args) {
        //创建新增元素!
        HashSet hset = new HashSet();
        hset.add(123);
        hset.add(123);
        hset.add(456);
        hset.add("ABC");
        hset.add("DEF");
        hset.add(new User(1, "张三"));
        hset.add(new User(1, "张三"));

        //遍历输出
        Iterator it = hset.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }
}

在这里插入图片描述

  • 可以看到, hashset 确实是 无序,唯一
  • 两次 123 都,直接输出不了了 并且输入顺序 和输出的顺序并不一样
  • 但. 两个User对象…确并不是结果唯一 这是为什么呢? set不是值唯一吗? 接下来让我们来深入源码!

HashSet 实现分析:

  • 进行 深入 HashSet() 构造!
    在这里插入图片描述

  • 我们发现,HashSet的构造其实就是 HashMap
    HashSet的值存放于HashMap的key上 HashMap的value统一为PRESENT
    那么, 这里就不深入了解了…后面对HashMap进行深入!

  • 而我们通过上面的代码,发现 两个一模一样的对象, 为什么出现了两次, 不是唯一值吗?

HashSet 存储原理:

  • 首先, 我们已经知道, Hashset 底层就是HashMap
    而, 一般要进行存储唯一的元素, 难免要对元素进行, 比较是否一致, 一致则不添加! 不一致添加HaseSet集合
    Java 中进行比较的方法我们也都知道是 equals() 而, equals其实本质上就是 == 比较地址, 上面两个对象地址不同当然不同,所以是唯一的!

  • 没错, HashSet 也确实是这么干的, 通过比较equals 对象是否true 一致 则不新增!

  • 如果, 我们想对新增的对象,类型的值,进行比较唯一, 对equals重写..即可! 不同地址对象, 值相同添加失败! 实现唯一的效果!


  • 如果,只是重写 equals理论上确实可以实现效果... 实际却并不是这样!   改变上面 User 重写equals 执行!
    在这里插入图片描述
    实际情况,直接使用 工具重写即可!

  • 执行之后, 发现效果并不变, 还是两个 id=1 name=张三

总结:

  • HashSet 本质上就是一个: 数组+链表
    在这里插入图片描述
    初始容量为16,当如果使用率超过0.75 负载因子(16*0.75=12) 就会扩大容量为原来的2倍。32 64...

HashSet 集合判断两个元素相等的标准 唯一 / 无序

  • 直接通过equals 其实就可以,实现 唯一 的特点,但因为这样会极大影响程序性能!
    一个个eq比较,如果集合有 10个 100个 1000个难道每次新增都要比较...? 太垃圾了!😥

通过 数组 + hashcode 和 equals 实现!

  • 首先, 创建出一个 数组长度16的数组…

  • 当 HashSet 新增一个元素,

  • 首先调用 haseCode() 方法, 方法经过重写, 返回一个哈希值 **
    内部通过某种算法...获得具体存放数组的位置! 比如取模 16直接就获得在数组中的位置…当然不会在这么简单的算法..
    判断 ,该位置上是否存在元素 ,如果没有则说明元素 不存在, 新增成功! 这里就表现出, 无序的原因! 新增时候根据, 哈希值 获得在数组上的位置!

  • 存在: 则比较链表上元素的 哈希值 是否一样, 不一样,新增成功! 并以链表的形式, 排列在集合数组中..

  • hase值一致: 则直接比较 equale是否返回 true true一致添加失败! flase不一致添加成功! 继续排列链表!

haseCode() 和 equals()

重写 hashCode() 方法 原则

  • 同一个对象多次调用 hashCode() 方法应该返回相同的值。

  • 当两个对象的 equals() 方法比较返回 true 时 ,
    这两个对象的 hashCode() 方法的返回值也应相等。

  • 对象中用作 equals() 方法比较的属性,都应该用来计算 hashCode 值。

重写 equals() 方法

  • 当一个类有自己特有的“逻辑相等”概念
    改写equals()的时候,
    总是要改写hashCode(),根据一个类的equals方法(改写后)
    两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法,它们仅仅是两个对象
  • 违反了: 相等的对象必须具有相等的散列码
  • 复写equals方法的时候一般都需要同时复写hashCode方法。
    通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

IDEA工具的重写
在这里插入图片描述

User 的重写!

    //比较同一个类型对象, 值是否一致:
        //一致返回 true
        //不一致返回 false
    @Override
    public boolean equals(Object o) {   //返回true 表示对象一致,新增失败!
        //比较对象地址是否一致
        if (this == o) return true;
        //判断当前对象是否为 null 或 类型一致...
        if (o == null || getClass() != o.getClass()) return false;
        //获取对象, 比较id name 属性是否一致....
        User user = (User) o;
        if (id != user.id) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

    //同一个类型, 相同值,调用该方法返回相同的hash值!
    @Override
    public int hashCode() {         //idea hashCode生成!
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

为啥 *31

  • 31 = (2的5次幂 -1)
  • 通常程序,为了方便技术将一个数值进行 , 加倍变大…计算… 当然太大也不好!超过最大值了!
  • 计算机是二进制进行计算的… 2的次幂会提高性能. 所以就找了一个不大不小 又是素数的 31 来提高程序计算效率!

JDK7 和 JDK8

JDK7

  • 数组的实现的 饿汉式 创建时候就指定了长度!
  • 链表插入是头插法…

JDK8

  • 数组的实现的 懒汉式 第一次使用时,才指定长度!

为什么7头插 8尾插

  • 头插法是操作速度最快的,找到数组位置就直接找到插入位置了
    但 , 因为hashmap是不安全的, 多线程情况下,
    AB 执行添加, 在同一个数组位置, B先头插了… A本来要插在C 前面的… 造成死循环…

  • jdk8开始hashmap链表在节点长度达到8之后会变成红黑树

  • 这样一来在数组后节点长度不断增加时,遍历一次的次数就会少很多很多(否则每次要遍历所有)

  • 尾插法比头插法而言,尾插法操作额外的遍历消耗已经小很多了,也可以避免之前的循环列表问题。 尾插法同样是线程不安全的。

LinkedHashSet

总结:

  • LinkedHashSet 是 HashSet 的子类

  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,
    但它同时使用双向链表维护元素的次序,这使得元素看起来以插入顺序保存的。
    在这里插入图片描述
    每新增一个元素时候, 会像 LinkedList 一样 每个元素指定了下一个元素, 和上一个元素的地址!

  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。

  • LinkedHashSet 不允许集合元素重复。

TreeSet

  • TreeSet 是 SortedSet 接口的实现类

  • TreeSet 可以确保集合元素处于排序状态。 TreeSet 可以确保集合元素处于排序状态。

  • Integer和String对象都可以进行默认的TreeSet排序
     
    而自定义类的对象是不可以的,
    自己定义的类必须实现Comparable接口,并且覆写相应的compareTo()函数,才可以正常使用。

  • 而且, TreeSet存储的是一组,相同类型的数据…

  • 不像之前, 123 "ABC" new User() 它一次只能存储一组类型… 因为, TreeSet会对存储的值进行排序…类型不同如何排序~
    TreeSet 两种排序方法:自然排序定制排序。默认情况下,TreeSet 采用自然排序。
    在这里插入图片描述

  • 默认 Integer 和 String类型可以存储 它俩默认实现了自然排序! 存储一组相同类型数据!
    在这里插入图片描述

Java比较器

  • Java中的对象, 正常情况下, 只能进行比较,==(同地址) 或 !=(地址不同) 不能使用 > < 的方式比较值..
  • 但是在开发场景中, 我门需要对多个对象进行, 排序, 言外之意就是比较对象的大小;
  • Java通过两个接口实现: Comparable( 中: 比较 读: 看牌啊爆 ) 或 Comparator( 中: 比较器 读: 看牌啊特 );
  • 分为两种:
    自然排序: java.util.Comparable
    定制排序: java.util.Comparator

自然排序:

  • 类实现,Comparable接口 重写 compareTo( obj );
  • 可以对类对象进行,某种方式的排序; 称为:自然排序
  • 实现接口的类,对象数组/集合。也可以通过, Collections.sort(); 或 Arrays.sort 其实内部就是Collection.sort(); 给数组/集合进行排序;

在这里插入图片描述

  • 这是我之前的代码截图…
  • 总结:
    要进行排序的对象类, 继承Comparable接口, 重写 compareTo(); 方法;
    返回 1 当然对象  大于>  比较对象
    返回 -1 当然对象  小于<  比较对象
    返回 0 无法比较, 一般就像相等… 但是在, TreeSet中如果比较值相等,表示 对象相等 Set 无序唯一原则: 相等的对象,不存在, 添加失败!

定制排序:

因为Java是继承的…

  • 当元素没有实现 Comparable接口, 而不方便修改代码;
  • 或者实现了 Comparable 接口, 但其排序的操作,并不符合当前操作 String 默认从大小排,而我想要从小到大排序……
    可以考虑 Comparator 类型对象来排序
    在这里插入图片描述

实现:

User.Java

public class User {
    private int id=0;
    private String name;

    //静态内部类..
    public static class UserCompare implements Comparator {
        public int compare(Object ob1, Object ob2){
            User u1 = (User)ob1;    //当前对象
            User u2 = (User)ob2;    //比较对象
            //当前id 与 比较id 对比: 大于1 小于-1 等于0
            int result = u1.id > u2.id ? 1 :(u1.id == u2.id ? 0 : -1);
            //如果id一致在比较name... String默认存在compareTo排序...
            if(result == 0){
                result = u1.name.compareTo(u2.name);
            }
            return result;
        }
    }
    //省略其它...
}

main 运行:

public class CSDemo {
    public static void main(String[] args) {
        //创建新增元素!
        TreeSet hset = new TreeSet(new User.comp());
        hset.add(new User(2, "张三"));
        hset.add(new User(3, "张三"));
        hset.add(new User(1, "张三"));
        hset.add(new User(5, "张三"));
        hset.add(new User(5, "张三"));

        //遍历输出
        Iterator it = hset.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }
}

在这里插入图片描述
两接口的重写的方法, 用于比较两个对象的大小
内部操作细节可自定义, 返回值 int , Java的 Arrays类会调用方法使用, 根据返回值给 数组元素重新排位置, 1 往后排 -1小往前 )

总结:

  • TreeSet 和常规,的Set不同, 在新增元素的时候, 会根据元素的 自然排序 给元素排好序!
  • 因此, 对于自定义的类型要处理号排序… 自然排序 定制排序 而且, 存储的都是一组相同类型的数据!

底层在存储数据 也有一定变化!

  • 新增一个元素时: ,
  • 会调用对象类实现的 Comparable 接口的 compareTo() 方法和集合中的对象比较,根据方法返回的结果有序存储
  • 如果比较结果为 0 则该元素 添加失败!
  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
java源码包实例源码JAVA开发源码50个合集: Ajax框架 ZK.rar Java图书馆管理系统源程序.rar Java图片倒影效果实例源码.rar Java图片翻折,将图像压扁.rar Java坦克大战网络对战版源代码.rar Java声音播放程序源代码.rar JAVA实现CLDC与MIDP底层编程的代码.rar Java实现HTTP连接与浏览,Java源码下载.rar Java实现的FTP连接与数据浏览程序.rar Java实现的放大镜效果附有源文件.rar Java实现的点对点短消息发送协议(smpp)开发包源码.rar Java实现的视频播放程序源码.rar Java实现移动的遮照效果.rar JAVA实现超级玛丽.zip Java实现跟踪鼠标运行坐标的源码.rar Java手机与计算机互发彩信源码.rar Java手机游戏大富翁源代码+注释.rar Java手机短信项目源码.rar Java扫雷源码.rar Java生成自定义控件源代码.rar Java调色板面板源代码.rar Java跳棋(基于SWT).rar Java通讯录手机版源码.rar Java鼠标拖拽功能.rar 乐趣大型购物系统.rar 可实现网上对战和人机对战.rar 基于BS结构的Java可视化工作流定制软件.rar 基于J2ME的Java游戏梦幻炸弹人源程序.rar 基于JAVA的ICQ系统.rar 基于Java的mp3播放器源代码.rar 基于Java的小型人事管理系统,带数据库.rar 基于JAVA的日程提醒簿.rar 基于Java的邮件服务器源程序.rar 基于MVC的Java资源管理器 v2.0.rar 基于smpp协议的Java点对点短信发送源码包.rar 季风进销存管理系统(JSP版).rar 客户管理系统 Alfresco Content Management.rar 家庭多媒体播放器.rar 局域网广播系统java源码.rar 开源Winzip压缩工具Java源码.rar 很不错的Java计算器.rar 很强的Java加密解密算法源码.rar 泡泡堂战车游戏JAVA源码.rar 简单模拟的J2ME潜艇大战源代码.rar 简单的注册与登录功能.rar 类似QQ的聊天软件JAVA源码(附设计文档).rar 进程通信.rar 连接postsql数据库的java代码.rar 附加数据库.rar 雷电游戏JAVA版源程序.rar

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java.慈祥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值