【黑马程序员】集合框架(上)——Java复习笔记

来源:互联网 发布:数据挖掘导论 豆瓣 编辑:程序博客网 时间:2024/05/01 05:18

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

集合

(1)集合和数组的区别?    A:长度区别        数组固定,集合可变    B:内容区别        数组可以是基本类型,也可以是引用类型        集合只能是引用类型    C:元素内容        数组只能存储同一种类型        集合可以存储不同类型(其实集合一般存储的也是同一种类型)(2)集合的继承体系结构    由于需求不同,Java就提供了不同的集合类。这多个集合类的数据结构不同,但是它们都是要提供存储和遍历功能的,我们把它们的共性不断的向上提取,最终就形成了集合的继承体系结构图。    Collection        |--List            |--ArrayList            |--Vector            |--LinkedList        |--Set            |--HashSet            |--TreeSet(3)Collection的功能概述    A:添加功能         add(obj);       //添加制定元素        addAll(Collection)  //添加集合    B:删除功能         clear()             //移除所有元素            remove(obj)           removeAll(Collection)   //只要有一个元素被移除了就返回true    C:判断功能         contains(obj)       //判断集合中是否包含制定元素          containsAll(Collection)     //只有包含所有元素才返回true        isEmpty()           //判断是否为空    D:获取功能          Iterator<E> iterator()    E:长度功能          size()    F:交集         retainAll(Collection) //两个集合都有的元素,返回的布尔值表示的是调用该方法的集合是否发生过改变(而不是是否有交集)。调用该方法的集合将只保留交集    G:把集合转数组         toArray()   //返回的是Object[]数组(4)Collection集合的遍历    A:把集合转数组    toArray()    B:迭代器(5)Iterator迭代器
Iterator it = c.iterator();     //通过集合对象获取迭代器对象//判断接下来是否还有元素while(it.hasNext()){    System.out.println( (String)it.next() );//获取元素并移动到下一个位置}   
    A:Iterator是集合获取元素的方式,是依赖于集合而存在的。    B:为什么Iterator定义为一个接口而不是实现类?        因为Java中提供很多集合类,而这些类的数据结构不同,所以存储和遍历方式也不同,所以没有定义为实现类。而不管哪种集合的遍历都应该先判断再获取,所以把hasNext和next方法提取出来但并不实现,在真正的具体子类中以内部类方式体现

集合List

(1)List是Collection的子接口    特点:存储顺序和取出顺序一致,可重复。(2)List的特有功能:    A:添加功能         add(index,element)  在指定位置添加元素 addAll同理    B:删除功能         remove(index)     根据索引删除元素,返回被删除元素    C:获取功能         get(index)  获取指定位置元素返回obj对象          indexOf(obj)    返回第一次出现obj的索引         lastIndexOf 最后一次            subList(fromIndex,toIndex)      截取部分内容,包括from不包括to    D:迭代器功能         listIterator    E:修改功能         set(index,obj)  根据索引修改元素,返回被修改元素(3)List集合的特有遍历功能    A:由size()和get()结合。    B:代码演示
//创建集合对象List list = new ArrayList();            //创建并添加元素list.add("hello");list.add("world");list.add("java");           //遍历集合  for(int x=0; x<list.size(); x++) {    String s =(String) list.get(x);    System.out.println(s);}
(4)列表迭代器的特有功能    可以逆向遍历(previous方法),但是要先正向遍历,所以无意义,基本不使用。(5)并发修改异常ConcurrentModificationException    A:出现的现象        迭代器遍历集合,集合修改集合元素    B:原因        迭代器是依赖于集合的,而集合的改变迭代器并不知道。迭代器遍历元素的时候,通过集合是不能修改元素的    C:解决方案        a:迭代器遍历元素,迭代器修改元素(ListIterator)            迭代器没有添加功能,所以使用其子接口ListIterator的add();            元素是添加在刚才迭代的元素后面        b:集合遍历元素,集合修改元素(size()和get())            用for循环和get方法遍历。元素添加在集合的末尾(6)常见数据结构及其优缺点    A:栈 先进后出    (压栈)(弹栈)    B:队列 先进先出    C:数组 查询快,增删慢    D:链表 查询慢,增删快 具体见图(7)List的子类特点    ArrayList        底层数据结构是数组,查询快,增删慢。        线程不安全,效率高。    Vector        底层数据结构是数组,查询快,增删慢。        线程安全,效率低。    LinkedList        底层数据结构是链表,查询慢,增删快。        线程不安全,效率高。    应用(一般情况就用ArrayList):要安全吗?            要:Vector(即使要,也不使用这个,后面再说)            不要:ArrayList或者LinkedList                查询多;ArrayList                增删多:LinkedList  

