黑马程序员_java_集合总结(上)

来源:互联网 发布:脸部比例测试软件 编辑:程序博客网 时间:2024/05/18 12:30
------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------


集合是学习java语言的一个重点,集合同数组一样,好比是一个容器,数组可以保存多个对象,但在某些情况下无法确定到底需要保存多少个对象,此时数组将不再使用,因为数组长度不可变。而集合这可以存储任意类型的对象,并且长度可变,所以集合运用比较广泛,也尤为重要。


一、Collection(单列集合)


定义:Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。


单例集合体系图  (顶层)Collection--List        --ArrayList   --Vector
       --LinkedList


 --Set         --HashSet
       --TreeSet 


Collection的功能:
(1)添加
boolean add(Objcect obj):向集合中添加元素
boolean addAll(Collection c):向集合中添加一个集合的元素
(2)删除
void clear():删除集合中的所有元素
boolean remove(Object obj):删除集合中的指定元素
boolean removeAll(Collection c):删除指定集合中的元素
(3)判断
boolean isEmpt():判断集合是否为空
boolean contains(Object obj):判断集合是否包含指定元素
boolean containsAll(Collection c):判断集合是否包含指定集合中的所有元素
(4)获取
int size():获得集合的元素个数
(5)遍历
Iterator iterator():迭代器.
hasNext():判断是否还有下一个元素
next():获取下一个元素
(6)取交集
boolean retainAll(Collection c):判断集合中是否有相同的元素
(7)转换
Object[ ] toArray():把集合转换成数组


二、Iterator接口
Iterator迭代器的原理
集合的使用步骤:
A:创建集合对象
B:创建元素对象
C:向集合中添加数据add()
D:遍历集合


Iterator迭代器的使用:
1、异常NoSuchElementException:
当获取到了最后一个元素,它又继续获取这样就会报这个错误.


2、 boolean hasNext()
判断迭代器中是否还有元素
(一般用在if或者while的判断条件位置)


     1 通过集合对象获取迭代器对象iterator()
     2 通过hasNext()方法进行判断指针后是否有下一个元素
     3 通过next()方法获取到指针后的下一个元素




每一个集合都有自己的数据结构,都有特定的取出自己内部元素的方式。为了便于操作所有的容器,取出元素。将容器内部的取出方式按照一个统一的规则向外提供,这个规则就是Iterator接口。


pbulic static void main (String args){
Colleation coll = new ArrayList();
coll.add("abc0");
coll.add("abc1");
coll.add("abc2");
//---------方式1----------
Iterator it =coll.Iterator();
While(it.hasNext()){
System.out.println("it.next");
}
//---------方式2----------
for(Iterator it = coll.iterator();it.hasNest){
System.out.println("it.next");
}
}




三、List接口
(1)List特点:
1. 有序(存储和取出的顺序一致)
2. 可以重复
3. 可以通过索引值操作对应位置的元素


(2)功能:
1,添加:
add(index,element) :在指定的索引位插入元素。
addAll(index,collection) :在指定的索引位插入一堆元素。
2,删除:
remove(index) :删除指定索引位的元素。 返回被删的元素。
3,获取:
Object get(index) :通过索引获取指定元素。
int indexOf(obj) :获取指定元素第一次出现的索引位,如果该元素不存在返回-1;
 所以,通过-1,可以判断一个元素是否存在。
int lastIndexOf(Object o) :反向索引指定元素的位置。
List subList(start,end) :获取子列表。
4,修改:
Object set(index,element) :对指定索引位进行元素的修改。
5,获取所有元素:
ListIterator listIterator():list集合特有的迭代器。


6,截取功能
List subList( int fromIndex, int toIndex )
截取集合从指定位置开始到指定位置结束,返回截取出来的集合.


(3)常见问题
并发修改异常的产生及解决方案
1、并发修改异常怎么产生的ConcurrentModificationException
就是在用迭代器对集合进行遍历的同时对集合进行了改变.




(4)ArrayList 集合遍历案例
底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快。


步骤:
1、创建集合对象
2、创建并添加元素
3、遍历集合:
创建迭代器对象
hasNext判断是否有下一个对象
next获取下一个对象
public class ArrayListDemo {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();


// 创建并添加元素
array.add("hello");
array.add("world");
array.add("java");


// 遍历集合
Iterator it = array.iterator();
while (it.hasNext()) {
String s = (String) it.next();
System.out.println(s);
}


System.out.println("-----------");


// for (int x = 0; x < array.size(); x++) {
// String s = (String) array.get(x);
// System.out.println(s);
// }
// 开发中,我会按照如下方式写,请问为什么?
//上面那种方式,每次都会调用size()方法,效率低。
//下面这种方式,我们只调用一次。效率较高。
int length = array.size();
for (int x = 0; x < length; x++) {
String s = (String) array.get(x);
System.out.println(s);
}
}
}


(5)Vector集合
底层的数据结构就是数组,线程同步的,Vector无论查询和增删都巨慢。


