1. 课前回顾:
  2. 1.Lock锁:接口-> 实现类 ->ReentractLock
  3. 2.方法: lock()获取锁 unlock()释放锁
  4. 3.实现原理:乐观锁,比较并交换->CAS
  5. 4.Condition:线程阻塞队列->和Lock锁结合使用
  6. 获取: Lock类中的newCondition()
  7. 方法: await()->线程等待
  8. signal()->线程唤醒
  9. 5.线程池:Executors
  10. 获取: ExecutorService newFixedThreadPool(int n)->获取线程池对象,并且指明池子中最多能都n个线程
  11. submit(Runnable r)
  12. submit(Callable c)
  13. call方法和run方法的区别:
  14. 相同点:都是设置线程任务
  15. 不同点:
  16. call()有返回值,可以直接throws异常
  17. run()没有返回值,不能直接throws异常
  18. Future接口:用于接收run方法或者call方法的返回值,但是run方法没有返回值,所以,不用Future接收
  19. get()->将call方法的返回值获取到
  20. 6.定时器:Timer->可以让线程任务每隔多长时间执行一次
  21. 构造:Timer()
  22. 方法:void schedule(TimerTask task, Date firstTime, long period)
  23. task:设置线程任务
  24. firsttime:从哪个时间开始计算
  25. period:间隔多长时间
  26. 7.集合
  27. a.概述:容器
  28. b.作用:存储元素
  29. c.特点:
  30. 长度可变
  31. 只能存储引用数据类型
  32. 8.Collection:单列集合的顶级接口
  33. 方法:add(E e)->添加元素
  34. addAll(Collection c)->合并集合
  35. clear()清空集合元素
  36. contains(E e)集合中是否包含指定的元素
  37. remove(E e):删除指定元素
  38. size():获取几个元素个数
  39. isEmpty() : 判断集合中是否有元素
  40. toArray() :将集合转成数组
  41. 9.迭代器:Iterator接口
  42. a.获取:Collection下的方法 Iterator<E> iterator()
  43. b.方法:hashNext()->判断集合中有没有下一个元素
  44. next()->获取下一个元素
  45. c.并发修改异常:
  46. 什么原因出现:当add之后,再次调用next方法,导致了实际操作次数和预期操作次数不相等
  47. 10.数据结构:
  48. 栈:先进后出
  49. 队列:先进先出
  50. 数组:查询快(有索引),增删慢(数组定长)
  51. 链表:查询慢,增删快
  52. 单向链表:前面的元素知道后面的元素;但是后面的不知道前面的->不能保证元素有序
  53. 双向链表:前面的记录后面的元素,后面的元素记录前面的元素-> 能保证元素有序
  54. 11.List接口(有索引,元素有序,元素可重复)
  55. 12.实现类:ArrayList
  56. 特点: 有索引,元素有序,元素可重复
  57. 数据结构: 数组
  58. 方法:
  59. add(E)
  60. add(int index,E e)->往指定索引位置上添加元素
  61. remove(Object o)->删除指定的元素
  62. remove(int index)->按照指定索引删除元素
  63. get(int index)->获取元素
  64. set(int index, E element) ->将指定索引位置上的元素,修改成element
  65. 底层:
  66. 利用空参构造newArraylist集合时,底层会创建一个长度为10的列表
  67. 不是一new就创建出来了,而是第一次add的时候,底层会创建一个长度为10的列表
  68. ArrayList长度为什么可变:数组复制
  69. 扩容多少: 1.5
  70. 今日重点:
  71. 1.会使用LinkedList的方法操作元素
  72. 2.会使用Collections集合工具类的方法
  73. 3.会使用增强for遍历集合
  74. 4.知道HashSetLinkedHashSet的特点以及使用
  75. 5.知道HashSet元素去重复的过程
  76. 6.如果HashSet存储自定义类型如何去重

第一章.LinkedList集合

