集合类
一、集合类

答:面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储。集合就是存储对象最常用的一种方式。

答:数组当然也可以存储对象,但长度是固定的。集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
集合的特点:集合用于存储对象,长度是可变的,可以存储不同类型的对象。
集合框架:

答:因为每个容器对数据的存储方式都不同。这个存储方式称为——数据结构。
例:
import java.util.*;
class Demo
{
public static void main(String args[]){
//创建一个集合容器,使用Collection的子类ArrayList
ArrayList al = new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//获取集合长度
System.out.println(al.size());
}
}
注:出现了安全隐患提示,但是不影响运行。
集合的存储:
例:
import java.util.*;
class Person
{
private String name;
private int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println("name = "+name+",age = "+age);
}
}
class Demo{
public static void main(String []args){
Person p = new Person("zhangsan",20);
ArrayList al = new ArrayList();
al.add(p);
}
}
分析:图解
分析:
1.add方法的参数类型是Object,以便于接收任意类型对象。
2.集合中存储的都是对象的引用(地址)和数组一样。
删除元素:
例:
import java.util.*;
class Demo
{
public static void main(String args[]){
//创建一个集合容器,使用Collection的子类ArrayList
ArrayList al = new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//删除前打印
System.out.println("删除前:"+al);
//删除元素
al.remove("java02");
//al.clear();
System.out.println("删除后:"+al);
}
}
判断元素:
例:
import java.util.*;
class Demo
{
public static void main(String args[]){
//创建一个集合容器,使用Collection的子类ArrayList
ArrayList al = new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//判断
System.out.println("java03是否存在:"+al.contains("java03"));
System.out.println("集合是否为空:"+al.isEmpty());
}
}
取交集:
例:
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al1 = new ArrayList();
al1.add("java01");
al1.add("java02");
al1.add("java03");
al1.add("java04");
ArrayList al2 = new ArrayList();
al2.add("java01");
al2.add("java03");
al2.add("java06");
//取交集,al1中只会保留与al2中相同的元素
al1.retainAll(al2);
System.out.println("al1 : "+al1);
System.out.println("al2 : "+al2);
}
}
去除两集合相同元素:
例:
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al1 = new ArrayList();
al1.add("java01");
al1.add("java02");
al1.add("java03");
al1.add("java04");
ArrayList al2 = new ArrayList();
al2.add("java01");
al2.add("java03");
al2.add("java06");
//移除包含的al2中的所有元素
al1.removeAll(al2);
System.out.println("al1 : "+al1);
System.out.println("al2 : "+al2);
}
}
元素的取出(重点):
迭代器:Iterator接口。

答:其实就是集合的取出元素的方式。
迭代器原理:把取出方式定义在了集合的内部,这样取出方式就可以直接访问集合内的元素,那么取出方式就被定义成了内部类。而每个容器的数据结构不同,所以取出的动作细节也不一样,但都有共性:判断和取出,那么可以将这些共性抽取,就形成了Iterator接口。

