导读:集合框架(体系概述,共性方法,迭代器,List集合、ListIterator、List集合具体对象的特点,Vector中的枚举,LinkedList,LinkedList练习,ArrayList练习,HashSet,HashSet自定义对象、判断和删除的依据)
1、集合框架(体系概述)
l 数据多了要将其封装成对象。对象多了也要进行存储,存储在哪?有二个地方:一、数组。二、集合。有数组了为什么还要集合呢?你能确定员工的人数吗?数组是固定长度的,集合是可变长度的。数组中存储对象的时候只能存同一种对象。如,全存Demo,或者全存Person。数组在在的时候已经指明其中的类型了。集合只要是对象就行。集合的应用领域更广。
l 为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
l 数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
l 集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
l Java的工程师们对这些能存储对象的容器进行了不同的化分和描述,向上抽形成体系。这个体系叫做集合框架。框架产生后我们一般会先看谁?先看顶层的,顶层中定义的是体系中最共性,最基本的行为。看完顶层后,找下面具体的实现来用。因为原因有两个,一、顶层可能不能创建对象(抽象)。子类对象方法更多一些。
l 集合构架是工具包(Java.util)中的一个成员。
l Collection:add、remove、contains、clear、iterator
l 为什么会出现这么多的容器呢?
因为每人个容器对于数据的存储方式都有不同。这个存储方式称之为数据结构。
2、集合框架(共性方法)
Ø add():对于不同的Collection子类都有不同的实现
Ø boolean addAll():一次添加多个元素、一堆元素(传入一个容器)
Ø void clear() 清空容器,容器中所有元素都清掉
Ø boolean contains(Object o) 包含某个元素
Ø boolean containsAll(Collection<?> c) 包含指定 collection 中的所有元素,则返回true。
Ø boolean equals(Object o):比较此 collection 与指定对象是否相等。
Ø int hashCode():算哈希值的。
Ø boolean isEmpty():集合中是否有元素
Ø Iterator<E> iterator():取数据
Ø boolean remove(Object o):移除一个元素
Ø boolean removeAll(Collection<?> c):移除一堆(不是全部),将传进来的容器中一样的元素删除。
Ø boolean retainAll(Collection<?> c):取交集
Ø int size():size为0时为空
Ø Object[] toArray():集合转化为数组
Ø toArray(T[] a):返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。
l 对于于取出不止有一个动作(多个功能来体现)的话,他就把取出这个动作封装成一个对象。而不同的数据结构他取出的方式不一样。对过一个类来描述取出的动作,而这个类就定义在了集合的内部。因为元素在集合当中,你想直接操作元素,定义内部类最方便。每一个容器中都有内部类。以后再出现容器,这些容器中还定义的有这些东西。这些东西一般会有一个共性,在取出元素的时候一般都会做的动作就是:一、有没有元素。二、有。(取)。怎么判断,怎么取,取决于每个容器内部的数据结构。
l 把取出方式定义在集合的内部,这样就可以直接访问集合内容的元素。那么取出方式就被定义成了内部类。而每一个容器的数据结构不同,所以取出的动作细节也不一样。但是都有共性内容判断和取出。那么可以将这些共性抽取。这些都是扩展功能,形成的接口,起一个名字叫Iterator。
l 那么这些内部类都符合一个规则。该规则是Iterator。如何获取集合的取出对象呢?通过一个对于外提供的方法。Iterator(); 再出现一个集合的话,也实现这年接口就可以了。这样就统一了取出方式。
l Collection定义了集合框架的共性功能。
1,添加
add(e);
addAll(collection);
2,删除
remove(e);
removeAll(collection);
clear();
3,判断。
contains(e);
isEmpty();
4,获取
iterator();
size();
5,获取交集。
retainAll();
6,集合变数组。
toArray();
l add方法的参数类型是Object。以便于接收任意类型对象。
l 集合中存储的都是对象的引用(地址)
l 在集合中存放的不可能是对象实体,为什么呢?一、这样集合太大了。二、这样那好的集合就要移位置。集合和数组都一样,存的都是地址。
l 什么是迭代器呢?
其实就是集合的取出元素的方式。如同抓娃娃游戏机中的夹子。(夹子就是迭代器,这个容器对外暴露了功能,功能就是操纵杆。通过操纵杆我就能操纵这个对象。里面的夹子是什么样的不重要,重要的是你只要操纵操纵杆就可以了,将操纵杆和里面的夹子联系的紧密性都降低了)
l 什么叫内部事物?
容器是一个事物,容器的内部还有一个事物,这个事物定义成内部类,而且这个内部事物,可以直接访问容器中的成员。这就是内部类的设计。
l 迭代器是取出方式,会直接访问集合中的元素。
所以将迭代器通过内部类的形式来进行描述。
通过容器的iterator()方法获取该内部类的对象。
每个容器中都有存和取的方式,而且因为每一个容器的数据结构不一样。所以它们存取的动作实现的方式也不一样。
l import java.util.*;
class CollectionDemo
{
publicstatic void main(String[] args)
{
method_get();
}
publicstatic void method_get()
{
ArrayListal = new ArrayList();
//1,添加元素。
al.add("java01");//add(Object obj);
al.add("java02");
al.add("java03");
al.add("java04");
/*
Iteratorit = al.iterator();//获取迭代器,用于取出集合中的元素。Iterator返回的是Iterator接口的一个子类对象。接口型引用只能指向自己的子类对象。而这个方法不是直接new出来的而是通过集合中的方法获取出来的。
while(it.hasNext()) //和sop(a1)打印有区别,它每打印一都可以对它判断一次,可以不打印java02。用while来写是OK的,但循环结束后,it对象还在。如果用for的话,循环结束的话,it就释放了。
{
sop(it.next());
}
*/
for(Iteratorit = al.iterator(); it.hasNext() ; ) //老外用for来写。
{
sop(it.next());
}
}
publicstatic void method_2()
{
ArrayListal1 = new ArrayList();
al1.add("java01");
al1.add("java02");
al1.add("java03");
al1.add("java04");
ArrayListal2 = new ArrayList();
al2.add("java03");
al2.add("java04");
al2.add("java05");
al2.add("java06");
//al1.retainAll(al2);//取交集,al1中只会保留和al2中相同的元素。(对于al1集的内容进行了改变),al1输出[java03,java04]
al1.removeAll(al2); //求余集,输出[java01,java02]
sop("al1:"+al1);
sop("al2:"+al2);
}
publicstatic void base_method()
{
//创建一个集合容器。使用Collection接口的子类。ArrayList
ArrayListal = new ArrayList();
//1,添加元素。
al.add("java01");//在这里是Object接收的。Add中接收的对象 是E类型的,用多态,接收Object的也可以。add(Object obj);
al.add("java02");
al.add("java03");
al.add("java04");
//打印原集合。
sop("原集合:"+al);//将对象封装到一个[]中输出出来,看着方便。
//3,删除元素。
//al.remove("java02");
//al.clear();//清空集合。
//4,判断元素。
sop("java03是否存在:"+al.contains("java03")); //返回值为true
sop("集合是否为空?"+al.isEmpty()); //返回值为false
//2,获取个数。集合长度。
sop("size:"+al.size());
//打印改变后的集合。
sop(al);
}
publicstatic void sop(Object obj)
{
System.out.println(obj);
}
}
3、集合框架(迭代器)(见上面代码)
l 我们的目的是取出元素,并操作元素,不一写要打印的。在开发中把这个元素取出来,要么是判断这个元素,要么是对这个元素进行其他功能的调用。而不仅仅是打印一下。
l Iterator<E> iterator()方法返值类型是Iterator。Iterator是java.util中的接口,这个接口中有三个方法,hasNext(),next(),remove()
4、集合框架(List集合、ListIterator、List集合具体对象的特点)
(和Collection共性的不再重复,只总结List所特有的方法。)
l Collection
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。(可变长度的数组,默认为10,当添加的长度大于10的时候,它会再给我你new一个数组,长度为多少呢?50%延长。把原来的数据Copy到数组中来,再把新元素添加到后面去)
|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。(Vector和ArrayList重复了,Vector是元老级干部,有Vector的时候,集合框架还不在呢)区别:一、Vector是同步的,ArrayList是不同步的。二、当长度超过10的时候,Vector是100%延长,ArrayList50%延长。ArrayList好一点,因为他即能延长,又能节省空间。
|--Set:元素是无序,元素不可以重复。
l List:
特有方法。凡是可以操作角标的方法都是该体系特有的方法。
增
add(index,element);在指定位置插入指定元素
addAll(index,Collection);
删
remove(index);按角标位置移除
改
set(index,element);
查
get(index):某位置获取某元素
subList(from,to);包含头不包含尾
listIterator();
intindexOf(obj):获取指定元素的位置。
ListIteratorlistIterator();
l List集合特有的迭代器。ListIterator是Iterator的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。该接口只能通过List集合的listIterator方法获取。
l import java.util.*;
class ListDemo
{
publicstatic void sop(Object obj)
{
System.out.println(obj);
}
publicstatic void method()
{
ArrayListal = new ArrayList();
al.add("java01"); //添加元素
al.add("java02");
al.add("java03");
sop("原集合是:"+al);
al.add(1,"java09"); //在指定位置添加元素。
//al.remove(2); //删除指定位置的元素。
//al.set(2,"java007"); //修改元素。
sop("get(1):"+al.get(1)); //通过角标获取元素。
sop(al);
//获取所有元素。
for(intx=0; x<al.size(); x++) //但凡操作角标的都是数组原理特殊的取数方式,遍历。
{
System.out.println("al("+x+")="+al.get(x));
}
Iteratorit = al.iterator(); //遍历获取所有元素
while(it.hasNext()) //为什么是hasNext呢,因为指针在开始的时候是在无不的前面。
{
sop("next:"+it.next());
}
//通过indexOf获取对象的位置。
sop("index="+al.indexOf("java02"));
Listsub = al.subList(1,3); //子列表返回的还是List含1不含3
sop("sub="+sub);
}
publicstatic void main(String[] args)
{
//演示列表迭代器。
ArrayListal = new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
sop(al);
ListIteratorli = al.listIterator(); //ListIterator继承自Iterator。只有ListIterator才能增删改查,因为它还有角标。
//sop("hasPrevious():"+li.hasPrevious()); //判断迭代前面有没有元素。
while(li.hasNext())
{
Objectobj = li.next();
if(obj.equals("java02"))
//li.add("java009"); //在java02的后面添加了java009
li.set("java006"); //把java02的内容改为java009
}
while(li.hasPrevious()) //逆向遍历
{
sop("pre::"+li.previous());
}
//sop("hasNext():"+li.hasNext());
//sop("hasPrevious():"+li.hasPrevious());
sop(al); //打印迭代器操作之前。结果为[java01,java02,java03]
/*
建立迭代后,操作元素的方法有两种,一种可以用集合的方式,一种可以用迭代器的方式操作元素。可是这两种方式操作的是同一种元素,当用迭代取出数据的过程中,你又用到集合的功能操作这个元素的话,就有可能会出现操作隐患。这叫做并发隐患。你不能做的是对于同一组元素进行多种同时的操作。在这里会出现java.util中的ConcurrentModificationException。怎么解决?要么用集合的方法,要么用迭代器的方法。
//在迭代过程中,准备添加或者删除元素。
Iteratorit = al.iterator(); //在这里iterator只知道这里添加了三个元素,以后操作(增、删),它都不知道。
while(it.hasNext())//迭代器,在这里只能做三个动作,判断,取出和移除,其他的动作(添加),他做不了。
{
Objectobj = it.next();
if(obj.equals("java02"))
//al.add("java008");
it.remove();//将java02的引用从集合中删除了。 sop("obj="+obj);//java02的引用移除了,但是元素还被obj使用,所以能移除了后还能打印出来
}
sop(al); //打印迭代器操作之后。[java01,java03]
*/
}
}
5、集合框架(Vector中的枚举)
l Vector中带有Element的都是他所特有的。
l 枚举(Enumeration)就是Vector特有的取出方式(早期1.0的时候就只有这种方式)(Vector共有三种取出方式,Iterator,遍历[for循环],枚举)。发现枚举和迭代器很像。其实枚举和迭代是一样的。因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。枚举郁郁而终了(I/O中有一个对象用到枚举了,那个对象也出现在1.0时期,所以要介绍它)。
l import java.util.*;
class VectorDemo
{
publicstatic void main(String[] args)
{
Vectorv = new Vector();
v.add("java01");
v.add("java02");
v.add("java03");
v.add("java04");
Enumerationen = v.elements();
while(en.hasMoreElements())
{
System.out.println(en.nextElement());
}
}
}
6、集合框架(LinkedList)
l LinkedList:特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException
在JDK1.6出现了替代方法。(remove变poll,get变peek,add变offer)
offerFirst();
offerLast();
peekFirst();//后期LinkList升级了,没有元素的时候不抛出异常了,而是返回空。
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。
l import java.util.*;
class LinkedListDemo
{
publicstatic void main(String[] args)
{
LinkedListlink = new LinkedList();
link.addLast("java01");
link.addLast("java02");
link.addLast("java03");
link.addLast("java04");
//sop(link);
//sop(link.getFirst());
//sop(link.getFirst());
//sop(link.getLast());
//sop(link.removeFirst());
//sop(link.removeFirst());
//sop("size="+link.size());
while(!link.isEmpty()) //不用迭代器,获取元素的方法。
{
sop(link.removeLast());
}
}
publicstatic void sop(Object obj)
{
System.out.println(obj);
}
}
/*
---
java01//用addLast(“”);向尾部添加元素,输出后会先输出java01
java02
java03
java04
----
java04 //用addFirst(“”);向头部添加元素,输出后会先输出java04
java03
java02
java01
---
*/
l 现在有两个容器可以用了,开发时到底用哪一个?看需求
如果涉及的元素比较多,有比较频繁的增删操作的话,用LinkList
如果涉及的元素不多,也没有比较频繁的增删操作的话,用可以选择用ArrayList也可以选择用LinkList。
注意:涉及到了增删,也涉及到了改查,用哪个?建议使用ArrayList.其实频繁的增删操作实际并不多见。一般的情况都是查询比较多。当你实在选不出来用谁了,就用ArrayList。只要元素不是特别多,ArrayList也是允许的。
7、LinkedList练习(必须会)
/*
使用LinkedList模拟一个堆栈或者队列数据结构。
堆栈:先进后出 如同一个杯子。
队列:先进先出 First in First out FIFO 如同一个水管。
*/
import java.util.*;
class DuiLie
{
privateLinkedList link;
DuiLie()
{
link= new LinkedList();
}
publicvoid myAdd(Object obj)
{
link.addFirst(obj);
}
publicObject myGet()
{
returnlink.removeFirst();
}
publicboolean isNull()
{
returnlink.isEmpty();
}
}
class LinkedListTest
{
publicstatic void main(String[] args)
{
DuiLiedl = new DuiLie();
dl.myAdd("java01");
dl.myAdd("java02");
dl.myAdd("java03");
dl.myAdd("java04");
while(!dl.isNull())
{
System.out.println(dl.myGet());
}
}
}
l 我们想做成一些和我们项目相关的一些容器,我们要起一些特定的名称来用会更方便一些,我们会把它原有的接口,封装进我们的描述当中,对外提供一个自己更能识别的名字。这种开发方式很多见。
8、ArrayList练习一
//去除ArrayList集合中的重复元素。
import java.util.*;
class ArrayListTest
{
publicstatic void sop(Object obj)
{
System.out.println(obj);
}
publicstatic void main(String[] args)
{
ArrayListal = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java01");
al.add("java02");
al.add("java01");
// al.add("java03");
/*
在迭代时循环中next调用一次,就要hasNext判断一次。
Iteratorit = al.iterator();
while(it.hasNext())
{
sop(it.next()+"...."+it.next());
}
*/
sop(al);
al= singleElement(al);
sop(al);
}
publicstatic ArrayList singleElement(ArrayList al)
{
//定义一个临时容器。
ArrayListnewAl = new ArrayList();
Iteratorit = al.iterator();
while(it.hasNext())
{
Objectobj = it.next();
if(!newAl.contains(obj))
newAl.add(obj);
}
returnnewAl;
}
}
9、ArrayList练习2
/*
将自定义对象作为元素存到ArrayList集合中,并去除重复元素。
比如:存人对象。同姓名同年龄,视为同一个人。为重复元素。
思路:
1,对人描述,将数据封装进人对象。
2,定义容器,将人存入。
3,取出。
List集合判断元素是否相同,依据是元素的equals方法。
*/
import java.util.*;
class Person
{
privateString name;
privateint age;
Person(Stringname,int age)
{
this.name= name;
this.age= age;
}
publicboolean equals(Object obj) //重写父类中的equals方法。
{
if(!(objinstanceof Person))
returnfalse;
Personp = (Person)obj;
//System.out.println(this.name+"....."+p.name);
returnthis.name.equals(p.name) && this.age == p.age; //这里的equals是字符串中的equals
}
publicString getName()
{
returnname;
}
publicint getAge()
{
returnage;
}
}
class ArrayListTest2
{
publicstatic void sop(Object obj)
{
System.out.println(obj);
}
publicstatic void main(String[] args)
{
ArrayListal = new ArrayList();
al.add(newDemo());
al.add(newPerson("lisi01",30));//al.add(Object obj);//Object obj = newPerson("lisi01",30);
//al.add(newPerson("lisi02",32));
al.add(newPerson("lisi02",32));
al.add(newPerson("lisi04",35));
al.add(newPerson("lisi03",33));
//al.add(newPerson("lisi04",35));
//al= singleElement(al); //它判断的是ArrayList中存的对象是否相同,而不是比较对象的姓名和年龄。
sop("remove03 :"+al.remove(new Person("lisi03",33)));//remove方法底层也是依赖于元素的equals方法。
Iteratorit = al.iterator();
while(it.hasNext())
{
Personp = (Person)it.next();
sop(p.getName()+"::"+p.getAge());
}
}
publicstatic ArrayList singleElement(ArrayList al)
{
//定义一个临时容器。
ArrayListnewAl = new ArrayList();
Iteratorit = al.iterator();
while(it.hasNext())
{
Objectobj = it.next();
if(!newAl.contains(obj))//当 o==null ? e==null : o.equals(e)) 底层用的是equals()方法。
newAl.add(obj);
}
returnnewAl;
}
}
数据结构不同,它依赖的方法是不一样的ArrayList和LinkList中的remove和contains依靠的都是equals。
10、集合框架(HashSet)
(重点关注的都在子类对象上呢)
l |--Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。、
|--HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。
注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。(ArrayList只依赖equals)
|--TreeSet:
l Set集合的功能和Collection是一致的。
import java.util.*;
class HashSetDemo
{
publicstatic void sop(Object obj)
{
System.out.println(obj);
}
publicstatic void main(String[] args)
{
HashSeths = new HashSet();
sop(hs.add("java01")); //返回的是boolean型的值。结果:true
sop(hs.add("java01")); //地址相同,内容也相同,不存了。结果:false
hs.add("java02");
hs.add("java03");
hs.add("java03");
hs.add("java04");
Iteratorit = hs.iterator();
while(it.hasNext())
{
sop(it.next());
}
}
}
l 有一个集合,这个集合中是什么结构呢?哈希表(存放了一堆哈希值的表)存的时候存的顺序不是按你存的顺序来定义的。这个顺序是按照哈希值来存的。取的时候是按照表里面的顺序取。
l 在哈希表里面,当哈希值重复的时候,它还有一次校验方式。如果咱俩地址值不一样,你有你的位置,我有我的位置,咱俩不是一个元素。如果咱俩位置一样的话,还要再做一次判断,咱俩元素对象是否相同(equals)。如果不是同一个对象,它会在同一个位置顺延,两个对象都在同一个地址值上。(先看哈希值,如果一样,不用比较是否是同一个对象,这个表完全是以哈希值为主的)
11、HashSet存储自定义对象、判断和删除的依据
l 当我们在自定义对象的时候,通常要复写函数的equals,因为有可能这个对象会存到HashSet集合当中,你不复写的话,也有equals但是这个值都是根据地址来判断的,地址都不一样,所以不行。所以在开发的时候,你只的描述这个事物要往集合中去存的时候,这样的对象一般都会复写hashCode()和equals()方法。这个hashCode不是我们调用的,是集合底层内部调用的。
/*
往hashSet集合中存入自定对象
姓名和年龄相同为同一个人,重复元素。
*/
import java.util.*;
class HashSetTest
{
publicstatic void sop(Object obj)
{
System.out.println(obj);
}
publicstatic void main(String[] args)
{
HashSeths = new HashSet();
hs.add(newPerson("a1",11));
hs.add(newPerson("a2",12));
hs.add(newPerson("a3",13));
//hs.add(newPerson("a2",12));
//hs.add(newPerson("a4",14));
//sop("a1:"+hs.contains(newPerson("a2",12))); //判断是否有Person("a2",12),先比较哈希值是否相同。
//hs.remove(newPerson("a4",13)); //判断是否有Person("a2",12),先比较哈希值是否相同。
Iteratorit = hs.iterator();
while(it.hasNext())
{
Personp = (Person)it.next();
sop(p.getName()+"::"+p.getAge());
}
}
}
class Person
{
privateString name;
privateint age;
Person(Stringname,int age)
{
this.name= name;
this.age= age;
}
publicint hashCode() //如果存进去两个一样的值,说明一定是hashCode或者equals出问题了。
{
System.out.println(this.name+"....hashCode");
returnname.hashCode()+age*37; //name为字符串,字符串本身有自己的hashCode()方法。*37是为了尽量保持hash值唯一。如,张三hash值为20,年龄40,李四年龄40,哈希值20,哈希值计算的就相等了。返回值是int就可以了。
}
publicboolean equals(Object obj) //写成Person编译通过但是没有复写。
{
if(!(objinstanceof Person))
returnfalse;
Personp = (Person)obj;
System.out.println(this.name+"...equals.."+p.name);
returnthis.name.equals(p.name) && this.age == p.age;
}
publicString getName()
{
returnname;
}
publicint getAge()
{
returnage;
}
}
------- android培训、java培训、期待与您交流!----------