课前回顾:1.Lock锁:接口-> 实现类 ->ReentractLock2.方法: lock()获取锁 unlock()释放锁3.实现原理:乐观锁,比较并交换->CAS4.Condition:线程阻塞队列->和Lock锁结合使用获取: Lock类中的newCondition()方法: await()->线程等待signal()->线程唤醒5.线程池:Executors获取: ExecutorService newFixedThreadPool(int n)->获取线程池对象,并且指明池子中最多能都n个线程submit(Runnable r)submit(Callable c)call方法和run方法的区别:相同点:都是设置线程任务不同点:call()有返回值,可以直接throws异常run()没有返回值,不能直接throws异常Future接口:用于接收run方法或者call方法的返回值,但是run方法没有返回值,所以,不用Future接收get()->将call方法的返回值获取到6.定时器:Timer->可以让线程任务每隔多长时间执行一次构造:Timer()方法:void schedule(TimerTask task, Date firstTime, long period)task:设置线程任务firsttime:从哪个时间开始计算period:间隔多长时间7.集合a.概述:容器b.作用:存储元素c.特点:长度可变只能存储引用数据类型8.Collection:单列集合的顶级接口方法:add(E e)->添加元素addAll(Collection c)->合并集合clear()清空集合元素contains(E e)集合中是否包含指定的元素remove(E e):删除指定元素size():获取几个元素个数isEmpty() : 判断集合中是否有元素toArray() :将集合转成数组9.迭代器:Iterator接口a.获取:Collection下的方法 Iterator<E> iterator()b.方法:hashNext()->判断集合中有没有下一个元素next()->获取下一个元素c.并发修改异常:什么原因出现:当add之后,再次调用next方法,导致了实际操作次数和预期操作次数不相等10.数据结构:栈:先进后出队列:先进先出数组:查询快(有索引),增删慢(数组定长)链表:查询慢,增删快单向链表:前面的元素知道后面的元素;但是后面的不知道前面的->不能保证元素有序双向链表:前面的记录后面的元素,后面的元素记录前面的元素-> 能保证元素有序11.List接口(有索引,元素有序,元素可重复)12.实现类:ArrayList特点: 有索引,元素有序,元素可重复数据结构: 数组方法:add(E)add(int index,E e)->往指定索引位置上添加元素remove(Object o)->删除指定的元素remove(int index)->按照指定索引删除元素get(int index)->获取元素set(int index, E element) ->将指定索引位置上的元素,修改成element底层:利用空参构造newArraylist集合时,底层会创建一个长度为10的列表不是一new就创建出来了,而是第一次add的时候,底层会创建一个长度为10的列表ArrayList长度为什么可变:数组复制扩容多少: 1.5倍今日重点:1.会使用LinkedList的方法操作元素2.会使用Collections集合工具类的方法3.会使用增强for遍历集合4.知道HashSet和LinkedHashSet的特点以及使用5.知道HashSet元素去重复的过程6.如果HashSet存储自定义类型如何去重
第一章.LinkedList集合
1.LinkedList集合
1.概述:是List接口的实现类2.特点:有索引,元素有序,元素可重复底层数据结构:链表(双向链表)3.方法:- 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。
public class Demo01LinkedList {public static void main(String[] args) {LinkedList<String> linkedList = new LinkedList<>();linkedList.add("李云龙");linkedList.add("丁伟");linkedList.add("楚云飞");linkedList.add("魏和尚");linkedList.add("大彪");linkedList.add("沈泉");linkedList.add("秀琴");linkedList.addFirst("赵刚");System.out.println(linkedList);linkedList.addLast("田雨");System.out.println(linkedList);String first = linkedList.getFirst();System.out.println(first);System.out.println("=============================");//- public E pop():从此列表所表示的堆栈处弹出一个元素。//String pop = linkedList.pop();//System.out.println(pop);//System.out.println(linkedList);//- public void push(E e):将元素推入此列表所表示的堆栈。//linkedList.push("涛哥");//System.out.println(linkedList);System.out.println("==========================");for (int i = 0; i < linkedList.size(); i++) {System.out.println(linkedList.get(i));}System.out.println("=======================");Iterator<String> iterator = linkedList.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}}
1.1 LinkedList底层成员解释说明
1.LinkedList底层成员transient int size = 0; 元素个数transient Node<E> first; 第一个结点对象transient Node<E> last; 最后一个结点对象2.Node代表的是结点对象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;}}
1.2 LinkedList中add方法源码分析
LinkedList<String> linkedList = new LinkedList<>();linkedList.add("a");linkedList.add("b");System.out.println(linkedList);=========================================================public boolean add(E e) {linkLast(e);return true;}void linkLast(E e) {final Node<E> l = last; //nullfinal Node<E> newNode = new Node<>(l, e, null);last = newNode;if (l == null)first = newNode;elsel.next = newNode;size++;modCount++;}由此证明,LinkedList底层是双向链表存储,前面的记录后面的,后面的记录前面的
1.3.LinkedList中get方法源码分析
public E get(int index) {checkElementIndex(index);return node(index).item;}Node<E> node(int index) {// assert isElementIndex(index);if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}
index < (size >> 1)采用二分法,先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处。这样可以减少一部分不必要的遍历
第二章.Collections工具类
1.概述:专门操作集合的工具类2.方法都是静态的类名直接调用3.方法:- public static void shuffle(List<?> list):打乱集合顺序。- public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。->ASCII- public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。4.比较器:Comparator:比较器compare(o1,o2)-> o1-o2(升序) -> o2-o1(降序)Comparable:比较器compareTo(o)-> this-o(升序) -> o-this(降序)
public class Test01 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("宋江");list.add("晁盖");list.add("武松");list.add("林冲");System.out.println(list);//- public static void shuffle(List<?> list):打乱集合顺序。Collections.shuffle(list);System.out.println(list);}}
public class Test02 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("DDaaa");list.add("Accc");list.add("Bbbb");list.add("Cddd");System.out.println(list);//- public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。->ASCIICollections.sort(list);System.out.println(list);}}
//Comparator比较器public class Test03 {public static void main(String[] args) {ArrayList<Person> list = new ArrayList<Person>();list.add(new Person("柳岩",36));list.add(new Person("杨幂",32));list.add(new Person("金莲",40));/*- public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。*/Collections.sort(list, new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o2.getAge()-o1.getAge();}});System.out.println(list);}}
public class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}}
Comparable比较器接口:
public class Person implements Comparable<Person>{private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Person o) {return o.age-this.age;}}
public class Test04 {public static void main(String[] args) {ArrayList<Person> list = new ArrayList<Person>();list.add(new Person("柳岩",36));list.add(new Person("杨幂",32));list.add(new Person("金莲",40));Collections.sort(list);System.out.println(list);}}
第三章.增强for
1.增强for的介绍以及基本使用
1.作用: 用于遍历集合2.格式:for(元素类型 变量名:要遍历的集合或者数组){变量名就代表的是每一个元素}3.快捷键:集合名或者数组名.for
public class Test01 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("宋江");list.add("晁盖");list.add("武松");list.add("林冲");System.out.println("========普通for==========");for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}System.out.println("=======迭代器========");Iterator<String> iterator = list.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println("=======增强for======");for (String s : list) {System.out.println(s);}}}
public class Test02 {public static void main(String[] args) {int[] arr = {1,2,3,4,5,6};for (int i : arr) {System.out.println(i);}}}
2.使用增强for时要注意的事项
1.注意:在使用增强for的时候尽量不要改变集合长度,否则会出现并发修改异常2.如果使用增强for遍历集合,实现原理是迭代器如果使用增强for遍历数组,实现原理是普通for
![day15[集合] - 图1](/uploads/projects/liuye-6lcqc@vk53cd/37d8e488daaf4a878eeeb35da32db4c6.png)
![day15[集合] - 图2](/uploads/projects/liuye-6lcqc@vk53cd/08adeee1a1042528cf3eab1cb5dc36fe.png)
第四章.泛型
1.为什么要使用泛型
1.如果不使用泛型的话:集合类型默认为Object很可能会出现类型转换异常2.使用泛型好处:a.限制数据类型,防止类型转换异常
2.泛型的定义
2.1含有泛型的类
1.格式:public class 类名<E>{}2.什么时候确定泛型的类型呢?new对象是确定E的具体类型
public class ArrayListMyself <E>{public void add(E e){System.out.println(e);}}
public class Test01 {public static void main(String[] args) {ArrayListMyself<Integer> list = new ArrayListMyself<>();list.add(1);}}
2.2含有泛型的方法
1.格式:修饰符 <E> 返回值 方法名(E e){}2.什么时候确定类型被调用的时候确定类型
public class Person{public <E> void eat(E e){System.out.println(e);}}
public class Test {public static void main(String[] args) {Person person = new Person();person.eat("hahahah");//传递什么类型的数据,eat中的E类型就代表什么类型的数据}}
2.3含有泛型的接口
1.定义格式:修饰符 interface 接口名<E>{}2.什么时候确定类型a.在实现类的时候直接确定了类型-->Scannerb.在实现类的时候还不确定类型,等到new的时候确定类型-->ArrayList
public interface IteratorMyself<E> {public E next(E e);}public class ScannerMySelf implements IteratorMyself<String>{@Overridepublic String next(String s) {return "金莲叫涛哥起床";}}
public interface ListMyself <E>{public void add(E e);}public class ArrayListMySelf<E> implements ListMyself<E>{@Overridepublic void add(E e) {System.out.println(e);}}public class Test {public static void main(String[] args) {ArrayListMySelf<Integer> list = new ArrayListMySelf<>();list.add(1);}}
3.泛型的高级使用
3.1泛型通配符->?
1.什么时候用泛型通配符:当不知道接收什么类型时->集合方面2.用在什么位置:一般用在参数上
public class Test01 {public static void main(String[] args) {Collection<String> collection = new ArrayList<>();collection.add("张三");collection.add("李四");Collection<Integer> collection1 = new ArrayList<>();collection1.add(1);collection1.add(2);method(collection);method(collection1);}//定义一个方法,此方法遍历以上两个集合public static void method(Collection<?> collection){for (Object o : collection) {System.out.println(o);}}}
3.2泛型的上限下限
通配符高级使用----受限泛型上限:- 格式: 类型名称 <? extends 类 > 对象名称- 意义: 只能接收该类型及其子类下限:- 格式: 类型名称 <? super 类 > 对象名称- 意义: 只能接收该类型及其父类型
public class Test01 {public static void main(String[] args) {Collection<Integer> list1 = new ArrayList<Integer>();Collection<String> list2 = new ArrayList<String>();Collection<Number> list3 = new ArrayList<Number>();Collection<Object> list4 = new ArrayList<Object>();//getElement1(list1);//getElement1(list2);//报错//getElement1(list3);//getElement1(list4);//报错//getElement2(list1);//报错//getElement2(list2);//报错//getElement2(list3);//getElement2(list4);}// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类public static void getElement1(Collection<? extends Number> coll) {}// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类public static void getElement2(Collection<? super Number> coll) {}}
第五章.红黑树(了解)
1.底层数据结构是红黑树的原因:为了查询快
![day15[集合] - 图3](/uploads/projects/liuye-6lcqc@vk53cd/25a210881382cbad87135900b5b07a99.png)
第六章.Set集合
1.Set集合介绍
1.Set接口 是Collection接口的子接口2.特点:无索引元素不可重复无序3.Set集合没有对Collection集合中的方法做扩充
2.HashSet集合介绍以及使用
1.概述:HashSet是Set接口的实现类2.特点:无索引元素不可重复(如果存储了重复的元素,后面的会把前面的覆盖掉)无序(存进去的元素是什么顺序,第一次取出来有可能和存进去的顺序不一样)3.数据结构:哈希表结构jdk8之前:哈希表 = 数组+链表jdk8之后:哈希表 = 数组+链表+红黑树4.注意:HashSet集合本身没有功能,HashSet操作元素的功能都是依靠HashMap实现的
public class Demo01HashSet {public static void main(String[] args) {HashSet<String> hashSet = new HashSet<>();hashSet.add("张三");hashSet.add("李四");hashSet.add("王五");hashSet.add("赵六");hashSet.add("赵六");System.out.println(hashSet);System.out.println("==========迭代器==========");Iterator<String> iterator = hashSet.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println("=========增强for=========");for (String s : hashSet) {System.out.println(s);}}}
3.LinkedHashSet的介绍以及使用
1.概述: LinkedHashSet extends HashSet2.特点:没有索引元素不可重复有序3.数据结构:链表+哈希表 -> 双向链表4.方法:和HashSet使用上没区别
public class Demo02HashSet {public static void main(String[] args) {LinkedHashSet<String> hashSet = new LinkedHashSet<>();hashSet.add("张三");hashSet.add("李四");hashSet.add("王五");hashSet.add("赵六");hashSet.add("赵六");System.out.println(hashSet);}}
4.哈希值
1.概述:由计算机计算出来的一个十进制的数,可以看做是一个地址值(不是内存中分配)
1.结论:a.如果想获取的是内容的哈希值,我们需要重写Object类中的hashCode()方法b.内容一样,哈希值一定一样内容不一样,哈希值也有可能一样
public class Test01 {public static void main(String[] args) {Person p1 = new Person("柳岩", 36);Person p2 = new Person("柳岩", 36);System.out.println(p1);System.out.println(p1.hashCode());System.out.println(p2.toString());System.out.println("===============");String s1 = "abc";String s2 = new String("abc");String s3 = new String("abc");System.out.println(s1.hashCode());//96354System.out.println(s2.hashCode());//96354System.out.println(s3.hashCode());//96354System.out.println("==============");String s4 = "通话";String s5 = "重地";System.out.println(s4.hashCode());//1179395System.out.println(s5.hashCode());//1179395}}
5.字符串的哈希值是如何算出来的
public class Test02 {public static void main(String[] args) {String s = "abc";System.out.println(s.hashCode());}}
String s = "abc" -> char[] value = {'a','b','c'}public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;}第一圈循环: h = 31*h+'a' = 31*0+97 = 97第二圈循环: h = 31*h+'b' = 31*97+98 = 3105第三圈循环: h = 31*h+'c' = 31*3105+99 = 96354
小细节:
为什么String中重写的hashCode用31去参与运算呢?
31是一个质数,31参与运算就是为了减少字符串不同但是哈希值相同的情况
内容不同,但是算出来的哈希值相同-> 哈希碰撞(哈希冲突)
6.HashSet的存储去重复的过程
1.先算元素的哈希值,先比较元素哈希值,再比较元素内容2.先比较哈希值,如果哈希值不一样,直接存储到HashSet集合中3.如果哈希值一样,再比较内容如果哈希值一样,内容不一样,也直接存储如果哈希值一样(hashcode),内容也一样(equals),被认定为是同一个元素,直接去重复注意:我们比较哈希值时,我们比较的是对象内容的哈希值(重写hashcode方法)我们比较内容是,我们比较的是对象的内容(重写equals方法)
public class Test03 {public static void main(String[] args) {HashSet<String> hashSet = new HashSet<>(10);hashSet.add("abc");hashSet.add("通话");hashSet.add("重地");hashSet.add("abc");System.out.println(hashSet);}}
7.HashSet存储自定义类型如何去重复
1.重写hashCode和equals方法我们比较哈希值时,我们比较的是对象内容的哈希值(重写hashcode方法)我们比较内容是,我们比较的是对象的内容(重写equals方法)
public class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {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);}@Overridepublic int hashCode() {return Objects.hash(name, age);}}
public class Test04 {public static void main(String[] args) {HashSet<Person> hashSet = new HashSet<>();hashSet.add(new Person("柳岩",36));hashSet.add(new Person("涛哥",16));hashSet.add(new Person("柳岩",36));System.out.println(hashSet);}}