1.LinkedList集合

  1. 1.概述:
  2. List接口的实现类
  3. 2.特点:
  4. 有索引,元素有序,元素可重复
  5. 底层数据结构:链表(双向链表)
  6. 3.方法:
  7. - public void addFirst(E e):将指定元素插入此列表的开头。
  8. - public void addLast(E e):将指定元素添加到此列表的结尾。
  9. - public E getFirst():返回此列表的第一个元素。
  10. - public E getLast():返回此列表的最后一个元素。
  11. - public E removeFirst():移除并返回此列表的第一个元素。
  12. - public E removeLast():移除并返回此列表的最后一个元素。
  13. - public E pop():从此列表所表示的堆栈处弹出一个元素。
  14. - public void push(E e):将元素推入此列表所表示的堆栈。
  15. - public boolean isEmpty():如果列表不包含元素,则返回true
  1. public class Demo01LinkedList {
  2. public static void main(String[] args) {
  3. LinkedList<String> linkedList = new LinkedList<>();
  4. linkedList.add("李云龙");
  5. linkedList.add("丁伟");
  6. linkedList.add("楚云飞");
  7. linkedList.add("魏和尚");
  8. linkedList.add("大彪");
  9. linkedList.add("沈泉");
  10. linkedList.add("秀琴");
  11. linkedList.addFirst("赵刚");
  12. System.out.println(linkedList);
  13. linkedList.addLast("田雨");
  14. System.out.println(linkedList);
  15. String first = linkedList.getFirst();
  16. System.out.println(first);
  17. System.out.println("=============================");
  18. //- public E pop():从此列表所表示的堆栈处弹出一个元素。
  19. //String pop = linkedList.pop();
  20. //System.out.println(pop);
  21. //System.out.println(linkedList);
  22. //- public void push(E e):将元素推入此列表所表示的堆栈。
  23. //linkedList.push("涛哥");
  24. //System.out.println(linkedList);
  25. System.out.println("==========================");
  26. for (int i = 0; i < linkedList.size(); i++) {
  27. System.out.println(linkedList.get(i));
  28. }
  29. System.out.println("=======================");
  30. Iterator<String> iterator = linkedList.iterator();
  31. while(iterator.hasNext()){
  32. System.out.println(iterator.next());
  33. }
  34. }
  35. }

1.1 LinkedList底层成员解释说明

  1. 1.LinkedList底层成员
  2. transient int size = 0; 元素个数
  3. transient Node<E> first; 第一个结点对象
  4. transient Node<E> last; 最后一个结点对象
  5. 2.Node代表的是结点对象
  6. private static class Node<E> {
  7. E item;//结点上的元素
  8. Node<E> next;//记录着下一个节点地址
  9. Node<E> prev;//记录着上一个结点地址
  10. Node(Node<E> prev, E element, Node<E> next) {
  11. this.item = element;
  12. this.next = next;
  13. this.prev = prev;
  14. }
  15. }

1.2 LinkedList中add方法源码分析

  1. LinkedList<String> linkedList = new LinkedList<>();
  2. linkedList.add("a");
  3. linkedList.add("b");
  4. System.out.println(linkedList);
  5. =========================================================
  6. public boolean add(E e) {
  7. linkLast(e);
  8. return true;
  9. }
  10. void linkLast(E e) {
  11. final Node<E> l = last; //null
  12. final Node<E> newNode = new Node<>(l, e, null);
  13. last = newNode;
  14. if (l == null)
  15. first = newNode;
  16. else
  17. l.next = newNode;
  18. size++;
  19. modCount++;
  20. }
  21. 由此证明,LinkedList底层是双向链表存储,前面的记录后面的,后面的记录前面的

