总结
Collection
|-- List
|-- ArrayList
|-- Vector
|-- Stack
|-- LinkedList
|-- Queue
|-- Deque
|-- LinkedList
LinkedList
LinkedList 实现了接口 List, Deque
- 底层是链表, 增删快, 查找慢
- 不同步, 线程不安全, 效率高
构造方法
LinkedList()
构造一个空列表。
LinkedList(Collection<? extends E> c)
构造一个包含指定 collection 中的元素的列表,这些元素按其 collection 的迭代器返回的顺序排列。
/
LinkedList 特有的API
public void addFirst(E e)及addLast(E e)
public E getFirst()及getLast()
public E removeFirst()及public E removeLast()
//
Deque接口实现的方法
第一个元素(头部) 最后一个元素(尾部)
抛出异常 特殊值 抛出异常 特殊值
插入 addFirst(e) offerFirst(e) addLast(e) offerLast(e)
移除 removeFirst() pollFirst() removeLast() pollLast()
检查 getFirst() peekFirst() getLast() peekLast()
栈的API
void push(E e)
E pop()
E peek()
///
ListIterator<E> listIterator(int index)
返回此列表中的元素的列表迭代器(按适当顺序),从列表中指定位置开始
Iterator<E> descendingIterator()
返回以逆向顺序在此双端队列的元素上进行迭代的迭代器
boolean removeFirstOccurrence(Object o)
从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)。
boolean removeLastOccurrence(Object o)
从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)。
继承Vector的栈Stack
API:
boolean empty()
E peek()
E pop()
E push(E item)
int search(Object o)
但是不推荐使用Vector的栈,而推荐使用Deque的栈,因为:
- Stack继承Vector的, 效率比较低
- Stack具有Vector所有的API, 可以在任意位置添加和删除, 使用起来不是很安全。
Vector的栈
问题:哪一端当作栈顶比较好?尾
MyStack持有了Vector对象,因此,它就可以具有Vector的所有功能,并且可以对这些功能进行"加强".
哪些技术可以对一个方法进行加强?
a. 继承
b. 组合
哪一种方式比较好?
设计原则:组合优先于继承。
什么时候可以使用继承
类与类之间是 is a 的关系的时候。
public class MyStack {
private Vector vector; //组合
public MyStack(Vector vector) {
this.vector = vector;
}
public void push(Object obj){
vector.add(obj);
}
public Object pop() {
return vector.remove(vector.size() - 1);
}
public Object peek() {
return vector.get(vector.size() - 1);
}
public boolean isEmpty() {
return vector.isEmpty();
}
}
去重练习
思路1:
a. 新建一个List
b. 遍历原集合,拿到每一个元素
如果在新集合中已经存在, 不添加
如果在新集合中不存在, 添加
public static List removeDuplicated(List list) {
List result = new ArrayList();
for(Iterator it = list.iterator(); it.hasNext(); ) {
Object obj = it.next();
if (!result.contains(obj)) {
result.add(obj);
}
}
return result;
}
思路2:
拿到第一个元素, 然后删除后面所有与第一个元素相等的元素
拿到第二个元素, 然后删除后面所有与第二个元素相等的元素...
public static void removeDuplicated(List list) {
// int size = list.size();
for(int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
for (int j = i + 1; j < list.size(); j++) {
if (obj.equals(list.get(j))) {
list.remove(j);
j--; // caution! 需要回退一步
}
}
}
}
泛形
概念
有许多原因促成了泛型的出现,而最引人注目的一个原因,就是为了创造容器类。有些情况下,我们确实希望容器能够同时持有多种类型的对象。但是,通常而言我们只会使用容器来存储一种类型的对象。
泛型的主要目的之一就是用来指定容器要持有什么类型的,因此与其使用Object,我们更喜欢暂时不指定类型,而是稍后再決定具体使用什么类型。要达到这个目的,需要使用类型参数,用尖括号括住,放在类名后面。然后在使用这个类的时候,再用实际的类型替换此类型参数。
如果不申明泛型的类,JAVA则会自动使用Object来描述创建的对象,使用数据时必须进行强制类型转换,存在安全隐患。
泛型类
格式:public class 类名<泛型类型1,…>
泛型的命名
语法规则:只要满足标识符的规则即可
业界规则:用大写的字母表示
T type
E element
K key
V value
U
public class Tool<T> {// T就是泛型(类型形参), 泛型定义在类上, 作用域就是整个类
T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
泛型接口
格式:public interface 接口名<泛型类型1…>
泛型接口的实现类
a. 泛型类
b. 普通类
public interface Auto<T> { //泛型定义在接口上面, 作用域就是整个接口
T run(T t);
}
泛形方法
public static<T> void fun(Messages temp){
System.out.println(temp.getMsg());
}
泛型的好处:
a. 提高了程序的安全性
b. 将运行期遇到的问题转移到了编译期
c. 省去了类型强转的麻烦
设计原则:尽早失败原则
a.节省计算资源
b.容易定位问题
public class GenericDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
dosomething(list);
}
private static void dosomething(List list) {
for(Iterator it = list.iterator(); it.hasNext(); ) {
String s = (String) it.next(); // ClassCastException
System.out.println(s.toUpperCase());
}
}//不使用泛形需强制转换
private static void dosomething(List<String> list) {
for(Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s.toUpperCase());
}
}
}
问题:
String是Object的子类吗? 是
String[]是Object[]的子类吗? 不是
为了代码书写方便, JVM对数组类型进行了特殊处理。 但是同时也引入了一些问题。
数组是一个可协变类型。
集合不是一个可协变类型。
public static void main(String[] args) {
String s = "hello";
Object obj = s;
String[] strs = new String[10];
Object[] objs = strs;
Date[] dates = strs;//error
objs[1] = new Object(); // ArrayStoreException*/
List<String> strs = new ArrayList<>();
List<Object> objs = strs;//error
}
泛型通配符
利用泛型技术虽然解决了向下转型所带来的安全隐患,但同时又会产生一个新的问题:即便是同一个类,由于泛形设置类型不同,其对象表示的含义也不一样,因此不能直接进行引用操作.
例如
public static void fun(Messages<String> temp){
此方法只能用来接收String类型的对象
System.out.println(temp.getMsg());
}
public static void fun(Messages<?> temp){
此方法可以接受任意类型的的对象
System.out.println(temp.getMsg());
}
class Messages<T>{
private T msg;
public T getMsg() {
return msg;
}
public void setMsg(T msg) {
this.msg = msg;
}
}
一个问题:能否使用Object描述一切泛型,或者不设置类型?
- 问题的关键是:在明确设置一个类为泛型类型时若没有继承的概念范畴,也就是说虽然Object类与String类属于父子关系,但在泛型中就是两个完全独立的概念
- 如果在定义时不设置类型,也可以实现任意泛型类对象的就收,但存在一个问题:不指派类型,JAVA则默认Object,也就是说方法中可以随意修改接收到的对象的属性内容,例如:
public static void main(String[] args) {
Messages<Integer> m1 = new Messages<>();
m1.setMsg(100);
fun(m1);
}
public static void fun(Messages temp){
temp.setMsg("我把你Integer改成String了牛不牛皮");
System.out.println(temp.getMsg());
}
此时输出的就是String类,Integer被改了属性
- 上述方法就是非常不严谨的,所以必须要使用通配符<?>来制约这种随意修改数据的问题,故被通配符修饰的类型只可表示取出,不能设置内容,否则编译器报错.
设置泛型上下限
-
? extends E
设置上限,E及其子类 -
? super E
设置下限,E及其父类
泛型通配符:提供类似数组的功能,但是同时要避免数组可能存在的问题。
Collection<Object> c1 = new ArrayList<Object>();//error
Collection<Object> c2 = new ArrayList<Animal>();//error
Collection<Object> c3 = new ArrayList<Dog>();//error
Collection<Object> c4 = new ArrayList<Cat>();//error
Collection<?> c1 = new ArrayList<Object>();
Collection<?> c2 = new ArrayList<Animal>();
Collection<?> c3 = new ArrayList<Dog>();
Collection<?> c4 = new ArrayList<Cat>();
c2.add("string");//error
Collection<? extends Animal> c1 = new ArrayList<Object>();//error
Collection<? extends Animal> c2 = new ArrayList<Animal>();
Collection<? extends Animal> c3 = new ArrayList<Dog>();
Collection<? extends Animal> c4 = new ArrayList<Cat>();
c3.add(new Animal());//error
c3.add(new Dog());//error
c3.add(new Cat());//error
Collection<? super Animal> c1 = new ArrayList<Object>();
Collection<? super Animal> c2 = new ArrayList<Animal>();
Collection<? super Animal> c3 = new ArrayList<Dog>();//error
Collection<? super Animal> c4 = new ArrayList<Cat>();//error
c2.add(new Animal());
c2.add(new Object());//error
练习:往List类型的对象中,插入一个整数。
如何解决这个问题?我们可以利用反射绕过泛型检查
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
// list.add(1);//error
Class<? extends List> cl = list.getClass();
Method addMethod = cl.getDeclaredMethod("add", Object.class);
addMethod.invoke(list, 1);
System.out.println(list);
/* String s = list.get(3);
System.out.println(s);*/无法获取
如何获取这个值呢?继续通过反射
Method getMethod = cl.getDeclaredMethod("get", int.class);
int value = (int) getMethod.invoke(list, 3);
System.out.println(value);
数组和集合的转换
List<String> list = new ArrayList<>();
list.add("刘亦菲");
list.add("茜茜");
list.add("赵灵儿");
String[] arr = new String[3];
String[] strs = list.toArray(arr);
System.out.println(Arrays.toString(strs));//[刘亦菲, 茜茜, 赵灵儿]
System.out.println(strs == arr); // true*/
String[] arr = new String[1];
String[] strs = list.toArray(arr);
System.out.println(Arrays.toString(strs));//[刘亦菲, 茜茜, 赵灵儿]
System.out.println(Arrays.toString(arr));//[null]
System.out.println(strs == arr);//false
String[] arr = new String[list.size()];
list.toArray(arr);
System.out.println(Arrays.toString(arr));[刘亦菲, 茜茜, 赵灵儿]
增强形循环:foreach
foreach循环:
作用:简化数组和Collection集合的遍历
格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
好处:简化了数组和集合的遍历操作
坏处:
a. 不能获取索引信息
b. 不能修改集合和数组
建议:
尽量使用foreach循环, 简洁易懂。
原理:
编译器对数组单独进行处理
集合的foreach循环底层就是迭代器
问题:什么情况下可以使用foreach循环呢?
数组, 集合
实现了Iterable接口的对象, 都可以使用foreach循环
Iterable: (JDK1.5)
实现这个接口允许对象成为 "foreach" 语句的目标。
API:
Iterator<T> iterator()
// 遍历数组
一般方法:
int[] arr = {1, 2, 3};
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
foreach方法:
for(int a : arr) {
System.out.print(a + " ");
}
System.out.println();
// 遍历集合
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
一般方法:
for(Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.print(s + " ");
}
System.out.println();
foreach方法:
for(String s : list) {
System.out.print(s + " ");
// if ("hello".equals(s)) list.remove(s);//reeor
}
System.out.println();