Java SE高级--集合
泛型
泛型概述
集合中存放的对象是任意的,当我们把元素加入集合之后,就会转换为Object类型.每当我们想要对集合中的元素取出来并进行相应的操作的时候就需要进行类型转换.
在JDK5之后,增加了泛型的概念.泛型就是可以在类或方法中预支的使用未知的类型.
通常在创建具体对象的时候传入具体的数据类型,如果不传入,则默认为Object
使用泛型的好处
1.将运行时期的ClassCastException,转移到了编译期的编译失败
2.避免了类型强转的麻烦.
泛型的定义与使用
泛型,可以灵活的把数据类型应用到不同的类,方法,接口中.
泛型类
格式
public class 类名<T>{
}
在定义泛型类的时候,泛型是不明确的,只有当我们具体使用的时候才会明确的传入泛型.比如
ArrayList<String> list = new ArrayList<>();
此时集合list中只能存放String类型的数据,如果存放其他类型就会报错.
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("13");
/*list.add(1);
list.add(false);*/类型不符合集合中所要存储的类型要求
list.add("45");
}
}
泛型方法
格式
//修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
public class Ex2_6 {
public static <T> void print(T []arr){
System.out.print("[");
for (int i = 0; i <arr.length ; i++) {
if(i != arr.length-1){
System.out.print(arr[i]+",");
}
if(i == arr.length-1){
System.out.print(arr[i]+"]");
}
}
}
public static void main(String[] args) {
String []arr = {"1","2","3","4"};
print(arr);
}
}
泛型方法中泛型具体化是在我们调用泛型方法的时候才确定的
泛型接口
修饰符 interface接口名<代表泛型的变量> { }
public interface MyGenericInterface<E>{
public abstract void add(E e);
public abstract E getE();
}
public class MyImp1 implements MyGenericInterface<String> {
@Override
public void add(String e) {
// 省略...
}
@Override
public String getE() {
return null;
}
}
泛型接口的泛型的确定是在当我们去实现一个接口的时候实现的.
或者当我们用一个类实现了泛型接口之后,在创建该类的具体对象的时候,确定泛型.
集合
集合概述
集合又可以叫做容器,见名知意是用来存放数据的,在集合中只能够存放引用数据类型,如果说想要存放基本数据类型,可以考虑存储其对应的包装器类型,基于自动装箱和拆箱,存储包装器类型也可以进行基本的数据运算操作.与数组不同的是,数组一但定义,其长度不可以再更改,而且只能存放固定类型的数据,对于集合,如果没有指定泛型,集合可以存放任何数据类型,而且长度可变.
@Test
public void main(){
Collection collections = new ArrayList();
collections.add(123);
collections.add("aa");
collections.add(null);
collections.add(true);
collections.add(new A());
}
集合的继承体系
集合的迭代方式
1.foreach
2.迭代器
3.Lambda
4.通过下标get()
对于大对数集合来说,都有前三种迭代方式,对于ArrayList和Vector来说,还可以通过下标还迭代获取元素,这与ArrayLis和Vectort的实现原理有关.
可以看到ArrayList的底层是数组实现的,所以拥有一些数组的特点,就是通过下标进行元素的获取.
四种迭代方式的示例代码
@Test
public void main(){
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
System.out.println("--------foreach---------");
for (String s : list) {
System.out.println(s);
}
System.out.println("--------下标---------");
for (int i = 0; i < list.size(); i++) {
list.get(i);
}
System.out.println("---------iterator-------");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("---------Lambda--------");
list.forEach(c->{
System.out.println(c);
});
}
iterator原理
iterator迭代器主要是通过指针实现的,可以理解为,一开始hasNext()指向集合的头,判断后面是否包含元素,若有返回true,执行next()将元素取出来,然后has移动到第一个元素的尾部,继续进行判断和去元素,若返回false说明没有元素.
List
List接口介绍
从上面的Collection的继承结构图我们可以知道,List是Collection接口的子类,在List集合中允许出现重复的元素,元素的存取顺序是一样的.
List集合特点
1.有索引
2.可以存储重复元素
3.元素存取有序
List接口中常用的方法
List继承了Collection,所以Collection中的方法,List都有,除此之外还添加了一些特别的方法
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素
package com.feng.collections;
import com.feng.right.test1.A;
import org.junit.Test;
import java.lang.reflect.Array;
import java.util.*;
/**
* @Description TODO
* @Author Cautious-Xin
* @Date 2020/11/14 8:03
*/
public class ArrayListDemo {
@Test
public void test1(){
//ArrayList()
//构造一个初始容量为十的空列表。
ArrayList<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");
list.add("000");
//ArrayList(Collection<? extends E> c)
//构造一个包含指定集合的元素的列表,它们在集合的迭代器返回的顺序中返回。
ArrayList<Object> list1 = new ArrayList<>(list);
System.out.println(list1);
/**
* boolean add(E e)
* 向list中添加元素
* 此方法返回布尔值
*/
boolean add = list.add("110");
System.out.println(add);
System.out.println(list);
/**
* void add(int index, E element)
* 在列表中指定的位置上插入指定的元素。,原先位置上的元素不会被覆盖,会连着后面的元素向后
* 平移一位
*/
list.add(2,"as");
System.out.println(list);
/**
* boolean addAll(Collection<? extends E> c)
* 追加指定集合的所有元素到这个列表的末尾,按他们的指定集合的迭代器返回。
*参数传入一个集合,传入的集合的泛型需要是调用者泛型的子类或者同类
* 直接把传入的集合的数据追加到调用者集合的后方
*/
list1.addAll(list);
System.out.println(list1);
/**
* void clear()
* 将列表中所有元素移除(清空)
*/
System.out.println("-------clear-------");
//list1.clear();
System.out.println(list1);
/**
* boolean contains(Object o)
* 判断调用者集合中是否包含该元素,返回布尔值
*/
System.out.println("-------contains-------");
System.out.println(list.contains("1"));
System.out.println(list.contains("123"));
/**
* E get(int index)
* 根据索引获得元素
*/
System.out.println("-----get()----");
System.out.println(list.get(3));
/**
* int indexOf(Object o)
* 返回指定元素在集合中第一次出现的索引,没有返回-1
*/
System.out.println(list.indexOf("000000000"));
System.out.println(list.indexOf("789"));
/**
* boolean isEmpty()
* 判断集合中是否有元素,返回true表示集合中没有元素
*/
System.out.println(list.isEmpty());
System.out.println(list1.isEmpty());
/**
* int lastIndexOf(Object o)
* 返回此列表中指定元素的最后一个发生的索引,
* 或-如果此列表不包含元素,或- 1。
*/
System.out.println(list.lastIndexOf("789"));
/**
* E remove(int index)
* 移除此列表中指定位置的元素。
* 并返回移除的元素
*/
System.out.println(list);
String remove = list.remove(3);
System.out.println(remove);
System.out.println(list);
/**
* boolean removeAll(Collection<?> c)
* 把调用者里含有的参数传入的集合的元素全部移除
* 移除成功返回true
*/
System.out.println(list);
System.out.println(list1);
// System.out.println(list1.removeAll(list));
System.out.println(list1);
list1.retainAll(list);
System.out.println(list1);
}
@Test
public void demo2(){
/**
* boolean retainAll(Collection<?> c)
* 仅保留包含在指定集合中的列表中的元素。
* 只保留传入集合的元素,原先的元素全部移除
*/
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
ArrayList<String> list1 = new ArrayList<>();
list1.add("1");
list1.add("2");
list1.add("3");
list1.add("4");
list.retainAll(list1);
System.out.println(list);
}
@Test
public void main(){
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
System.out.println("--------foreach---------");
for (String s : list) {
System.out.println(s);
}
System.out.println("--------下标---------");
for (int i = 0; i < list.size(); i++) {
list.get(i);
}
System.out.println("---------iterator-------");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("---------Lambda--------");
list.forEach(c->{
System.out.println(c);
});
}
@Test
public void test(){
List<String> list = new ArrayList<>();
/**
* public void add(int index, E element)`:
* 将指定的元素,添加到该集合中的指定位置上。
*/
list.add("abc");
list.add("def");
list.add("ghi");
System.out.println(list);
/**
* `public E get(int index)`:
* 返回集合中指定位置的元素。
*/
String s = list.get(2);
System.out.println(s);
/**
* public E remove(int index)`:
* 移除列表中指定位置的元素, 返回的是被移除的元素。
*/
list.remove("abc");
System.out.println(list);
/**
* public E set(int index, E element)`:
* 用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
*/
list.set(1,"feng");
System.out.println(list);
}
}
ArrayList
java.util.ArrayList
集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList
是最常用的集合。
ArrayList常用API接口
//ArrayList()
//构造一个初始容量为十的空列表。
ArrayList<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");
list.add("000");
//ArrayList(Collection<? extends E> c)
//构造一个包含指定集合的元素的列表,它们在集合的迭代器返回的顺序中返回。
ArrayList<Object> list1 = new ArrayList<>(list);
System.out.println(list1);
/**
* boolean add(E e)
* 向list中添加元素
* 此方法返回布尔值
*/
boolean add = list.add("110");
System.out.println(add);
System.out.println(list);
/**
* void add(int index, E element)
* 在列表中指定的位置上插入指定的元素。,原先位置上的元素不会被覆盖,会连着后面的元素向后
* 平移一位
*/
list.add(2,"as");
System.out.println(list);
/**
* boolean addAll(Collection<? extends E> c)
* 追加指定集合的所有元素到这个列表的末尾,按他们的指定集合的迭代器返回。
*参数传入一个集合,传入的集合的泛型需要是调用者泛型的子类或者同类
* 直接把传入的集合的数据追加到调用者集合的后方
*/
list1.addAll(list);
System.out.println(list1);
/**
* void clear()
* 将列表中所有元素移除(清空)
*/
System.out.println("-------clear-------");
//list1.clear();
System.out.println(list1);
/**
* boolean contains(Object o)
* 判断调用者集合中是否包含该元素,返回布尔值
*/
System.out.println("-------contains-------");
System.out.println(list.contains("1"));
System.out.println(list.contains("123"));
/**
* E get(int index)
* 根据索引获得元素
*/
System.out.println("-----get()----");
System.out.println(list.get(3));
/**
* int indexOf(Object o)
* 返回指定元素在集合中第一次出现的索引,没有返回-1
*/
System.out.println(list.indexOf("000000000"));
System.out.println(list.indexOf("789"));
/**
* boolean isEmpty()
* 判断集合中是否有元素,返回true表示集合中没有元素
*/
System.out.println(list.isEmpty());
System.out.println(list1.isEmpty());
/**
* int lastIndexOf(Object o)
* 返回此列表中指定元素的最后一个发生的索引,
* 或-如果此列表不包含元素,或- 1。
*/
System.out.println(list.lastIndexOf("789"));
/**
* E remove(int index)
* 移除此列表中指定位置的元素。
* 并返回移除的元素
*/
System.out.println(list);
String remove = list.remove(3);
System.out.println(remove);
System.out.println(list);
/**
* boolean removeAll(Collection<?> c)
* 把调用者里含有的参数传入的集合的元素全部移除
* 移除成功返回true
*/
System.out.println(list);
System.out.println(list1);
// System.out.println(list1.removeAll(list));
System.out.println(list1);
list1.retainAll(list);
System.out.println(list1);
LinkedList
java.util.LinkedList
集合数据存储的结构是链表结构。方便元素添加、删除的集合。LinkedList的实现是双向链表.
LinkedList常用API
public void addFirst(E e)
:将指定元素插入此列表的开头。public void addLast(E e)
:将指定元素添加到此列表的结尾。public E getFirst()
:返回此列表的第一个元素。public E getLast()
:返回此列表的最后一个元素。public E removeFirst()
:移除并返回此列表的第一个元素。public E removeLast()
:移除并返回此列表的最后一个元素。public E pop()
:从此列表所表示的堆栈处弹出一个元素。public void push(E e)
:将元素推入此列表所表示的堆栈。public boolean isEmpty()
:如果列表不包含元素,则返回true。
package com.feng.collections;
import org.junit.Test;
import java.util.Iterator;
import java.util.LinkedList;
/**
* @Description TODO
* @Author Cautious-Xin
* @Date 2020/11/14 8:39
*/
public class LinkedListDemo {
@Test
public void demo(){
/**
* LinkedList()
* 构造一个空列表。
* LinkedList(Collection<? extends E> c)
* 构造一个包含指定集合的元素的列表,它们在集合的迭代器返回的顺序中返回。
*/
LinkedList<String> list = new LinkedList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
/**
* void addFirst(E e)
* 把元素插入到集合的第一个位置
*/
list.addFirst("0");
System.out.println(list);
/**
* void addLast(E e)
* 把元素插入到集合的最后一位
*/
list.addLast("z");
System.out.println(list);
/**
* Iterator<E> descendingIterator()
* 返回一个迭代器,从集合的尾部向头部迭代
*/
Iterator<String> stringIterator = list.descendingIterator();
while (stringIterator.hasNext()){
System.out.println(stringIterator.next());
}
/**
* E element()
* 返回集合的第一个元素,但是不把元素从集合中移除
*/
String element = list.element();
System.out.println(element);
System.out.println(list);
/**
* E getFirst()
* 返回此列表中的第一个元素。但是不删除
*/
System.out.println("------getFirst()-------");
String first = list.getFirst();
System.out.println(first);
System.out.println(list);
/**
* boolean offer(E e)
* 将指定的元素添加到列表的尾部(最后一个元素)。
*/
boolean ads = list.offer("ads");
System.out.println(list);
/**
* boolean offerFirst(E e)
* 在列表的前面插入指定的元素。
*/
list.offerFirst("qwe");
System.out.println(list);
/**
* E peek()
* 检索,但不删除,此列表的头(第一个元素)。
* E peekFirst()
* 检索,但不删除该列表的第一个元素,或返回 null如果这个列表是空的。
* E peekLast()
* 检索,但不删除该列表的最后一个元素,或返回 null如果这个列表是空的。
*/
System.out.println(list.peek());
System.out.println(list.peekFirst());
System.out.println(list.peekLast());
/**
* E poll()
* 检索并删除此列表的头(第一个元素)。
* E pollFirst()
* 检索并移除此列表的第一个元素,或返回 null如果这个列表是空的。
* E pollLast()
* 检索并移除此列表的最后一个元素,或返回 null如果这个列表是空的。
*/
System.out.println(list.poll());
System.out.println(list);
System.out.println(list.pollLast());
System.out.println(list);
/**
* E pop()
* 从这个列表所表示的堆栈中弹出一个元素。(将集合的 头部元素删除)
* void push(E e)
* 将一个元素推到由该列表所表示的堆栈上。
*/
list.pop();
System.out.println(list);
list.push("fengshihang");
System.out.println(list);
}
}
List总结
List集合(接口)
1.长度可变
2.允许存放重复的元素
2.存取顺序保持一致
ArrayList(类)
1.基于数组实现,在内存中存储是连续的空间
2.可以通过下标进行操作
3.查询快,增删慢
4.线程不安全
LinkedList(类)
1.实现原理是双向链表
2.在内存中存储不连续
3.对头尾操作更便捷
4.线程不安全
5.增删快,查询慢
在实际开放中,若查询操作比较多,优先考虑ArrayList,若增删操作比较多,优先考虑LinkedList
Set
Set接口也继承于Collection接口,它与Collection中 的方法是一致的,但是新加入了一些新的方法和新的特性,比如元素的存储顺序不一致,元素不可以重复等.
HashSet
HashSet是Set接口的实现类,拥有Set接口的特性(不可以存放重复元素,存储顺序不一致)
Hash值所以可以实现存取顺序不一致,主要原因是因为底层实现原理是依靠hash值进行计算存储位置,所以当我们存放自定义类型数据的时候可以考虑重写hashCode
与equals
方法。防止存储了重复数据的元素.
//存放API中的应用数据类型
@Test
public void test(){
HashSet<String> set= new HashSet<>();
set.add("a");
set.add("a");
set.add("a");
set.add("ab");
set.add("dc");
set.add("f");
System.out.println(set);
}
//[a, ab, f, dc]
//可以看见存取顺序不一致
//存放自定义数据类型,没有重写hashcode()和equals()方法
@Test
public void test2(){
HashSet<Student> set= new HashSet<>();
set.add(new Student("zhangsan",12));
set.add(new Student("zhangsan",12));
set.add(new Student("lisi",12));
set.add(new Student("zh",12));
System.out.println(set);//[Student{name = zh, age = 12}, Student{name = zhangsan, age = 12}, Student{name = lisi, age = 12}, Student{name = zhangsan, age = 12}]
}
//存放自定义数据类型,重写hashcode()和equals()方法
@Test
public void test3(){
HashSet<Student> set= new HashSet<>();
set.add(new Student("zhangsan",12));
set.add(new Student("zhangsan",12));
set.add(new Student("lisi",12));
set.add(new Student("zh",12));
System.out.println(set);//[Student{name = zhangsan, age = 12}, Student{name = lisi, age = 12}, Student{name = zh, age = 12}]
}
class Student{
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) &&
Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
//在重写了hashcode()方法和equals()方法之后,凡是成员属性的值相同的都被认为是相同元素,所以不会存放.
关于HashSet的一些思考
1.HashSet 怎么出现无序的情况的?
底层计算hash值时,通过(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);计算
计算对应添加元素的的“桶”数组的下标,通过(n - 1) & hash计算。
2.HashSet 如何保证元素的唯一性?
根据hashcode()和equals()保证元素的唯一性。
当hashcode一致时,equals不一定一致,需要查看equals是否一致,
若equals一致,则认定为相同元素;
若equals不一致,则认定为不同元素;
当equals一致时,hashcode一定一致,直接认定是相同元素。
3.JDK8版本前后,底层存储结构有什么不同?为什么做出改变?
1).底层是由数组 - 链表组成,当添加新元素时,它会根据元素的hash值找到对应的"桶",也就是HashMap源码中Node<K, V> 里的元素,
并插入到对应位置的链表中,链表元素个数过长时会转化为红黑树(JDK1.8后的版本)。
2).链表取元素是从头结点一直遍历到对应的结点,这个过程的复杂度是O(N) ,而红黑树基于二叉树的结构,查找元素的复杂度为O(logN) , 所以,当元素个数过多时,用红黑树存储可以提高搜索的效率。
4.既然红黑树的效率高,那怎么不一开始就用红黑树存储呢?
红黑树虽然查询效率比链表高,但是结点占用的空间大,只有达到一定的数目才有树化的意义,这是基于时间和空间的平衡考虑。
此处翻译源代码注释:单个 TreeNode 需要占用的空间大约是普通Node的两倍,所以只有当包含足够多的Nodes时才会转成 TreeNodes,
这个足够多的标准就是由 TREEIFY_THRESHOLD 的值(默认值8)决定的。而当桶中节点数由于移除或者 resize (扩容) 变少后,
红黑树会转变为普通的链表,这个阈值是 UNTREEIFY_THRESHOLD(默认值6)。
5.为什么树化标准是8个?
此处翻译源代码注释:如果hashCode的分布离散良好的话,那么红黑树是很少会被用到的,因为各个值都均匀分布,很少出现链表很长的情况。
在理想情况下,链表长度符合“泊松分布”,各个长度的命中概率依次递减,注释中给我们展示了1-8长度的具体命中概率,当长度为8的时候,
概率概率仅为0.00000006,这么小的概率,HashMap的红黑树转换几乎不会发生,因为我们日常使用不会存储那么多的数据。
若用户使用HashMap过程导致hashCode分布离散很差的场景(思考一下是什么场景),这个时候再转换为红黑树就是一种很好的退让策略。
可以避免查询效率低下的问题,从而有效防止用户自己实现了不好的哈希算法时导致链表过长的情况。
6.底层计算hash值时,为什么要(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);这样运算?
传入key之后,hash()会获取key的hashCode进行无符号右移 16 位,然后进行按位异或,并把运算后的值返回,这个值就是key的哈希值。这样运算是为了减少碰撞冲突,因为大部分元素的hashCode在低位是相同的,不做处理的话很容易造成冲突。
7.如何计算对应添加元素的的“桶”数组的下标?
tab[i = (n - 1) & hash]) 当查找不到对应的索引时,就会新建一个新的结点作为链表的头结点。
通过位运算,在每次扩容时都不用重新计算hash,省去了不少时间,而且新增有效位是0还是1是带有随机性的,
之前两个碰撞的Entry又有可能在扩容时再次均匀地散布开,达到较好的分布离散效果。
8.为什么退化为链表的阈值是6?
当链表长度达到阈值8的时候会转为红黑树,但是红黑树退化为链表的阈值却是6。主要是一个过渡,避免链表和红黑树之间频繁的转换。
如果阈值是7的话,删除一个元素红黑树就必须退化为链表,增加一个元素就必须树化,来回不断的转换结构无疑会降低性能,
所以阈值才不设置的那么临界。
这部分内容有很多转载老师的笔记
TreeSet
TreeSet集合是Set接口的实现类,与HashSet不同的是,存储到此集合中的数据会按照自然顺序排序,对于自定义类的数据,需要实现Comparable接口,不然程序运行会抛**java.lang.ClassCastException:**的异常
LinkedHashSet
@Test
public void test(){
//测试存储API中的数据
TreeSet<String> set = new TreeSet<>();
set.add("df");
set.add("adf");
set.add("cdf");
set.add("zdf");
set.add("edf");
System.out.println(set);//[adf, cdf, df, edf, zdf]
//String源码中可以知道,该类已经实现了Comparable接口,所以没有异常,而且也对元素进行了排序
}
@Test
public void demo(){
//测试自定义数据(未实现Comparable接口)
TreeSet<Person> set = new TreeSet<>();
set.add(new Person("zhangsan",12));
set.add(new Person("san",12));
set.add(new Person("lisi",12));
System.out.println(set);
}
class Person implements Comparable<Person>{
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Person{name = " + name + ", age = " + age + "}";
}
@Override
public int compareTo(Person o) {
return this.age.compareTo(o.age);
}
}
@Test
public void demo(){
//测试自定义数据(实现Comparable接口)
TreeSet<Person> set = new TreeSet<>();
set.add(new Person("zhangsan",12));
set.add(new Person("san",13));
set.add(new Person("lisi",11));
System.out.println(set);//[Person{name = lisi, age = 11}, Person{name = zhangsan, age = 12}, Person{name = san, age = 13}]
}
Set接口总结
HashSet
1.存储的元素不能重复,依靠hashcode()和equals()
2.存取顺序不一致
3.根据hash值确定元素的存储位置
4.线程不安全
5.具有良好的存储和查找性能
TreeSet
1.存入的元素会按照自然顺序排序(Comparable接口)
2.存储的元素是唯一的(依赖compareTo方法)
3.底层实现依赖TreeMap,是基于红黑树实现的.
4.线程不安全
Collections
Collections是对集合提供操作的工具类,就像Arrays一样是对数组进行操作的,里面封装了一些静态的比较常用的方法.
-
java.utils.Collections
是集合工具类,用来对集合进行操作。常用方法如下:
-
public static void shuffle(List<?> list)
:打乱集合顺序。 -
public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序。 -
public static <T> void sort(List<T> list,Comparator<? super T> )
:将集合中元素按照指定规则排序。