1.3.LinkedList中get方法源码分析

  1. public E get(int index) {
  2. checkElementIndex(index);
  3. return node(index).item;
  4. }
  5. Node<E> node(int index) {
  6. // assert isElementIndex(index);
  7. if (index < (size >> 1)) {
  8. Node<E> x = first;
  9. for (int i = 0; i < index; i++)
  10. x = x.next;
  11. return x;
  12. } else {
  13. Node<E> x = last;
  14. for (int i = size - 1; i > index; i--)
  15. x = x.prev;
  16. return x;
  17. }
  1. index < (size >> 1)采用二分法,先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处。这样可以减少一部分不必要的遍历

第二章.Collections工具类

  1. 1.概述:专门操作集合的工具类
  2. 2.方法都是静态的
  3. 类名直接调用
  4. 3.方法:
  5. - public static void shuffle(List<?> list):打乱集合顺序。
  6. - public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。->ASCII
  7. - public static <T> void sort(List<T> listComparator<? super T> ):将集合中元素按照指定规则排序。
  8. 4.比较器:
  9. Comparator:比较器
  10. compare(o1,o2)-> o1-o2(升序) -> o2-o1(降序)
  11. Comparable:比较器
  12. compareTo(o)-> this-o(升序) -> o-this(降序)
  1. public class Test01 {
  2. public static void main(String[] args) {
  3. ArrayList<String> list = new ArrayList<>();
  4. list.add("宋江");
  5. list.add("晁盖");
  6. list.add("武松");
  7. list.add("林冲");
  8. System.out.println(list);
  9. //- public static void shuffle(List<?> list):打乱集合顺序。
  10. Collections.shuffle(list);
  11. System.out.println(list);
  12. }
  13. }
  1. public class Test02 {
  2. public static void main(String[] args) {
  3. ArrayList<String> list = new ArrayList<>();
  4. list.add("DDaaa");
  5. list.add("Accc");
  6. list.add("Bbbb");
  7. list.add("Cddd");
  8. System.out.println(list);
  9. //- public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。->ASCII
  10. Collections.sort(list);
  11. System.out.println(list);
  12. }
  13. }
  1. //Comparator比较器
  2. public class Test03 {
  3. public static void main(String[] args) {
  4. ArrayList<Person> list = new ArrayList<Person>();
  5. list.add(new Person("柳岩",36));
  6. list.add(new Person("杨幂",32));
  7. list.add(new Person("金莲",40));
  8. /*
  9. - public static <T> void sort(List<T> list,Comparator<? super T> ):
  10. 将集合中元素按照指定规则排序。
  11. */
  12. Collections.sort(list, new Comparator<Person>() {
  13. @Override
  14. public int compare(Person o1, Person o2) {
  15. return o2.getAge()-o1.getAge();
  16. }
  17. });
  18. System.out.println(list);
  19. }
  20. }
  1. public class Person {
  2. private String name;
  3. private int age;
  4. public Person() {
  5. }
  6. public Person(String name, int age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public int getAge() {
  17. return age;
  18. }
  19. public void setAge(int age) {
  20. this.age = age;
  21. }
  22. @Override
  23. public String toString() {
  24. return "Person{" +
  25. "name='" + name + '\'' +
  26. ", age=" + age +
  27. '}';
  28. }
  29. }

Comparable比较器接口:

  1. public class Person implements Comparable<Person>{
  2. private String name;
  3. private int age;
  4. public Person() {
  5. }
  6. public Person(String name, int age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public int getAge() {
  17. return age;
  18. }
  19. public void setAge(int age) {
  20. this.age = age;
  21. }
  22. @Override
  23. public String toString() {
  24. return "Person{" +
  25. "name='" + name + '\'' +
  26. ", age=" + age +
  27. '}';
  28. }
  29. @Override
  30. public int compareTo(Person o) {
  31. return o.age-this.age;
  32. }
  33. }
  1. public class Test04 {
  2. public static void main(String[] args) {
  3. ArrayList<Person> list = new ArrayList<Person>();
  4. list.add(new Person("柳岩",36));
  5. list.add(new Person("杨幂",32));
  6. list.add(new Person("金莲",40));
  7. Collections.sort(list);
  8. System.out.println(list);
  9. }
  10. }

第三章.增强for

1.增强for的介绍以及基本使用

  1. 1.作用: 用于遍历集合
  2. 2.格式:
  3. for(元素类型 变量名:要遍历的集合或者数组){
  4. 变量名就代表的是每一个元素
  5. }
  6. 3.快捷键:
  7. 集合名或者数组名.for
  1. public class Test01 {
  2. public static void main(String[] args) {
  3. ArrayList<String> list = new ArrayList<>();
  4. list.add("宋江");
  5. list.add("晁盖");
  6. list.add("武松");
  7. list.add("林冲");
  8. System.out.println("========普通for==========");
  9. for (int i = 0; i < list.size(); i++) {
  10. System.out.println(list.get(i));
  11. }
  12. System.out.println("=======迭代器========");
  13. Iterator<String> iterator = list.iterator();
  14. while(iterator.hasNext()){
  15. System.out.println(iterator.next());
  16. }
  17. System.out.println("=======增强for======");
  18. for (String s : list) {
  19. System.out.println(s);
  20. }
  21. }
  22. }
  1. public class Test02 {
  2. public static void main(String[] args) {
  3. int[] arr = {1,2,3,4,5,6};
  4. for (int i : arr) {
  5. System.out.println(i);
  6. }
  7. }
  8. }

2.使用增强for时要注意的事项

  1. 1.注意:
  2. 在使用增强for的时候尽量不要改变集合长度,否则会出现并发修改异常
  3. 2.如果使用增强for遍历集合,实现原理是迭代器
  4. 如果使用增强for遍历数组,实现原理是普通for

day15[集合] - 图1

day15[集合] - 图2

第四章.泛型

1.为什么要使用泛型

  1. 1.如果不使用泛型的话:集合类型默认为Object
  2. 很可能会出现类型转换异常
  3. 2.使用泛型好处:
  4. a.限制数据类型,防止类型转换异常

2.泛型的定义

2.1含有泛型的类

  1. 1.格式:
  2. public class 类名<E>{
  3. }
  4. 2.什么时候确定泛型的类型呢?
  5. new对象是确定E的具体类型
  1. public class ArrayListMyself <E>{
  2. public void add(E e){
  3. System.out.println(e);
  4. }
  5. }
  1. public class Test01 {
  2. public static void main(String[] args) {
  3. ArrayListMyself<Integer> list = new ArrayListMyself<>();
  4. list.add(1);
  5. }
  6. }

2.2含有泛型的方法

  1. 1.格式:
  2. 修饰符 <E> 返回值 方法名(E e){}
  3. 2.什么时候确定类型
  4. 被调用的时候确定类型
  1. public class Person{
  2. public <E> void eat(E e){
  3. System.out.println(e);
  4. }
  5. }
  1. public class Test {
  2. public static void main(String[] args) {
  3. Person person = new Person();
  4. person.eat("hahahah");//传递什么类型的数据,eat中的E类型就代表什么类型的数据
  5. }
  6. }

2.3含有泛型的接口

  1. 1.定义格式:
  2. 修饰符 interface 接口名<E>{}
  3. 2.什么时候确定类型
  4. a.在实现类的时候直接确定了类型-->Scanner
  5. b.在实现类的时候还不确定类型,等到new的时候确定类型-->ArrayList
  1. public interface IteratorMyself<E> {
  2. public E next(E e);
  3. }
  4. public class ScannerMySelf implements IteratorMyself<String>{
  5. @Override
  6. public String next(String s) {
  7. return "金莲叫涛哥起床";
  8. }
  9. }
  1. public interface ListMyself <E>{
  2. public void add(E e);
  3. }
  4. public class ArrayListMySelf<E> implements ListMyself<E>{
  5. @Override
  6. public void add(E e) {
  7. System.out.println(e);
  8. }
  9. }
  10. public class Test {
  11. public static void main(String[] args) {
  12. ArrayListMySelf<Integer> list = new ArrayListMySelf<>();
  13. list.add(1);
  14. }
  15. }

3.泛型的高级使用

3.1泛型通配符->?

  1. 1.什么时候用泛型通配符:当不知道接收什么类型时->集合方面
  2. 2.用在什么位置:一般用在参数上
  1. public class Test01 {
  2. public static void main(String[] args) {
  3. Collection<String> collection = new ArrayList<>();
  4. collection.add("张三");
  5. collection.add("李四");
  6. Collection<Integer> collection1 = new ArrayList<>();
  7. collection1.add(1);
  8. collection1.add(2);
  9. method(collection);
  10. method(collection1);
  11. }
  12. //定义一个方法,此方法遍历以上两个集合
  13. public static void method(Collection<?> collection){
  14. for (Object o : collection) {
  15. System.out.println(o);
  16. }
  17. }
  18. }

3.2泛型的上限下限

  1. 通配符高级使用----受限泛型
  2. 上限:
  3. - 格式: 类型名称 <? extends > 对象名称
  4. - 意义: 只能接收该类型及其子类
  5. 下限:
  6. - 格式: 类型名称 <? super > 对象名称
  7. - 意义: 只能接收该类型及其父类型
  1. public class Test01 {
  2. public static void main(String[] args) {
  3. Collection<Integer> list1 = new ArrayList<Integer>();
  4. Collection<String> list2 = new ArrayList<String>();
  5. Collection<Number> list3 = new ArrayList<Number>();
  6. Collection<Object> list4 = new ArrayList<Object>();
  7. //getElement1(list1);
  8. //getElement1(list2);//报错
  9. //getElement1(list3);
  10. //getElement1(list4);//报错
  11. //getElement2(list1);//报错
  12. //getElement2(list2);//报错
  13. //getElement2(list3);
  14. //getElement2(list4);
  15. }
  16. // 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
  17. public static void getElement1(Collection<? extends Number> coll) {
  18. }
  19. // 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
  20. public static void getElement2(Collection<? super Number> coll) {
  21. }
  22. }

第五章.红黑树(了解)

  1. 1.底层数据结构是红黑树的原因:为了查询快

day15[集合] - 图3

第六章.Set集合

1.Set集合介绍

  1. 1.Set接口 Collection接口的子接口
  2. 2.特点:
  3. 无索引
  4. 元素不可重复
  5. 无序
  6. 3.Set集合没有对Collection集合中的方法做扩充

2.HashSet集合介绍以及使用

  1. 1.概述:HashSetSet接口的实现类
  2. 2.特点:
  3. 无索引
  4. 元素不可重复(如果存储了重复的元素,后面的会把前面的覆盖掉)
  5. 无序(存进去的元素是什么顺序,第一次取出来有可能和存进去的顺序不一样)
  6. 3.数据结构:
  7. 哈希表结构
  8. jdk8之前:哈希表 = 数组+链表
  9. jdk8之后:哈希表 = 数组+链表+红黑树
  10. 4.注意:
  11. HashSet集合本身没有功能,HashSet操作元素的功能都是依靠HashMap实现的
  1. public class Demo01HashSet {
  2. public static void main(String[] args) {
  3. HashSet<String> hashSet = new HashSet<>();
  4. hashSet.add("张三");
  5. hashSet.add("李四");
  6. hashSet.add("王五");
  7. hashSet.add("赵六");
  8. hashSet.add("赵六");
  9. System.out.println(hashSet);
  10. System.out.println("==========迭代器==========");
  11. Iterator<String> iterator = hashSet.iterator();
  12. while(iterator.hasNext()){
  13. System.out.println(iterator.next());
  14. }
  15. System.out.println("=========增强for=========");
  16. for (String s : hashSet) {
  17. System.out.println(s);
  18. }
  19. }
  20. }

3.LinkedHashSet的介绍以及使用

  1. 1.概述: LinkedHashSet extends HashSet
  2. 2.特点:
  3. 没有索引
  4. 元素不可重复
  5. 有序
  6. 3.数据结构:
  7. 链表+哈希表 -> 双向链表
  8. 4.方法:
  9. HashSet使用上没区别
  1. public class Demo02HashSet {
  2. public static void main(String[] args) {
  3. LinkedHashSet<String> hashSet = new LinkedHashSet<>();
  4. hashSet.add("张三");
  5. hashSet.add("李四");
  6. hashSet.add("王五");
  7. hashSet.add("赵六");
  8. hashSet.add("赵六");
  9. System.out.println(hashSet);
  10. }
  11. }

4.哈希值

  1. 1.概述:由计算机计算出来的一个十进制的数,可以看做是一个地址值(不是内存中分配)
  1. 1.结论:
  2. a.如果想获取的是内容的哈希值,我们需要重写Object类中的hashCode()方法
  3. b.内容一样,哈希值一定一样
  4. 内容不一样,哈希值也有可能一样
  1. public class Test01 {
  2. public static void main(String[] args) {
  3. Person p1 = new Person("柳岩", 36);
  4. Person p2 = new Person("柳岩", 36);
  5. System.out.println(p1);
  6. System.out.println(p1.hashCode());
  7. System.out.println(p2.toString());
  8. System.out.println("===============");
  9. String s1 = "abc";
  10. String s2 = new String("abc");
  11. String s3 = new String("abc");
  12. System.out.println(s1.hashCode());//96354
  13. System.out.println(s2.hashCode());//96354
  14. System.out.println(s3.hashCode());//96354
  15. System.out.println("==============");
  16. String s4 = "通话";
  17. String s5 = "重地";
  18. System.out.println(s4.hashCode());//1179395
  19. System.out.println(s5.hashCode());//1179395
  20. }
  21. }

5.字符串的哈希值是如何算出来的

  1. public class Test02 {
  2. public static void main(String[] args) {
  3. String s = "abc";
  4. System.out.println(s.hashCode());
  5. }
  6. }
  1. String s = "abc" -> char[] value = {'a','b','c'}
  2. public int hashCode() {
  3. int h = hash;
  4. if (h == 0 && value.length > 0) {
  5. char val[] = value;
  6. for (int i = 0; i < value.length; i++) {
  7. h = 31 * h + val[i];
  8. }
  9. hash = h;
  10. }
  11. return h;
  12. }
  13. 第一圈循环: h = 31*h+'a' = 31*0+97 = 97
  14. 第二圈循环: h = 31*h+'b' = 31*97+98 = 3105
  15. 第三圈循环: h = 31*h+'c' = 31*3105+99 = 96354

小细节:

为什么String中重写的hashCode用31去参与运算呢?

31是一个质数,31参与运算就是为了减少字符串不同但是哈希值相同的情况

内容不同,但是算出来的哈希值相同-> 哈希碰撞(哈希冲突)

6.HashSet的存储去重复的过程

  1. 1.先算元素的哈希值,先比较元素哈希值,再比较元素内容
  2. 2.先比较哈希值,如果哈希值不一样,直接存储到HashSet集合中
  3. 3.如果哈希值一样,再比较内容
  4. 如果哈希值一样,内容不一样,也直接存储
  5. 如果哈希值一样(hashcode),内容也一样(equals),被认定为是同一个元素,直接去重复
  6. 注意:
  7. 我们比较哈希值时,我们比较的是对象内容的哈希值(重写hashcode方法)
  8. 我们比较内容是,我们比较的是对象的内容(重写equals方法)
  1. public class Test03 {
  2. public static void main(String[] args) {
  3. HashSet<String> hashSet = new HashSet<>(10);
  4. hashSet.add("abc");
  5. hashSet.add("通话");
  6. hashSet.add("重地");
  7. hashSet.add("abc");
  8. System.out.println(hashSet);
  9. }
  10. }

7.HashSet存储自定义类型如何去重复

  1. 1.重写hashCodeequals方法
  2. 我们比较哈希值时,我们比较的是对象内容的哈希值(重写hashcode方法)
  3. 我们比较内容是,我们比较的是对象的内容(重写equals方法)
  1. public class Person {
  2. private String name;
  3. private int age;
  4. public Person() {
  5. }
  6. public Person(String name, int age) {
  7. this.name = name;
  8. this.age = age;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. public int getAge() {
  17. return age;
  18. }
  19. public void setAge(int age) {
  20. this.age = age;
  21. }
  22. @Override
  23. public String toString() {
  24. return "Person{" +
  25. "name='" + name + '\'' +
  26. ", age=" + age +
  27. '}';
  28. }
  29. @Override
  30. public boolean equals(Object o) {
  31. if (this == o) return true;
  32. if (o == null || getClass() != o.getClass()) return false;
  33. Person person = (Person) o;
  34. return age == person.age &&
  35. Objects.equals(name, person.name);
  36. }
  37. @Override
  38. public int hashCode() {
  39. return Objects.hash(name, age);
  40. }
  41. }
  1. public class Test04 {
  2. public static void main(String[] args) {
  3. HashSet<Person> hashSet = new HashSet<>();
  4. hashSet.add(new Person("柳岩",36));
  5. hashSet.add(new Person("涛哥",16));
  6. hashSet.add(new Person("柳岩",36));
  7. System.out.println(hashSet);
  8. }
  9. }