-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
黑马程序员——33集合
一:概述----》
集合:是一种长度可变的,可以存放不同类型的对象的容器。集合不断往上提取共性部分就会形成集合体系。之所以会有这么多容器,是因为其底层的存放数据的方式(数据结构)不一样。把对象添加进集合中,实际上并不是真的把整个对象放进集合里面,而是对象的地址值放进集合中,那么集合就可以访问对象了。
数组也是一种容器,但是只能够装入基本数据类型,而且长度也是固定好的,另外如果直接用System.out.println()打印的话只会打印出数组对应的哈希值。而集合则是可以直接用System.out.println()打印,打印出来的是用[ ]括起来的元素,元素与元素之间用逗号隔开。
集合一般都有泛型限定,如果编写代码的时候不写,到了编译时候就会出现注意提示,这并不是出错了,这一点以后会有所解释。
集合一般放在java.util包中,编写程序的时候记得要导包。
该体系构成大致是这样的:
图1
先说一下该体系中共有的常用方法,也就是最上层的Collection里面的方法(API相关资料):
boolean add(E e) //该集合末尾添加元素
boolean addAll(Collection<? extends E> c) //将指定集合的所有元素添加到该集合中
void clear() //清除该集合中所有的元素
boolean contains(Object o) //检测该集合中是否含有指定元素
boolean containsAll(Collection<?> c) //检测该集合中是否含有指定集合的所有元素
boolean equals(Object o) //比较该集合与指定对象是否相等
int hashCode() //返回该集合的哈希值
boolean isEmpty() //判断该集合是否为空
Iterator<E> iterator() //返回该集合内部的迭代器//特别注意:所谓的迭代器就是封装在集合内部的一种取出元素的方式
boolean remove(Object o) //移除该集合中的某个元素
boolean removeAll(Collection<?> c) //移除该集合所有与指定集合的交集部分元素
boolean retainAll (Collection<?> c) //仅仅保留该集合中与指定集合有交集的部分元素
int size() //返回该集合的元素个数
Object[] toArray() //返回装有该集合元素的Object类型数组
<T>T[] toArray(T[] a) //返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。假定 x 是只包含字符串的一个已知 collection。以下代码用来将 collection 转储到一个新分配的 String 数组:String[] y = x.toArray(new String[0]);
关于迭代器的使用例题
import java.util.*;
class Jihe3
{
public static void main(String[] args)
{
ArrayList a=new ArrayList();
//ArrayList是Collection接口的子类
a.add("jdk01");
a.add("jdk02");
a.add("jdk03");
a.add("jdk04");
a.add("jdk05");
Iterator it=a.iterator();//建立迭代器(取出方式的对象)
while(it.hasNext())//判断是否还有下一个元素
{
Object obj=it.next();//next返回的是Object类的对象,如果有需要,请注意类型转换!
/*
soc(it.next()+”---”+it.next());
//在同一个作用域内连续调用多个next方法,如该行,第一个next方法返回第一个元素后,第二个next方法返回第二个元素,两个返回元素不一样的!这种情况就如同指针功能。而且在这种情况中,元素个数若是奇数容易出问题。
*/
soc(obj);
}
soc(a);//把集合直接打印
}
public static void soc(Object obj)//打印方法
{
System.out.println(obj);
}
}
/*
以上代码编译运行结果:
jdk01
jdk02
jdk03
jdk04
jdk05
[jdk01, jdk02, jdk03, jdk04, jdk05]
*/
Collection下面有两个常用的子接口:List和Set
List中的元素有序排列,元素可以重复,该集合体系有索引,凡是可以操作角标的方法都是该体系的特有方法
Set中的元素无序排列,元素不可以重复,该集合体系没有索引
二:ArrayList----》
ArrayList是数组列表,底层数据结构是是数组结构,查询快,增减慢,线程不同步。默认长度10,只要元素超出了长度就延长百分之50,直到能够足够装下所有元素。
常见构造方法:
public ArrayList() //构造一个初始容量为 10 的空列表。
public ArrayList(Collection<?extendsE> c)//构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
public ArrayList(intinitialCapacity)//构造一个具有指定初始容量的空列表。
下面用例子说明方法的使用(已经给出两个ArrayList集合a和b,而sop是打印方法):
ArrayList a=new ArrayList();
a.add("jdk00");
a.add("jdk01");
a.add("jdk02");
a.add("jdk03");
a.add("jdk04");
a.add("jdk05");
ArrayList b=new ArrayList();
b.add("yu00");
b.add("yu01");
b.add("yu02");
b.add("yu03");
b.add("yu04");
b.add("yu05");
添加元素的相关操作:
a.add(2,"吃饭啦");//在第二位添加元素"吃饭啦"
sop("a.add(2,\"吃饭啦\")---"+a);
//a.add(2,"吃饭啦")---[jdk00, jdk01, 吃饭啦,jdk02, jdk03, jdk04, jdk05]
添加另一个集合元素操作:
a.addAll(3,b);//从第3位开始添加集合b的元素
sop("a.addAll(3,b)---"+a);
//a.addAll(3,b)---[jdk00, jdk01, jdk02,yu00, yu01, yu02, yu03, yu04, yu05, jdk03, jdk04, jdk05]
删除元素操作:
a.remove(0);//删除第0位元素
sop("a.remove(0)---"+a);
//a.remove(0)---[jdk01, jdk02, jdk03, jdk04,jdk05]
设置元素操作:
a.set(4,"呵");//设置第4位元素为"呵"
sop("a.set(4,\"呵\")---"+a);
//a.set(4,"呵")---[jdk00, jdk01, jdk02, jdk03, 呵, jdk05]
获取元素操作:
sop("获取集合a的第1位元素="+a.get(1));
//获取集合a的第1位元素=jdk01
//获取操作不会改变原集合元素
获取元素列表:
sop("a.subList(0,4)---"+a.subList(0,4));//获取集合a的0到4位元素
//a.subList(0,4)---[jdk00, jdk01, jdk02, jdk03]
//获取操作不会改变原集合元素
搜索元素位置:
sop(a.indexOf("jdk03"));//3
//indexOf返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1
public static void sop(Object obj)//打印方法
{
System.out.println(obj);
}
举一个例子说明关于listIterator()方法的使用。
还是针对集合a来进行演示:
ArrayList a=new ArrayList();
a.add("hei00");
a.add("hei01");
a.add("hei02");
a.add("hei03");
a.add("hei04");
a.add("hei05");
对此集合进行如下操作:
Iterator it= a.listIterator();
while(it.hasNext())
{
Object obj=it.next();
if(obj.equals("hei04"))
{
//a.add("java");//这里不能用
/*
因为不能够同时用集合方法和迭代器对同一组元素操作,
容易产生并发修改异常
*/
it.remove();//将a中的hei04删除
}
sop("obj---"+obj);
//不管迭代器有没有进行删除操作,obj 的指向都没有改变,所以都会打印出来,这也侧面验证了实际上集合里装的是对象的地址。
//本质上迭代器删除的是储存在容器中的对象地址而已
}
sop("修改后的a---"+a);
/*此部分操作编译运行结果:
obj---hei00
obj---hei01
obj---hei02
obj---hei03
obj---hei04
obj---hei05
修改后的a---[hei00, hei01, hei02, hei03, hei05]
*/
又或者同样针对集合a,调用hasPrevious和hasNext方法观察,:
ListIterator li=a.listIterator();//ListIterator的使用
sop("正向遍历前的li.hasPrevious()---"+li.hasPrevious());
//hasPrevious用来判断有没有前面元素,有的话返回true否则返回false
sop("正向遍历前的li.hasNext()---"+li.hasNext());
//hasNext用来判断有没有后面的元素,有的话返回true否则返回false
while(li.hasNext())
{
Object obj=li.next();
if(obj.equals("hei04"))
{
li.add("老虎");//这个是ListIterator特有方法,这是Iterator所没有的
//"老虎"是添加在hei04的后面的
//li.set("小猫");//在hei04的位置上改成"小猫"
//这个是ListIterator特有方法,这是Iterator所没有的
}
sop("obj---"+obj);
}
sop("修改后的a---"+a);
sop("正向遍历后的li.hasPrevious()---"+li.hasPrevious());
sop("正向遍历后的li.hasNext()---"+li.hasNext());
/*此部分操作编译运行结果:
正向遍历前的li.hasPrevious()---false
正向遍历前的li.hasNext()---true
obj---hei00
obj---hei01
obj---hei02
obj---hei03
obj---hei04
obj---hei05
修改后的a---[hei00, hei01, hei02, hei03, hei04, 老虎, hei05]
正向遍历后的li.hasPrevious()---true
正向遍历后的li.hasNext()---false
*/
三:Vector----》
Vector---底层数据结构数组,功能与ArrayList一样,但是线程同步,因此效率低下,用得少。Vector默认长度10,只要元素超出长度就延长百分之100,直到能够装下所有元素。
一般也是这样建立对象:Vector vec=new Vector();
Vector也有一个获取元素的方式——枚举,用法也和Iterator相似。
Enumeration en=vec.elements();//建立一个枚举对象
While( en.hasMoreElements ) //判断是否还有元素
{
en.nextElement(); //下一个元素
}
四:LinkedList ----》
LinkedList---底层数据结构是链表结构,(像链子一样,后一个勾着前一个元素)增删快,查询慢,线程不同步。
LinkedList也有着一些自己的方法,也用例题演示(sop是打印方法):
针对于以下LinkedList类的变量a进行操作:
LinkedList a=new LinkedList();
a.add("绵羊00");
a.add("绵羊01");
a.add("绵羊02");
a.add("绵羊03");
a.add("绵羊04");
进行如下操作:
addFirst操作:
a.addFirst("斑点");
sop(a);//[斑点, 绵羊00, 绵羊01, 绵羊02, 绵羊03, 绵羊04]
addLast操作:
a.addLast("最后的绵羊");
sop(a);//[绵羊00, 绵羊01, 绵羊02, 绵羊03, 绵羊04, 最后的绵羊]
getFirst与getLast方法获取但是不删除元素,返回的都是获取到的元素:
sop("a.getFirst()---"+a.getFirst());//a.getFirst()---绵羊00
sop("a.getLast()---"+a.getLast());//a.getLast()---绵羊04
sop(a); //[绵羊00, 绵羊01, 绵羊02, 绵羊03, 绵羊04]
removeFirst和removeLast方法返回的都是被增删的元素
sop("a.removeFirst()---"+a.removeFirst());//a.removeFirst()---绵羊00
sop(a);//[绵羊01, 绵羊02, 绵羊03, 绵羊04]
sop("a.removeLast()---"+a.removeLast());//a.removeLast()---绵羊04
sop(a); //[绵羊01, 绵羊02, 绵羊03]
/*
如果容器里面没有元素,就用getFirst,getLast,removeFirst或者removeLast方法操作元素,那么,就会抛出“没有这个元素”的异常NoSuchElementException
*/
为了解决这个问题,jdk1.6版本又推出了几个新的方法:
offerFirst和offerLast方法添加元素
sop("a.offerFirst(\"456\")---"+a.offerFirst("456"));//a.offerFirst("456")---true
sop(a);//[456, 绵羊00, 绵羊01, 绵羊02, 绵羊03, 绵羊04]
sop("a.offerLast(\"456\")---"+a.offerLast("456"));//a.offerLast("456")---true
sop(a);//[456, 绵羊00, 绵羊01, 绵羊02, 绵羊03, 绵羊04, 456]
peekFirst和peekLast方法使用:
//先清空容器
a.clear();
//peekFirst和peekLast方法获取但是不删除元素
//容器中没有元素时候,peekFirst和peekLast方法返回null
sop("a.peekFirst()---"+a.peekFirst());//a.peekFirst()---null
sop("a.peekLast()---"+a.peekLast());//a.peekLast()---null
sop("a---"+a);//a---[]
pollFirst和pollLast方法使用:
//先清空容器
a.clear();
//pollFirst和pollLast方法获取并且删除元素
//容器中没有元素时候,peekFirst和peekLast方法返回null
sop("a.pollFirst()---"+a.pollFirst());//a.pollFirst()---null
sop("a.pollLast()---"+a.pollLast());//a.pollLast()---null
sop("a---"+a);//a---[]
LinkedList的练习:用LinkedList模拟队列先进先出,模拟栈先进后出。
这种题目主要还是思路,使用addFirst,addLast进行添加元素,然后使用removeFirst,removeLast或者pollFirst,pollLast进行删减元素。
值得注意的是,操作元素这些动作需要调用方法,如果方法名起得更加有意义的话就会更加方便编程。例如把队列封装成一个类,把那些方法封装在自定义的方法中,如下:
class Dueilie //队列类
{
private LinkedList a=new LinkedList();//建立一个链表
public void project_Add(Object obj) //对外提供添加元素的方法,这就可以自定义该添加元素方法的名称,比如这个project_Add
{
a.addLast(obj); //把LinkedList的方法封装在里面了
}
}
另外,还有一个关于Collection集合的共有方法contains的使用小知识点:如果需要添加一个元素进集合内,但是不想元素重复,那么就要调用contains方法观察是否含有该元素,该方法会内部调用对象的equals方法比较对象是否一样,所有的类都默认是继承了Object类,所以也继承Object类的equals方法,如果有需要可以复写equals方法。remove方法也是根据同样的原理,调用对象的equals方法比较之后再删减元素。
五:HashSet ----》
Set中的元素无序,元素不可以重复,该集合体系没有索引,,无序意思就是存入和取出顺序不一定一样。
HashSet ----底层数据结构是哈希表,接收到的数据(对象的哈希值)都按照哈希表顺序排列,一个数据按照自己的哈希值进来后,发现如果之前已经有存入的数据,就比较是否为同一个对象,如果不是的话就在该哈希值下面顺延表示有两个对象在同一个哈希值下,否则就踢出去。
对于HashSet集合而言,判断元素是否存在,删除这些操作都依赖于储存对象的hashCode和equals方法。对象的hashCode方法返回哈希值(哈希值是int型的),计算机会根据哈希值给元素分配储存空间,如果哈希值一样,对象的equals方法就派上用处了,用来比较两者是否相等。如果对象的类没有自定义的equals方法或者hashCode方法,默认都是继承了Object类的equals或者hashCode方法。所以,如果有需要可以复写equals或者hashCode方法。
常见的例子就是每new 一个对象,虚拟机一般都会默认分配一个新的哈希值给该对象。如果我们定义了一个带参数(姓名,年龄)的Person类:
Person p1=new Person(“张三”,23);
Person p2=new Person(“张三”,23);
建立Person类的对象p1和p2,那么p1和p2被虚拟机默认分配到了不同的地址,两者都可以被存放到HashSet集合中,同时存在于HashSet集合中!然而,我们实际开发中是认为在这种情况下,p1和p2都是指向同一个人的,如何处理这种情况?
可以在Person类中复写hashCode方法,定义返回的int型哈希值是(姓名的哈希值+年龄), 这样就可以更加保险。
可见,这种复写的操作在实际开发中是多么的重要。
-----------android培训、java培训、java学习型技术博客、期待与您交流!------------