答:通过一个对外提供的方法iterator();。
例:
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//获取迭代器对象
Iterator it = al.iterator();
//判断是否有元素
while(it.hasNext()){
//因为返回的是Object类型,所以要强制转换成String类
String s = (String)it.next();
System.out.println(s);
}
}
}
国外的写法:
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//放到for循环中,局部变量用完后就会被垃圾回收,节省了空间。
for(Iterator it = al.iterator();it.hasNext();){
String s = (String)it.next();
System.out.println(s);
}
}
}
Collection
|——List:元素是有序的,元素可以重复,因为该集合体系有索引。
|——Set:元素是无序的,元素不可以重复。
List特有方法:凡是可以操作角标的方法都是该体系特有的方法。
增:
void add(int index, E element);在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection<? extends E> c);将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
删:
E remove(int index);移除列表中指定位置的元素(可选操作)。
改:E set(int index, E element);用指定元素替换列表中指定位置的元素(可选操作)。
查:
E set(int index, E element);用指定元素替换列表中指定位置的元素(可选操作)。
List<E> subList(int fromIndex, int toIndex);返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
ListIterator<E> listIterator(int index);返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
采用for循环:
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
for(int x=0;x<al.size();x++){
String s = (String)al.get(x);
System.out.println(s);
}
}
}
采用Iterator:
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
for(Iterator it = al.iterator();it.hasNext();){
String s = (String)it.next();
System.out.println(s);
}
}
}
练习:在迭代过程中,添加或者删除元素。
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//放到for循环中,局部变量用完后就会被垃圾回收,节省了空间。
for(Iterator it = al.iterator();it.hasNext();){
Object obj = it.next();
if(obj.equals("java02")){
al.add("java08");//添加
//al.remove("java01");//删除
}
System.out.println(obj);
}
}
}
分析:发生了ConcurrentModificationException并发修改异常,产生原因在Iterator操作元素时,ArrayList也操作元素。
解决办法:要么使用迭代器的方法,要么使用集合的方法。
发现:Iterator里没有添加操作,所以使用其子类ListIterator。List集合特有的迭代器ListIterator是Iterator的子接口。在迭代时,不可以通过集合对象的方法操作集合的元素,因为会发生并发修改异常,所以在迭代时,只能用迭代器的方法操作元素。可是Iterator方法是有限的,不能对元素进行判断,取出,删除的操作。如果想要其他的操作如:添加,修改等,就需要使用其子接口ListIterator。该接口只能通过List集合的listIterator();方法获取。
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//放到for循环中,局部变量用完后就会被垃圾回收,节省了空间。
for(ListIterator it = al.listIterator();it.hasNext();){
Object obj = it.next();
if(obj.equals("java02")){
it.add("java08");//添加
//it.remove();//删除
}
}
System.out.println(al);
}
}
ArrayList:底层的数据结构使用的是数组结构。查询速度很快,但是增删速度稍慢。线程不同步,默认是10个长度,50%延长。
LinkedList:底层的数据结构使用是链表数据结构。增删速度很快,但是查询速度稍慢。(面试)
Vector:底层的数据结构使用是数组结构。1.0版本之后出现。线程同步。被ArrayList替代。100%延长。
数组结构:
链表结构:
最常用到的集合:
List:ArrayList,LinkerList
Set:HashSet
Map:HashMap
练习:Vector
import java.util.*;
class Demo
{
public static void main(String args[]){
Vector v = new Vector();
v.add("java01");
v.add("java02");
v.add("java03");
v.add("java04");
Enumeration e = v.elements();
while(e.hasMoreElements()){
Object obj = e.nextElement();
System.out.println(obj);
}
}
}
发现:枚举和迭代器很像。其实枚举和迭代器是一样的,因为枚举的名称及方法的名称都过长,所以别迭代器取代了。枚举郁郁而终。
LinkedList特有方法:
增:
void addFirst(E e);将指定元素插入此列表的开头。
void addLast(E e);将指定元素添加到此列表的结尾。
删:
E removeFirst();移除并返回此列表的第一个元素。
E removeLast();移除并返回此列表的最后一个元素。
查:
E getFirst();返回此列表的第一个元素。
E getLast();返回此列表的最后一个元素。
例:
import java.util.*;
class Demo
{
public static void main(String args[]){
LinkedList v = new LinkedList();
v.removeFirst();
}
}
分析:当集合为空时,调用removeFirst();报NoSuchElementException没有这个的元素异常。
解决办法:1.6版本后,出现了pollFirst();获取并移除此列表的第一个元素,如果此列表为空,在返回null。
import java.util.*;
class Demo
{
public static void main(String args[]){
LinkedList v = new LinkedList();
v.pollFirst();
}
}
在1.6版本后出现的替代方法:
增:
boolean offerFirst(E e);在此列表的开头插入指定的元素。
boolean offerLast(E e);在此列表末尾插入指定的元素。
E peekFirst();获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
E peekLast();获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
获取元素,但不删除元素。如果集合没有元素,会返回null。
E pollFirst();获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
E pollLast();获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
获取元素,但元素被删除。如果集合没有元素,会返回null。
练习:LinkedList模拟一个堆栈或者队列数据结构。
堆栈:先进后出。
import java.util.*;
class DuiZhan
{
private LinkedList ll;
DuiZhan(){
ll = new LinkedList();
}
public void myAdd(Object obj){
ll.offerFirst(obj);
}
public Object myGet(){
return ll.pollFirst();
}
public boolean isNull(){
return ll.isEmpty();
}
}
class Demo
{
public static void main(String args[]){
DuiZhan dz = new DuiZhan();
dz.myAdd("java01");
dz.myAdd("java02");
dz.myAdd("java03");
dz.myAdd("java04");
while(!dz.isNull()){
System.out.println(dz.myGet());
}
}
}
队列:先进先出。
import java.util.*;
class DuiLie
{
private LinkedList ll;
DuiLie(){
ll = new LinkedList();
}
public void myAdd(Object obj){
ll.offerFirst(obj);
}
public Object myGet(){
return ll.pollLast();
}
public boolean isNull(){
return ll.isEmpty();
}
}
class Demo
{
public static void main(String args[]){
DuiLie dl = new DuiLie();
dl.myAdd("java01");
dl.myAdd("java02");
dl.myAdd("java03");
dl.myAdd("java04");
while(!dl.isNull()){
System.out.println(dl.myGet());
}
}
}
练习:去除ArrayList集合中的重复元素。
import java.util.*;
class Demo
{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java03");
al.add("java04");
al.add("java04");
System.out.println(singleElement(al));
}
public static ArrayList singleElement(ArrayList al){
ArrayList newAl = new ArrayList();
Iterator it = al.iterator();
while(it.hasNext()){
Object obj = it.next();
if(!newAl.contains(obj))
newAl.add(obj);
}
return newAl;
}
}
ArrayList和LinkedList的选择:查询多用ArrayList,修改多用LinkedList。
Set
|——HashSet:底层数据结构是哈希表。