List的子类

ArrayList

最常用的List
案例1:去除集合中的重复元素
方法1:创建新集合,遍历旧集合每个元素,拿到新集合去找,有则不管,没有就加进去

public static ArrayList delDup(ArrayList list){        ArrayList newList = new ArrayList();        //创建Iterator对象        Iterator it = list.iterator();        while (it.hasNext()){   //检查是否还有元素可以迭代            String s = (String) it.next(); //获取迭代到的元素            if (!newList.contains(s)){                newList.add(s);            }        }        return newList;    }

方法2:就在一个集合中操作,应用选择排序思想,第一个跟后面的比,第二个跟后面的比……

    public static void delDup2(ArrayList list){        //第1个跟第2,3,4...个比,第2个跟第3,4,5,6...个比,  .....最后一个不用比       for (int x = 0;x<list.size()-1;x++){           for (int y = x+1;y<list.size();y++){               //equals比较是否相同,相同就删掉               if (list.get(x).equals(list.get(y))){                   list.remove(y);                   //重点:删除以后,后面元素全部前移一位,原来的y+1变成了y,若不进行y--则会漏掉y+1,下一循环比对的是y+2,所以删除元素以后要重新判断该位置的新元素                   y--;                }           }       }    }

Vector

特有功能
a:添加
public void addElement(E obj) – add()
b:获取
public E elementAt(int index) – get()
public Enumeration elements() – iterator()

LinkedList

特有功能
a:添加
addFirst() 在开头添加
addLast()
b:删除
removeFirst() 删除并返回第一个元素
removeLast()
c:获取
getFirst() 获取第一个元素
getLast()

练习:用LinkedList模拟栈数据结构的集合并测试

import java.util.LinkedList;import static java.lang.StrictMath.pow;//静态导入/** * Created by mo on 15/11/10. * 用LinkedList模拟栈数据结构的集合并测试 */public class MyStack {//    public static void main(String[] args) {////        /**//         * LinkedList特有添加功能addFirst//         * 但是不合题目要求,题目要求是定义自己的集合类//         *///        LinkedList lk = new LinkedList();//        lk.addFirst("hello");//        lk.addFirst("world");//        lk.addFirst("java");////       printList(lk);//    }    private LinkedList linkedList;    public MyStack(){        linkedList = new LinkedList();    }    public void add(Object obj){        linkedList.addFirst(obj);    }    public Object get(){        return linkedList.removeFirst();    }    public boolean isEmpty(){        return linkedList.isEmpty();    }}class MyStackTest{    public static void main(String[] args) {        MyStack mStack = new MyStack();        mStack.add("hello");        mStack.add("world");        mStack.add("java");        while (!mStack.isEmpty()){            System.out.println(mStack.get());        }        /**         * 测试静态导入         */        System.out.println(pow(2,3)+2);    }}

泛型 (JDK5+)

泛型概述

JDK5以后的自动装箱功能让集合里也能储存基本数据类型(自动装箱转为对应的包装类),这样会导致以后的操作可能因为储存数据类型不同而出错。因此Java用泛型的方式在创建对象或调用方法时限定集合储存的数据类型,是一种参数化类型,把类型当做参数传递。

泛型一般应用于集合

格式:

<数据类型> 可以有多个String,Student,…
注意:该数据类型只能是引用类型。

JDK新特性:泛型推断 后面的<>可以不写,推荐还是写上
ArrayList array = new ArrayList<>;//注意,=号左右的<>必须一致

优点:

A:把运行时期的问题提前到了编译期间

B:避免了强制类型转换(Iterator加上泛型,遍历的时候就不用再强转了)

C:优化了程序设计,解决了黄色警告线问题,让程序更安全

泛型的前世今生

泛型的由来

Object类型作为任意类型的时候,在向下转型的时候,会隐含一个转型问题(存进去的时候是Integer,get的时候若不知情,不转Integer而转为String就会报错)

泛型类 把泛型定义在类上

表示任意类型泛型 这样在写方法的时候传入参数(T t)就很方便,不用针对每个类型重载一次方法。但是这样创建对象的时候指定的什么类型,方法就接收的对应的类型,而不能接收任意类型的参数,于是有了泛型方法

泛型方法 把泛型定义在方法上

public void show(T t){} 这样该方法就能接收任意类型参数

泛型接口 把泛型定义在接口上

在接口上定义
情况1:实现类已知是什么类型,直接在implements 接口名后面定义<>,比如,则创建对象的时候只能传入String

情况2:不知道是什么类型,则要在实现类类名和implements 接口名后面都定义,写方法传入参数就是(T t),可接收任意类型

泛型高级通配符 三种

?
? extends E 向下限定,E类及其子类
? super E 向上限定,E类及其父类
代码演示:

Collection<Object> c1=new ArrayList<Animal>(); //左右不一致,报错Collection<?> c2=new ArrayList<Animal>(); //?表示任意类型,不报错Collection<? extends Animal> c3=new ArrayList<Cat>(); //不报错Collection<? extends Animal> c4=new ArrayList<Object>(); //报错Collection<? super Animal> c5=new ArrayList<Cat>(); //报错Collection<? super Animal> c6=new ArrayList<Object>(); //不报错

foreach (JDK5+)

底层还是迭代器,但是foreach是用来替代迭代器的(和迭代器一样会出现并发修改异常)

格式:
for(元素的数据类型 变量名 : 数组或者Collection集合的对象) {
使用该变量即可,该变量其实就是数组或者集合中的元素。
}
例 int[]数组arr 遍历:for(int x:arr){sout(x);}
同样也可用于集合遍历

弊端
foreach的目标不能为null。建议在使用前,先判断是否为null。

静态导入

可以直接导入到方法级别

格式:
import static 包名….类名.方法名;

注意事项:
A:方法必须是静态的
B:如果该类下有同名的方法,就不好区分了,还得加上包名前缀,所以一般我们并不使用静态导入,但是一定要能够看懂。

可变参数

如果我们在写方法的时候,参数个数不明确,就应该定义可变参数。

格式:修饰符 返回值类型 方法名(数据类型… 变量) {}

注意:
A:该变量其实是一个数组
B:如果一个方法有多个参数,并且有可变参数,可变参数必须在最后

Arrays工具类的一个方法
Arrays.asList()把数组转成集合。该方法的参数就应用的可变参数+泛型
public static List asList(T… a)
注意:转换后的集合长度不能改变(不能增删)。

Set集合

Set集合的特点

无序(储存数据和获取数据是无序的),唯一

HashSet集合

A:底层数据结构是哈希表(是一个元素为链表的数组)
HashSet不保证set的迭代顺序,特别是不保证该顺序恒久不变

B:HashSet的add方法底层依赖两个方法:hashCode()和equals()

执行顺序:
首先比较哈希值是否相同
相同:继续执行equals()方法 || 比较地址值
返回true:元素重复了,不添加
返回false:直接把元素添加到集合
不同:就直接把元素添加到集合

C:如何保证元素唯一性的呢?
由hashCode()和equals()保证的。如果是自定义对象,要重写equals和hashCode方法,自动生成即可,见上图

D:哈希表是一个元素为链表的数组,综合了数组和链表的优点

E:HashSet子类LinkedHashSet(该集合是有序的):
底层数据结构由哈希表和链表组成,哈希表保证元素的唯一性,链表保证元素有序

TreeSet集合

底层数据结构是红黑树(是一个自平衡的二叉树)
第一个元素存储的时候,直接作为根节点存储。从第二个元素开始,每个元素从根节点开始比较。小的往左放,大的往右放

取出元素:(前序遍历,中序遍历,后序遍历) 从根节点开始,按左中右的顺序依次取出

保证元素的排序方式
a:自然排序(元素具备比较性)
比较依赖于元素的compareTo方法,这个方法定义在Comparable接口里,所以要让元素所属的类实现Comparable<>接口,这个接口表示的就是自然排序,可通过重写该方法改变排序方式

b:比较器排序(集合具备比较性)
让TreeSet集合的构造方法接收Comparator<>的实现类对象(只用一次的话就直接匿名内部类),不需要让元素所属类实现接口

TressSet<Student> ts = new TreeSet<>(new Comparator<Student>(){    public int compare(Student stu1,Student stu2){     //重写compare方法    }});

保证唯一性的原理:根据比较的返回是否为0来决定

排序原理:
A自然排序:让元素所述的类实现自然排序接口Comparable
B比较器排序:让集合的构造方法接收一个比较器接口的子类对象Comparator

Collection应用

    不重复:Set        排序用TreeSet 不排序用HashSet  一般用HashSet    重复:List        需要安全用Vector,不需要安全用ArrayList或者LinkedList,一般用ArrayList            查询多:ArrayList             增删多:LinkedList  Collection里一般优先用ArrayList。集合中常见的数据结构:ArrayXxx:底层数据结构是数组,查询快,增删慢LinkedXxx:底层数据结构是链表,查询慢,增删快HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序
0 0