Java集合之Collection接口
1 - Java集合介绍
/*
1. 一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象 的操作,就要对对象进行存储。
2. 另一方面,使用Array存储对象方面具有一些弊 端,而Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。
①数组在内存存储方面的特点:
数组初始化以后,长度就确定了。
数组声明的类型,就决定了进行元素初始化时的类型
②数组在存储数据方面的弊端:
数组初始化以后,长度就不可变了,不便于扩展
数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。 同时无法直接获取存储元素的个数
数组存储的数据是有序的、可以重复的。---->存储数据的特点单一
3. Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组
*/
package com.lzh.java1; /* 集合框架概述: 1. 集合、数组都是对多个数据进行存储操作的结构,简称Java容器。说明:此时的存储,主要指的是内存层面的存储,不涉及到硬盘层面的存储。 2. 数组在存储多个数据方面的特点: > 一旦初始化以后,其长度就确定了 > 数组一旦定义好,其元素的类型也确定了,也就只能操作指定类型的数据了,比如:String[] array;int[] arr 3. 数组在存储多个数据方面的缺点: > 一旦初始化以后,其长度就不可修改 > 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便 > 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用 > 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足 */ public class CollectionTest { }
2 - Java集合的使用场景
3 - Java集合Collection 和 Map 两种体系
/*
Java 集合可分为 Collection 和 Map 两种体系:
1 Collection接口:单列数据,定义了存取一组对象的方法的集合
①List:元素有序、可重复的集合 --> "动态"数组
②Set:元素无序、不可重复的集合 --> 数学层面的集合(无序,确定,互异)
2 Map接口:双列数据,保存具有映射关系“key-value对”的集合 --> 数学函数 y = f(x)
*/
Collection接口:单列集合,用来存储一个一个的对象。其中List接口:用来存储,有序可重复的数据,Set接口:用来存储,无序,不可重复的数据
Map接口:双列集合,用来存储一对(key - value)的一对数据
4 - Collection接口方法
/*
Collection接口
1 Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法 既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
2 JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)实现。
3 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都 当成 Object 类型处理;从 JDK 5.0 增加了泛型以后,Java 集合可以记住容器中对象的数据类型
4 结论:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
*/
/* 1、添加 ① add(Object obj) ② addAll(Collection coll) 2、获取有效元素的个数 ① int size() 3、清空集合 ① void clear() 4、是否是空集合 ① boolean isEmpty() 5、是否包含某个元素 ① boolean contains(Object obj):是通过元素的equals方法来判断是否 是同一个对象 ② boolean containsAll(Collection c):也是调用元素的equals方法来比 较的。拿两个集合的元素挨个比较。 6、删除 ① boolean remove(Object obj) :通过元素的equals方法判断是否是 要删除的那个元素。只会删除找到的第一个元素 ② boolean removeAll(Collection coll):取当前集合的差集 7、取两个集合的交集 ① boolean retainAll(Collection c):把交集的结果存在当前集合中,不 影响c 8、集合是否相等 ① boolean equals(Object obj) 9、转成对象数组 ① Object[] toArray() 10、获取集合对象的哈希值 ① hashCode() 11、遍历 ① iterator():返回迭代器对象,用于集合遍历 */
package com.lzh.java1; import org.junit.Test; import java.util.*; /* Collection接口中声明的方法测试 结论: 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals() */ public class CollectionTest { // Collection接口中方法的使用 @Test public void test1(){ Collection coll = new ArrayList(); // add(Object e) 将元素e添加到集合中 coll.add(123); // 自动装箱 coll.add("a"); coll.add("b"); coll.add(new Date()); // size() 获取集合中元素的个数 System.out.println(coll.size()); // addAll(Collection coll1) 将集合coll1中的所有元素添加到当前的集合中 Collection coll1 = new ArrayList(); coll1.add(222); coll1.addAll(coll); System.out.println(coll1.size()); // 5 System.out.println(coll1); // isEmpty() 判断当前集合是否为空 System.out.println(coll.isEmpty()); // false // clear() 清空集合元素 coll.clear(); System.out.println(coll.isEmpty()); // true } @Test public void test2(){ Collection collection1 = new ArrayList(); collection1.add(13); collection1.add(22); collection1.add(new String("alex")); collection1.add(false); collection1.add(new Person("alex",22)); // System.out.println(collection1); // contains(Object obj):是通过元素的equals方法来判断是否 是同一个对象 boolean isContains = collection1.contains(13); System.out.println(isContains); System.out.println(collection1.size()); System.out.println(collection1.contains(new Person("alex",22))); // true // containsAll(Collection coll1) 判断形参coll1中的所有元素是否都存在于当前集合中。 Collection collection2 = Arrays.asList(13,false); System.out.println(collection1.containsAll(collection2)); // true } @Test public void test3(){ // 集合 --> 数组 toArray() 返回值为 Object[] Collection collection = new ArrayList(); for(int i = 0;i < 5;i++){ collection.add(i); } Object[] arrays = collection.toArray(); System.out.println(arrays.getClass()); // class [Ljava.lang.Object; // 遍历数组 for(int i = 0;i < arrays.length;i++){ System.out.println(arrays[i]); } // 数组 --> 集合 调用Arrays类的静态方法asList() Collection collection1 = Arrays.asList(11,"adf",22); System.out.println(collection1.getClass()); // class java.util.Arrays$ArrayList } @Test public void test4(){ // iterator() 返回Iterator接口的实例,用于遍历集合元素。 Collection collection = new ArrayList(); for(int i = 0;i < 5;i++){ collection.add("list"+1); } Iterator iterator = collection.iterator(); // 集合元素的遍历操作,使用迭代器Iterator接口 } } class Person{ private String name; private int age; public Person(){} public Person(String name,int age){ this.name = name; this.age = age; } @Override public boolean equals(Object o) { System.out.println("执行 equals()..."); if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } }
5 - Iterator迭代器接口
/*
Iterator迭代器介绍:
1 Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
2 GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元 素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公 交车上的售票员”、“火车上的乘务员”、“空姐”。
3 Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所 有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了 Iterator接口的对象。
4 Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。
5 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合 的第一个元素之前
*/
Iterator接口的方法
package com.lzh.java1; import org.junit.Test; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /* 集合元素的遍历操作,使用迭代器Iterator接口 1. 内部的方法:hasNext() 和 next() 2. 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前 3. 内部定义了remove(),可以在遍历的时候,删除集合中的元素,此方法不同于集合直接调用remove() */ public class IteratorTest { @Test public void test1(){ // iterator() 返回Iterator接口的实例,用于遍历集合元素。 Collection collection = new ArrayList(); for(int i = 0;i < 5;i++){ collection.add("list"+i); } Iterator iterator = collection.iterator(); // 遍历集合:方式1 不推荐使用 for(int i = 0;i < collection.size();i++){ System.out.println(iterator.next()); // 超过5次就报错(局限性) } // 遍历集合:方式2 推荐使用 // hasNext()与next()搭配使用 while(iterator.hasNext()){ System.out.println(iterator.next()); } } @Test public void test2(){ // iterator中的remove() Collection collection = new ArrayList(); for(int i = 0;i < 5;i++){ collection.add("list"+i); } Iterator iterator = collection.iterator(); while(iterator.hasNext()){ if("list2".equals(iterator.next())){ iterator.remove(); } } Iterator iterator1 = collection.iterator(); // 重新获取迭代器 while(iterator1.hasNext()) { System.out.println(iterator1.next()); } /*注意: 1 Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方 法,不是集合对象的remove方法。 2 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法, 再调用remove都会报IllegalStateException。 */ } }
迭代器的执行原理
6 - 使用 foreach 循环遍历集合元素
/*
foreach循环:
1 Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。
2 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
3 遍历集合的底层调用Iterator完成操作。
4 foreach还可以用来遍历数组。
*/
package com.lzh.java1; import org.junit.Test; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /* 1 Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。 2 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。 3 遍历集合的底层调用Iterator完成操作。 4 foreach还可以用来遍历数组。 */ public class foreachTest { @Test public void test1(){ Collection collection = new ArrayList(); for(int i = 0;i < 5;i++){ collection.add("list"+i); } // for(集合中元素的类型 局部变量 : 集合对象/数组){} // 内部任然调用迭代器 for(Object i:collection){ System.out.println(i); } } // 遍历数组 @Test public void test2(){ int[] intArray = new int[]{1,2,3,4,5,6,7,8,9,10}; for(int i:intArray){ System.out.println(i); } } // 面试题:输出结果? @Test public void test3(){ String[] arr = new String[3]; for(String i:arr){ i = "mm"; System.out.println(i); } for(int i = 0;i < arr.length;i++){ System.out.println(arr[i]); // null } } }
7 - Collection子接口之一:List接口
/*
List接口:
1 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
2 List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
3 List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据 序号存取容器中的元素。
4 JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。
*/
/* List接口:"动态"数组 List接口的3个实现类:ArrayList、LinkedList和Vector 面试题:ArrayList \ LinkedList \ Vector 3者之间的异同? 同:3个类都实现了List接口,存储数据的特点相同(有序,可重复) 异: ArrayList(List接口主要实现类):线程不安全,执行效率高。底层用Object[] elementData数组进行存储 LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向列表存储 Vector(List接口古老实现类):线程安全,执行效率偏低。底层用Object[] elementData数组进行存储 ArrayList源码分析: JDK7 ArrayList list = new ArrayList(); // 底层创建了长度为10的Object[] 数组elementData list.add(123); // elementData[0] = new Integer(123); ... list.add(11); // 如果此次的添加导致底层elementData数组容量不够,则扩容。默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。 结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity); JDK8 中ArrayList的变化 ArrayList list = new ArrayList(); // 底层Object[] elementData初始化为{},并没有创建长度为10的数组 list.add(123); // 第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData[0] ... 后续的扩容问题与JDK7一样 结论:JDK7中的ArrayList的对象的创建类似于单例模式的饿汉式,而JDK8中ArrayList的对象的创建类似于单例模式的懒汉式,延迟了数组的创建,节省内存。 LinkedList的源码分析: LinkedList list = new LinkedList(); // 内部声明了Node类型的first和last属性,默认值为null list.add(123); // 将123封装到Node中,创建Node对象 其中。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; } } */
List实现类之一:ArrayList
1 ArrayList 是 List 接口的典型实现类、主要实现类
2 本质上,ArrayList是对象引用的一个”变长”数组
3 ArrayList的JDK1.8之前与之后的实现区别?
① JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
② JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元 素时再创建一个始容量为10的数组
4 Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是 Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
List实现类之二:LinkedList
1 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
2 新增方法:
1 void addFirst(Object obj)
2 void addLast(Object obj)
3 Object getFirst()
4 Object getLast()
5 Object removeFirst()
6 Object removeLast()
3 LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last, 用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基 本结构。Node除了保存数据,还定义了两个变量:
1 prev变量记录前一个元素的位置
1 next变量记录下一个元素的位置
List 实现类之三:Vector
1 Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList 相同,区别之处在于Vector是线程安全的。
2 在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时, 使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
3 新增方法:
1 void addElement(Object obj)
2 void insertElementAt(Object obj,int index)
3 void setElementAt(Object obj,int index)
4 void removeElement(Object obj)
5 void removeAllElements()
/* List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来 操作集合元素的方法。 1 void add(int index, Object ele) 在index位置插入ele元素 2 boolean addAll(int index, Collection eles) 从index位置开始将eles中 的所有元素添加进来 3 Object get(int index) 获取指定index位置的元素 4 int indexOf(Object obj) 返回obj在集合中首次出现的位置 5 int lastIndexOf(Object obj) 返回obj在当前集合中末次出现的位置 6 Object remove(int index) 移除指定index位置的元素,并返回此元素 7 Object set(int index, Object ele) 设置指定index位置的元素为ele 8 List subList(int fromIndex, int toIndex) 返回从fromIndex到toIndex 位置的子集合 */
package com.lzh.java1; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ListMethodTest { @Test public void test1(){ ArrayList list1 = new ArrayList(); for(int i = 0;i < 5;i++){ list1.add("list"+i); } // 1 void add(int index, Object ele) 在index位置插入ele元素 list1.add(0,"第一个元素"); System.out.println(list1); // [第一个元素, list0, list1, list2, list3, list4] // 2 boolean addAll(int index, Collection eles) 从index位置开始将eles中 的所有元素添加进来 List list2 = Arrays.asList(1,2,3); list1.addAll(list2); System.out.println(list1.size()); // 9 // 3 Object get(int index) 获取指定index位置的元素 System.out.println(list1.get(0)); // 第一个元素 System.out.println(list2.get(0)); // 0 // 4 int indexOf(Object obj) 返回obj在集合中首次出现的位置,如果不存在返回 -1 int index = list1.indexOf(3); System.out.println(index); // 8 } @Test public void test2(){ ArrayList list = new ArrayList(); List l = Arrays.asList("a",1,5,3); list.addAll(l); list.add(3); // System.out.println(list); // 5 int lastIndexOf(Object obj) 返回obj在当前集合中末次出现的位置 int index = list.lastIndexOf(3); System.out.println(index); // 4 // 6 Object remove(int index) 移除指定index位置的元素,并返回此元素 Object remove = list.remove(0); System.out.println(remove); // a System.out.println(list); // [1, 5, 3, 3] // 7 Object set(int index, Object ele) 设置指定index位置的元素为ele Object p = list.set(1, "hello"); System.out.println(p); // 5 System.out.println(list); // [1, hello, 3, 3] // 8 List subList(int fromIndex, int toIndex) 返回从fromIndex到toIndex 位置的子集合 List newList = list.subList(2,4); System.out.println(newList); // [3,3] } }
8 - Collection子接口之二:Set接口
/*
set接口:
1 Set接口是Collection的子接口,set接口没有提供额外的方法(用的都是Collection声明的方法)
2 Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。
3 Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法
4 存储数据特点:无序、不可重复
*/
package com.lzh.java2; import org.junit.Test; import java.util.*; /* 1 Set接口框架: /----Set接口:存储无序、不可重复的数据 -->高中讲的"集合" /----HashSet:作为Set接口的主要实现类,线程不安全的,可以存储null值 /----LinkedHashSet:Linked(链表),作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历 /----TreeSet:可以按照添加对象的指定属性,进行排序,要求:添加的数据是相同类的对象 ①两种排序方式:自然排序(comparable) 和 定制排序(comparator) ②自然排序中,比较两个对象是否相同的标准为:compareTo() 返回0,不再是equals() ③定制排序中,比较两个对象是否相同的标准为:compare() 返回0,不再是equals() 要求: 1 向Set中添加的数据,其所在的类一定要重写 hashCode() 和 equals() 2 重写的 hashCode() 和 equals() 尽可能保持一致性:相等的对象必须具有相等的散列表 2 理解Set接口存储数据:无序、不可重复性。 ① 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值进行存储 ② 不可重复:保证添加的元素按照equals()判断时,不能返回true。即相同的元素只能添加一个 3 添加元素的过程:以HashSet为例 我们向HashSet中添加元素a,首先调用元素a所在类的HashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中存放的位置(索引), 判断数组此位置上是否已经有元素: 如果此位置上没有其他元素,则元素a添加成功。 --> 情况1 如果此位置上没有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值: 如果hash值不相同,则元素a添加成功。 --> 情况2 如果hash值相同,进而需要调用元素a所在类的equals()方法: equals()返回true,元素a添加失败 equals()返回false,则元素a添加成功 --> 情况3 对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据已链表的方式存储 JDK 7:元素a放到数组中,指向原来的元素 JDK 8:原来的元素在数组中,指向元素a 总结:HashSet底层:数组+链表的结构 */ public class SetTest { @Test public void test1(){ Set set = new HashSet(); for(int i = 0;i < 5;i++){ set.add("list"+i); // 有序存储 } set.add(new User("howie",21)); set.add(new User("howie",21)); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); // 无序输出 } } // LinkedHashSet遍历内部数据 // LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据 // 优点:对于频繁遍历操作,LinkedHashSet效率高于HashSet @Test public void test2(){ Set set = new LinkedHashSet(); for(int i = 0;i < 5;i++){ set.add("list"+i); // 有序存储 } Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); // 有序输出 } } } class User{ private String name; private int age; public User(){} public User(String name,int age){ this.name = name; this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return age == user.age && Objects.equals(name, user.name); } @Override public int hashCode() { return Objects.hash(name, age); } }
1-Set实现类之一:HashSet
/*
HashSet介绍:
1 HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
2 HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除 性能。
3 HashSet 具有以下特点:
① 不能保证元素的排列顺序
② HashSet 不是线程安全的
③ 集合元素可以是 null
4 HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相 等,并且两个对象的 equals() 方法返回值也相等。
5 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”
6 向HashSet中添加元素的过程:
①当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法 来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象 在 HashSet 底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)
②如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果 为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了, 那么会通过链表的方式继续链接。
7 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相 等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功
*/
/* 1 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。 2 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。 3 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值 */
/* 以自定义的Customer类为例,何时需要重写equals()? 1 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是 要改写hashCode(),根据一个类的equals方法(改写后),两个截然不 同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法, 它们仅仅是两个对象。 2 因此,违反了“相等的对象必须具有相等的散列码”。 3 结论:复写equals方法的时候一般都需要同时复写hashCode方法。通 常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。 */
2-Set实现类之二:LinkedHashSet
/*
LinkedHashSet介绍:
1 LinkedHashSet 是 HashSet 的子类
2 LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置, 但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入 顺序保存的。
3 LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全 部元素时有很好的性能。
4 LinkedHashSet 不允许集合元素重复
*/
底层结构
3-Set实现类之三:TreeSet
1 TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
2 TreeSet底层使用红黑树结构存储数据
3 新增的方法如下: (了解)
1 Comparator comparator()
2 Object first() Object last()
3 Object lower(Object e)
4 Object higher(Object e)
5 SortedSet subSet(fromElement, toElement)
6 SortedSet headSet(toElement)
7 SortedSet tailSet(fromElement)
4 TreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序