今天我们主要讲了List的子类:ArrayList, Vector, LinkedList 以及 JDK5 的一些特性:静态导入和泛型。
接昨天的练习:
Ex3:反转链表
1 package com.cskaoyan.exercise;
2
3 public class Ex1 { 4 public static Node reverse(Node head) { 5 if (head.next == null) return head; 6 // 如果链表不止一个结点,反转head.next 7 Node reversed = reverse(head.next); 8 // 反转head结点 9 head.next.next = head; 10 head.next = null; 11 return reversed; 12 } 13 public static void print(Node head) { 14 Node x = head; 15 while (x != null) { 16 System.out.print(x.value); 17 if (x.next != null) System.out.print(" --> "); 18 x = x.next; 19 } 20 System.out.println(); 21 } 22 23 public static void main(String[] args) { 24 Node head = new Node(3); 25 head = new Node(2, head); 26 head = new Node(1, head); 27 print(head); 28 head = reverse(head); 29 print(head); 30 } 31 }
数组VS链表
数组和链表的插入,删除,随机访问操作的时间复杂度刚好相反。
① 数组使用的是连续的内存空间,可以利用CPU的高速缓存预读数据。链表的内存空间不是连续的,不能有效预读数据。当然如果数组过大,系统没有足够的连续内存空间,会抛出OOM。
② 数组的缺点是大小固定,没法动态的调整大小。如果要存储一些对象,如果数组太大,浪费内存空间;如果数组太小,我们需要重新申请一个更大数组,并将数据拷贝过去,耗时。
③如果业务对内存的使用非常苛刻,数组更适合。因为结点有指针域,更消耗内存。而且对链表的频繁插入和删除,会导致结点对象的频繁创建和销毁,有可能会导致频繁的GC活动。
List子类
- ArrayList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
1 package com.cskaoyan.list; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 5 6 /* 7 ArrayList 8 特性: 9 底层数据结构是数组,增删慢,查找快。 10 不同步, 线程不安全, 效率高。 11 存储null元素 12 构造方法: 13 ArrayList(): 默认初始大小为10 14 ArrayList(int initialCapacity): 可以指定数组的初始大小 15 ArrayList(Collection c) 16 API: 17 void ensureCapacity(int minCapacity): 避免频繁扩容 18 void trimToSize():慎用,确保元素不会在添加的情况下用。 19 */ 20 public class ArrayListDemo1 { 21 public static void main(String[] args) { 22 // ArrayList list = new ArrayList(); 23 // ArrayList list = new ArrayList(100); 24 // System.out.println(list); 25 26 Collection c = new ArrayList(); 27 c.add("hello"); 28 c.add("world"); 29 c.add("java"); 30 ArrayList list = new ArrayList(c); 31 System.out.println(list); 32 list.add(null); 33 list.add(null); 34 list.add(null); 35 System.out.println(list); 36 } 37 }
- Vector
底层数据结构是数组,查询快,增删慢
线程安全,效率低
Vector 特有的API
public void addElement(E obj)
public E elementAt(int index)
public Enumeration elements()
1 package com.cskaoyan.list; 2 3 import java.util.Vector; 4 5 /* 6 Vector 7 特性: 8 底层是数组,增删慢,查找快 9 同步, 线程安全, 效率比较低 10 存储null元素 11 API: 12 void addElement(E obj) --> void add(E e) 13 void copyInto(Object[] anArray) --> Object[] toArray() 14 E elementAt(int index) --> E get(int index) 15 void insertElementAt(E obj, int index) --> void add(int index, E e) 16 void removeAllElements() --> void clear() 17 boolean removeElement(Object obj) --> boolean remove(Object obj) 18 void removeElementAt(int index --> E remove(int index) 19 void setElementAt(E obj, int index) --> E set(int index) 20 Enumeration<E> elements() --> Iterator iterator() 21 22 int capacity() 23 void setSize(int newSize) 24 E firstElement() 25 E lastElement() 26 int indexOf(Object o, int index) 27 int lastIndexOf(Object o, int index) 28 29 Enumeration<E> elements() 30 */ 31 public class VectorDemo1 { 32 public static void main(String[] args) { 33 34 Vector vector = new Vector(); 35 // int capacity() 36 /*System.out.println(vector.capacity()); // 10 37 System.out.println(vector.size()); // 0 38 System.out.println(vector);*/ 39 40 // void setSize(int newSize) 41 /*vector.setSize(20); 42 System.out.println(vector.capacity()); 43 System.out.println(vector.size()); 44 System.out.println(vector);*/ 45 vector.add("hello"); 46 vector.add("world"); 47 vector.add("java"); 48 /*System.out.println(vector.capacity()); 49 System.out.println(vector.size()); 50 System.out.println(vector); 51 52 vector.setSize(1); 53 System.out.println(vector.capacity()); 54 System.out.println(vector.size()); 55 System.out.println(vector);*/ 56 57 // vector.clear(); 58 //E firstElement() 59 // System.out.println(vector.firstElement()); // NoSuchElementException 60 //E lastElement() 61 // System.out.println(vector.lastElement()); NoSuchElementException 62 63 vector.add("hello"); 64 vector.add("hello"); 65 vector.add("hello"); 66 System.out.println(vector); 67 // int indexOf(Object o, int index) 68 // System.out.println(vector.indexOf("hello")); 69 /* System.out.println(vector.indexOf("hello", 1)); 70 System.out.println(vector.indexOf("hello", 3));*/ 71 72 // int lastIndexOf(Object o, int index) 73 /*System.out.println(vector.lastIndexOf("hello")); 74 System.out.println(vector.lastIndexOf("hello", 3)); 75 System.out.println(vector.lastIndexOf("hello", 1));*/ 76 } 77 }
1 package com.cskaoyan.list; 2 3 import java.util.Enumeration; 4 import java.util.Iterator; 5 import java.util.Vector; 6 7 /* 8 Enumeration: --> Iterator 9 boolean hasMoreElements() --> boolean hasNext() 10 E nextElement() --> E next() 11 */ 12 public class VectorDemo2 { 13 public static void main(String[] args) { 14 Vector vector = new Vector(); 15 vector.add("hello"); 16 vector.add("world"); 17 vector.add("java"); 18 19 for(Enumeration e = vector.elements(); e.hasMoreElements(); ) { 20 String s = (String) e.nextElement(); 21 System.out.println(s); 22 } 23 24 for(Iterator it = vector.iterator(); it.hasNext(); ) { 25 String s = (String) it.next(); 26 System.out.println(s); 27 } 28 } 29 }
- LinkedList
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
LinkedList 特有的API
① public void addFirst(E e)及addLast(E e)
② public E getFirst()及getLast()
③ public E removeFirst()及public E removeLast()
线程不安全,效率高
LinkedList 特有的API
① public void addFirst(E e)及addLast(E e)
② public E getFirst()及getLast()
③ public E removeFirst()及public E removeLast()
![](https://img2020.cnblogs.com/blog/1090144/202005/1090144-20200515012426148-983459623.png)
栈和队列
1 package com.cskaoyan.list; 2 3 import java.util.Iterator; 4 import java.util.LinkedList; 5 6 /* 7 Deque接口 8 概述:双端队列,可以在两端插入和删除 9 10 LinkedList implements List, Deque 11 特性: 12 底层数据结构是链表,增删快,查找慢 13 不同步, 线程不安全, 效率高 14 允许null元素 15 实现了Deque这个接口,可以当作栈,队列和双端队列来使用 16 17 构造方法: 18 LinkedList() 19 LinkedList(Collection c) 20 21 API: 22 Iterator<E> descendingIterator() 23 boolean removeFirstOccurrence(Object o) 24 boolean removeLastOccurrence(Object o) 25 26 在两端的操作 27 boolean offerFirst 28 boolean pollFirst 29 boolean peekFirst 30 31 */ 32 public class LinkedListDemo1 { 33 public static void main(String[] args) { 34 LinkedList list = new LinkedList(); 35 list.add("hello"); 36 list.add("world"); 37 list.add("java"); 38 39 // Iterator<E> descendingIterator() 40 /*for(Iterator it = list.descendingIterator(); it.hasNext(); ) { 41 String s = (String) it.next(); 42 System.out.println(s); 43 }*/ 44 45 /*list.addFirst(null); 46 list.addLast(null); 47 System.out.println(list);*/ 48 // boolean removeFirstOccurrence(Object o) 49 // list.removeFirstOccurrence(null); 50 51 // boolean removeLastOccurrence(Object o) 52 /*list.removeLastOccurrence(null); 53 System.out.println(list);*/ 54 55 // boolean offerFirst 56 // list.offerFirst(null); 57 // System.out.println(list); 58 59 // boolean pollFirst 60 /*System.out.println(list.pollFirst()); 61 System.out.println(list);*/ 62 // boolean peekFirst 63 64 System.out.println(list.peekFirst()); 65 System.out.println(list); 66 } 67 }
1 package com.cskaoyan.list; 2 3 import java.util.Deque; 4 import java.util.LinkedList; 5 6 /* 7 栈的API: 8 void push(E e) 9 E pop() 10 E peek() 11 注意事项:Java中提供了Stack类,但是我们应该优先使用Deque, 而不应该使用Stack 12 13 为什么? 14 a. Stack同步的, 效率相对来说比较低。 15 b. Stack继承了Vector, 所以Stack拥有Vector中所有的方法, 使用起来不安全。 16 17 */ 18 public class LinkedListDemo2 { 19 public static void main(String[] args) { 20 Deque stack = new LinkedList(); 21 stack.push("hello"); 22 stack.push("world"); 23 stack.push("java"); 24 25 System.out.println(stack.peek()); 26 27 while (!stack.isEmpty()) { 28 String s = (String) stack.pop(); 29 System.out.println(s); 30 } 31 32 System.out.println(stack.peek()); // null 33 } 34 }
练习:
1. 去重
2. 请用ArrayList实现栈数据结构,并测试。
3. 集合的嵌套遍历
4. 获取10个1-20之间的随机整数,要求集合中的数不能重复。
2. 请用ArrayList实现栈数据结构,并测试。
3. 集合的嵌套遍历
4. 获取10个1-20之间的随机整数,要求集合中的数不能重复。
Ex1:去重
1 package com.cskaoyan.exercise;
2
3 import java.util.ArrayList; 4 import java.util.Iterator; 5 import java.util.List; 6 import java.util.ListIterator; 7 8 /* 9 1.去重 10 输入:[a, b, c, a, a, b, c] 11 输出:[a, b, c] 12 13 思路1: 14 1. 新键一个List result = new ArrayList(); 15 2. 遍历原来的list, 判断元素是否在result中存在 16 存在:不添加 17 不存在:添加 18 3. 遍历结束后, 返回result 19 20 思路2: 21 1. 逆序遍历list 22 2. 获取元素, 截取从0到nextIndex的子列表,判断元素是否在子列表中存在 23 存在:删除元素 24 不存在: 25 3. 遍历结束后,完成去重 26 */ 27 public class Ex2 { 28 public static List disctinct1(List list) { 29 List result = new ArrayList(); 30 for(Iterator it = list.iterator(); it.hasNext(); ) { 31 Object obj = it.next(); 32 if (!result.contains(obj)) result.add(obj); 33 } 34 return result; 35 } 36 37 public static void disctinct2(List list) { 38 for(ListIterator it = list.listIterator(list.size()); it.hasPrevious(); ) { 39 Object element = it.previous(); 40 List subList = list.subList(0, it.nextIndex()); 41 if (subList.contains(element)) it.remove(); 42 } 43 } 44 45 public static void main(String[] args) { 46 // [a, b, c, a, a, b, c] 47 List list = new ArrayList(); 48 list.add('a'); 49 list.add('b'); 50 list.add('c'); 51 list.add('a'); 52 list.add('a'); 53 list.add('b'); 54 list.add('c'); 55 // List result = disctinct1(list); 56 disctinct2(list); 57 System.out.println(list); 58 } 59 }
Ex2:请用ArrayList实现栈数据结构,并测试。
1 package com.cskaoyan.exercise; 2 3 import java.util.ArrayList; 4 import java.util.EmptyStackException; 5 6 /* 7 a. 如果一个类持有某个类的成员,那么就能够"拥有"这个类的所有公共方法。 8 但是我们可以对这些方法进行限制。 9 b. 组合还可以"加强"另一个类的方法。 10 c. 可以组合多个对象 11 12 "加强"一个方法 13 a. 继承 14 b. 组合 15 16 设计原则: 17 优先使用组合,而不是继承 18 19 什么情况下,可以使用继承呢? 20 如果两个类之间有"is a"的关系,可以使用继承。 21 22 GO语言不支持继承。 23 */ 24 public class MyStack { 25 private ArrayList list; //组合 26 private String str; 27 28 public MyStack(ArrayList list) { 29 this.list = list; 30 } 31 32 public void push(Object obj) { 33 list.add(obj); 34 } 35 36 public Object pop() { 37 if (isEmpty()) throw new EmptyStackException(); 38 return list.remove(list.size() - 1); 39 } 40 41 public Object peek() { 42 if (isEmpty()) throw new EmptyStackException(); 43 return list.get(list.size() - 1); 44 } 45 46 public boolean isEmpty() { 47 return list.isEmpty(); 48 } 49 }
1 package com.cskaoyan.exercise; 2 3 public class Node { 4 int value; 5 Node next; 6 7 public Node(int value) { 8 this.value = value; 9 } 10 11 public Node(int value, Node next) { 12 this.value = value; 13 this.next = next; 14 } 15 16 @Override 17 public String toString() { 18 return "Node{" + 19 "value=" + value + 20 '}'; 21 } 22 }
JDK5的新特性
1 package com.cskaoyan.jdk5; 2 3 import java.util.Date; 4 import java.util.Map; 5 import static java.lang.Math.*; 6 /* 7 静态导入 8 导包 9 必须导入到类这一级别 10 作用:导入的类就好像定义在当前这个包下面。 11 静态导入: 12 必须导入到方法这一级别,并且必须是静态方法。 13 作用:导入的静态方法就好像定义在当前这个类中一样。 14 推荐:不要使用静态导入,可读性差 15 16 总结:static的用法有哪些? 17 a. 静态代码块:对类进行初始化,类加载的时候执行,并且只执行一次 18 b. 静态变量:该变量是类所有,被该类的所有成员共享 19 c. 静态方法 20 d. 静态内部类 21 e. 静态导入:导入静态方法 22 */ 23 public class StaticImportDemo1 { 24 public static void main(String[] args) { 25 // java.util.Date date = new java.util.Date(); 26 // Date date = new Date(); 27 28 System.out.println(sqrt(1.0)); 29 System.out.println(abs(-100)); 30 System.out.println(max(2, 3)); 31 } 32 }
泛型
泛型的引入
泛型的好处
1 package com.cskaoyan.jdk5; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 import java.util.List; 6 7 /* 8 泛型的好处: 9 a. 提高了程序的安全性 10 b. 将运行期遇到的问题转移到了编译期 11 c. 省去了类型强转的麻烦 12 13 设计原则: 14 及早失败原则 15 16 对大家的要求: 17 a. 可以利用泛型操作集合 18 b. 能够看懂别人些的泛型代码 19 20 */ 21 public class GenericDemo1 { 22 public static void main(String[] args) { 23 /*List list = new ArrayList(); 24 list.add("hello"); 25 list.add("world"); 26 list.add("java"); 27 list.add(1); 28 doSomething(list);*/ 29 30 List<String> list = new ArrayList<>(); 31 list.add("hello"); 32 list.add("world"); 33 list.add("java"); 34 // list.add(1); 35 doSomething(list); 36 } 37 38 private static void doSomething(List list) { 39 /*for(Iterator it = list.iterator(); it.hasNext(); ) { 40 String s = (String) it.next(); // ClassCastException 41 s.toUpperCase(); 42 }*/ 43 44 for (Iterator<String> it = list.iterator(); it.hasNext(); ) { 45 String s = it.next(); 46 System.out.println(s.toUpperCase()); 47 } 48 }
49 }
1 package com.cskaoyan.jdk5; 2 /* 3 泛型类: 4 泛型定义在类上面 5 作用域:整个类 6 格式: public class 类名<泛型类型1,…> 7 8 泛型的命名规则: 9 必须满足标识符的规则 10 业界规范: 11 一般用大写字母表示 12 T:type 13 E:element 14 K:key 15 V:value 16 */ 17 public class Tool2<T> { // 定义泛型, T就是泛型, 类似于形式参数 18 T obj; // 使用泛型 19 20 public T getObj() { 21 return obj; 22 } 23 24 public void setObj(T obj) { 25 this.obj = obj; 26 } 27 }
1 package com.cskaoyan.jdk5; 2 /* 3 如果创建对象的时候指定类型: 4 默认是Object类型 5 6 JDK7特性:<>棱形操作符,可以利用类型推断机制。 7 */ 8 public class GenericDemo2 { 9 public static void main(String[] args) { 10 /*Tool2<String> tool2 = new Tool2<String>(); // String是参数化类型, 类似实参 11 tool2.setObj("hello"); 12 // tool2.setObj(1); 13 String s = tool2.getObj(); 14 System.out.println(s);*/ 15 16 Tool2<Object> tool1 = new Tool2(); 17 tool1.setObj("hello"); 18 Object obj = tool1.getObj(); 19 20 Tool2<String> tool2 = new Tool2<>(); 21 22 Tool2 tool3 = new Tool2<String>(); 23 24 } 25 }
泛型应用
泛型类
把泛型定义在类上
格式:public class 类名<泛型类型1,…>
注意:参数化类型必须是引用类型
格式:public class 类名<泛型类型1,…>
注意:参数化类型必须是引用类型
泛型方法
把泛型定义在方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 .)
格式:public <泛型类型> 返回类型 方法名(泛型类型 .)
1 package com.cskaoyan.jdk5; 2 /* 3 泛型方法: 4 把泛型定义在方法上面 5 作用域:方法签名上或者方法体内 6 格式:public <泛型类型> 返回类型 方法名(泛型类型...) 7 8 为什么泛型要定义在返回值类型的前面呢? 9 因为返回值类型也可以是泛型,泛型必须先定义才能使用 10 11 问题: 12 有泛型方法的类一定是泛型类吗? 不是 13 14 */ 15 public class Tool3 { 16 17 /*public String echo(String s) { 18 return s; 19 } 20 21 public Date echo(Date s) { 22 return s; 23 }*/ 24 25 /*public Object echo(Object obj) { 26 return obj; 27 }*/ 28 // T t; 29 public <T> T echo(T t) { 30 return t; 31 } 32 33 /*public void method(T t) { 34 35 }*/ 36 // ... 37 }
1 package com.cskaoyan.jdk5; 2 3 public class GenericDemo3 { 4 public static void main(String[] args) { 5 /*Tool3 tool3 = new Tool3(); 6 Object obj = tool3.echo("hello"); 7 String s = (String) obj; 8 System.out.println(s.toUpperCase());*/ 9 10 Tool3 tool3 = new Tool3(); 11 String s = tool3.echo("hello"); 12 System.out.println(s); 13 } 14 }
泛型接口
把泛型定义在接口上
格式:public interface 接口名<泛型类型1…>
格式:public interface 接口名<泛型类型1…>
package com.cskaoyan.jdk5; /* 泛型接口: 在接口上面定义放行 作用域:接口内 格式:格式:public interface 接口名<泛型类型1…> */ public interface Auto<T> { T run(T t); }
package com.cskaoyan.jdk5; /* 泛型接口的实现类 a. 普通类 b. 泛型类 */ public class Car implements Auto<String> { @Override public String run(String s) { return null; } }
package com.cskaoyan.jdk5;
/*public class Bus<T> implements Auto<String> {
@Override
public String run(String s) {
return null;
}
}*/
public class Bus<T> implements Auto<T> {// Bus<T>是定义泛型T, Auto<T>是使用泛型T @Override public T run(T t) { return null; } }
1 package com.cskaoyan.jdk5; 2 3 import java.util.ArrayList; 4 import java.util.Date; 5 import java.util.List; 6 7 /* 8 数组: 9 问题1:String是Object的子类吗? 是 10 问题2:String[]是Object[]的子类吗? 不是 11 父类的引用变量可以指向子类的对象。 12 13 JVM对数组进行特殊"照顾",但是这样也会引入一些问题。 14 */ 15 public class GenericDemo4 { 16 public static void main(String[] args) { 17 /*String[] strs = {"hello", "world", "java"}; 18 Object[] objs = strs; // JVM对数组进行特殊"照顾" 19 objs[1] = new Date(); // ArrayStoreException*/ 20 21 /*List<String> strs = new ArrayList<>(); 22 strs.add("hello"); 23 strs.add("world"); 24 strs.add("java"); 25 List<Object> objs = strs;*/ 26 } 27 }
泛型通配符:
① 泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意的Java类了
② ? extends E
向下限定,E及其子类
③ ? super E
向上限定,E及其父类
1 package com.cskaoyan.jdk5; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 5 6 /* 7 泛型通配符: 8 目的:提供类似数组的功能,但是不要引入数组中可能出现的问题。 9 10 ① 泛型通配符<?> 11 任意类型,如果没有明确,那么就是Object以及任意的Java类了 12 ② ? extends E 13 向下限定,E及其子类 14 ③ ? super E 15 向上限定,E及其父类 16 17 */ 18 public class GenericDemo5 { 19 public static void main(String[] args) { 20 // Collection<Object> c1 = new ArrayList<Object>(); 21 /*Collection<Object> c2 = new ArrayList<Animal>(); 22 Collection<Object> c3 = new ArrayList<Cat>(); 23 Collection<Object> c4 = new ArrayList<Dog>();*/ 24 25 /*Collection<?> c1 = new ArrayList<Object>(); 26 Collection<?> c2 = new ArrayList<Animal>(); 27 Collection<?> c3 = new ArrayList<Cat>(); 28 Collection<?> c4 = new ArrayList<Dog>();*/ 29 // c2.add(new Dog()); 30 // c2.add(new Animal()); 31 32 // Collection<? extends Animal> c1 = new ArrayList<Object>(); 33 /* Collection<? extends Animal> c2 = new ArrayList<Animal>(); 34 Collection<? extends Animal> c3 = new ArrayList<Cat>(); 35 Collection<? extends Animal> c4 = new ArrayList<Dog>();*/ 36 /*c3.add(new Animal()); 37 c3.add(new Dog()); 38 c3.add(new Cat());*/ 39 40 Collection<? super Animal> c1 = new ArrayList<Object>(); 41 Collection<? super Animal> c2 = new ArrayList<Animal>(); 42 // Collection<? super Animal > c3 = new ArrayList<Cat>(); 43 // Collection<? super Animal> c4 = new ArrayList<Dog>(); 44 c2.add(new Animal()); 45 c2.add(new Dog()); 46 c2.add(new Cat()); 47 // c2.add(new Object()); 48 } 49 } 50 51 class Animal { 52 } 53 54 class Dog extends Animal { 55 } 56 57 class Cat extends Animal { 58 }