【黑马程序员】集合框架(上)——Java复习笔记
2015-11-11 16:57
555 查看
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
案例1:去除集合中的重复元素
方法1:创建新集合,遍历旧集合每个元素,拿到新集合去找,有则不管,没有就加进去
方法2:就在一个集合中操作,应用选择排序思想,第一个跟后面的比,第二个跟后面的比……
a:添加
public void addElement(E obj) – add()
b:获取
public E elementAt(int index) – get()
public Enumeration elements() – iterator()
a:添加
addFirst() 在开头添加
addLast()
b:删除
removeFirst() 删除并返回第一个元素
removeLast()
c:获取
getFirst() 获取第一个元素
getLast()
练习:用LinkedList模拟栈数据结构的集合并测试
泛型一般应用于集合
注意:该数据类型只能是引用类型。
JDK新特性:泛型推断 后面的<>可以不写,推荐还是写上
ArrayList array = new ArrayList<>;//注意,=号左右的<>必须一致
B:避免了强制类型转换(Iterator加上泛型,遍历的时候就不用再强转了)
C:优化了程序设计,解决了黄色警告线问题,让程序更安全
情况1:实现类已知是什么类型,直接在implements 接口名后面定义<>,比如,则创建对象的时候只能传入String
情况2:不知道是什么类型,则要在实现类类名和implements 接口名后面都定义,写方法传入参数就是(T t),可接收任意类型
? extends E 向下限定,E类及其子类
? super E 向上限定,E类及其父类
代码演示:
格式:
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)
注意:转换后的集合长度不能改变(不能增删)。
HashSet不保证set的迭代顺序,特别是不保证该顺序恒久不变
B:HashSet的add方法底层依赖两个方法:hashCode()和equals()
执行顺序:
首先比较哈希值是否相同
相同:继续执行equals()方法 || 比较地址值
返回true:元素重复了,不添加
返回false:直接把元素添加到集合
不同:就直接把元素添加到集合
C:如何保证元素唯一性的呢?
由hashCode()和equals()保证的。如果是自定义对象,要重写equals和hashCode方法,自动生成即可,见上图
D:哈希表是一个元素为链表的数组,综合了数组和链表的优点
E:HashSet子类LinkedHashSet(该集合是有序的):
底层数据结构由哈希表和链表组成,哈希表保证元素的唯一性,链表保证元素有序
第一个元素存储的时候,直接作为根节点存储。从第二个元素开始,每个元素从根节点开始比较。小的往左放,大的往右放
取出元素:(前序遍历,中序遍历,后序遍历) 从根节点开始,按左中右的顺序依次取出
保证元素的排序方式
a:自然排序(元素具备比较性)
比较依赖于元素的compareTo方法,这个方法定义在Comparable接口里,所以要让元素所属的类实现Comparable<>接口,这个接口表示的就是自然排序,可通过重写该方法改变排序方式
b:比较器排序(集合具备比较性)
让TreeSet集合的构造方法接收Comparator<>的实现类对象(只用一次的话就直接匿名内部类),不需要让元素所属类实现接口
保证唯一性的原理:根据比较的返回是否为0来决定
排序原理:
A自然排序:让元素所述的类实现自然排序接口Comparable
B比较器排序:让集合的构造方法接收一个比较器接口的子类对象Comparator
集合
(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:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统