/* Vector其实就像是ArrayList的前身
 * Vector的特有功能:
 * A:添加功能
 * public void addElement(Object obj) -- add(Object obj)
 * B:获取功能
 * public Object elementAt(int index)-- get(int index)
 * public Enumeration elements()-- Iterator
 * boolean hasMoreElements()-- hasNext()
 * Object nextElement()--  next()
 * C:长度功能
 * public int size()
 * 
 * JDK版本升级:
 * A:安全
 * B:效率
 * C:简化书写
 */
public class VectorDemo {
public static void main(String[] args) {
// 创建集合对象
Vector v = new Vector();


// 添加元素
v.addElement("hello");
v.addElement("world");
v.addElement("java");


// 遍历
// System.out.println(v.elementAt(0));
// System.out.println(v.elementAt(1));
// System.out.println(v.elementAt(2));
for (int x = 0; x < v.size(); x++) {
String s = (String) v.elementAt(x);
System.out.println(s);
}

System.out.println("----------------");

//public Enumeration elements()
Enumeration en = v.elements();
while(en.hasMoreElements()){
String s = (String)en.nextElement();
System.out.println(s);
}
}
}




(6)LinkedList集合
底层的数据结构是链表,线程不同步,增删元素的速度非常快。


public class LinkedListTest {
public static void main(String[] args) {
// LinkedList link = new LinkedList();
// link.addFirst("hello");
// link.addFirst("world");
// link.addFirst("java");
// Iterator it = link.iterator();
// while (it.hasNext()) {
// String s = (String) it.next();
// System.out.println(s);
// }


// 创建集合对象
MyStack ms = new MyStack();


// 创建并添加元素
ms.add("hello");
ms.add("world");
ms.add("java");


// 获取
// System.out.println(ms.get(0));
// System.out.println(ms.get(1));
// System.out.println(ms.get(2));
// System.out.println(ms.get(3));


for (int x = 0; x < ms.size(); x++) {
String s = (String) ms.get(x);
System.out.println(s);
}
}
}
输出结果是:
removeFirst:hello
removeLast:java
link:[world]




四、泛型
泛型:jdk1.5版本以后出现的一个安全机制。表现格式:< >


好处:
1:将运行时期的问题ClassCastException问题转换成了编译失败,体现在编译时期,程序员就可以解决问题。
2:避免了强制转换的麻烦。


只要带有<>的类或者接口,都属于带有类型参数的类或者接口,在使用这些类或者接口时,必须给<>中传递一个具体的引用数据类型。


泛型技术:其实应用在编译时期,是给编译器使用的技术,到了运行时期,泛型就不存在了。
为什么? 因为泛型的擦除:也就是说,编辑器检查了泛型的类型正确后,在生成的类文件中是没有泛型的。
在运行时,如何知道获取的元素类型而不用强转呢?
泛型的补偿:因为存储的时候,类型已经确定了是同一个类型的元素,所以在运行时,只要获取到该元素的类型,在内部进行一次转换即可,所以使用者不用再做转换动作了。


什么时候用泛型类呢?
当类中的操作的引用数据类型不确定的时候,以前用的Object来进行扩展的,现在可以用泛型来表示。这样可以避免强转的麻烦,而且将运行问题转移到的编译时期。






泛型在程序定义上的体现:
//泛型类:将泛型定义在类上。
class Tool<Q> {
private Q obj;
public  void setObject(Q obj) {
this.obj = obj;
}
public Q getObject() {
return obj;
}
}
//当方法操作的引用数据类型不确定的时候,可以将泛型定义在方法上。
public <W> void method(W w) {
System.out.println("method:"+w);
}
//静态方法上的泛型:静态方法无法访问类上定义的泛型。如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
public static <Q> void function(Q t) {
System.out.println("function:"+t);
}
//泛型接口.
interface Inter<T> {
void show(T t);
}
class InterImpl<R> implements Inter<R> {
public void show(R r) {
System.out.println("show:"+r);
}
}




泛型中的通配符:可以解决当具体类型不确定的时候,这个通配符就是 ?  ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。


泛型限定:
上限:?extends E:可以接收E类型或者E的子类型对象。
下限:?super E:可以接收E类型或者E的父类型对象。


上限什么时候用:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。为什么?因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象。


下限什么时候用:当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收。


泛型的细节:
1)、泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型;
2)、使用带泛型的类创建对象时,等式两边指定的泛型必须一致;
原因:编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了;
3)、等式两边可以在任意一边使用泛型,在另一边不使用(考虑向后兼容);
ArrayList<String> al = new ArrayList<Object>();  //错
//要保证左右两边的泛型具体类型一致就可以了,这样不容易出错。
ArrayList<? extends Object> al = new ArrayList<String>();
al.add("aa");  //错
//因为集合具体对象中既可存储String,也可以存储Object的其他子类,所以添加具体的类型对象不合适,类型检查会出现安全问题。 ?extends Object 代表Object的子类型不确定,怎么能添加具体类型的对象呢?
public static void method(ArrayList<? extends Object> al) {
al.add("abc");  //错
//只能对al集合中的元素调用Object类中的方法,具体子类型的方法都不能用,因为子类型不确定。
}
zxf
0 0
原创粉丝点击