答:是通过元素的两个方法:hashCode();和equals();来完成的,如果元素的hashCode相同,才会判断equals是否为true。如果元素的hashCode值不同,不会调用equals。
注:对于判断元素是否存在,以及删除元素等操作依赖的方法是元素的hashCode和equals方法。ArrayList只依赖equals。HashSet先判断hashCode再判断equals。
练习:往HashSet集合中存入自定义对象,姓名和年龄相同为同一个人。
import java.util.*;
class Person
{
private String name;
private int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println("name = "+name+",age = "+age);
}
public int hashCode(){
System.out.println("hashCode run "+name);
return 60;
}
public boolean equals(Object obj){
Person p = (Person)obj;
System.out.println("equals run "+name+"::"+p.name);
return name.equals(p.name)&&age==p.age;
}
}
class Demo
{
public static void main(String args[]){
HashSet hs = new HashSet();
System.out.println(hs.add(new Person("zhangsan",20)));
System.out.println(hs.add(new Person("wangwu",18)));
System.out.println(hs.add(new Person("wangwu",18)));
}
}
分析:可以看到,当hashcode相同时,集合开始调用对象的equals方法进行比较。可见HashSet会先判断hashCode值,如果相同则再对equals进行判断。
改进:为了减少判断次数,提高效率,可以对hashcode方法进行复写。
import java.util.*;
class Person
{
private String name;
private int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println("name = "+name+",age = "+age);
}
public int hashCode(){
System.out.println("hashCode run "+name);
return name.hashCode()+age*31;
}
public boolean equals(Object obj){
Person p = (Person)obj;
System.out.println("equals run "+name+"::"+p.name);
return name.equals(p.name)&&age==p.age;
}
}
class Demo
{
public static void main(String args[]){
HashSet hs = new HashSet();
System.out.println(hs.add(new Person("zhangsan",20)));
System.out.println(hs.add(new Person("wangwu",18)));
System.out.println(hs.add(new Person("wangwu",18)));
}
}