集合框架概述
在java中任何事物都是对象的,集合框架也是一种类。数据结构定义的类保存在集合框架中,所以也称为集合类,它们都在util包下。使用集合框架可以保存数据,和数组有很多相似的地方,可以把它看做一种“容器”。但是集合和数组还是有很大区别的,在数组定义时必须定义数组的大小,从而给数组对象分配内存。在定义集合时,并不需要定义集合大小的,系统会自动给集合设置一个大小。
Java中集合类关系图:
Collection
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。
|--Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。
|--HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
|--TreeSet:可以对Set集合中的元素进行排序。底层数据结构是二叉树。
Collection
Collection 层次结构 中的根接口,Collection定义了集合框架的共性功能。
集合的作用是用来保存数据,所以集合接口中包含了对数据的增删改查等基本功能。
1,添加:
add(object):添加一个元素
addAll(Collection) :添加一个集合中的所有元素。
2,删除:
clear():将集合中的元素全删除,清空集合。
remove(obj) :删除集合中指定的对象。注意:删除成功,集合的长度会改变。
removeAll(collection) :删除部分元素。部分元素和传入Collection一致。
3,判断:
boolean contains(obj) :集合中是否包含指定元素 。
boolean containsAll(Collection) :集合中是否包含指定的多个元素。
boolean isEmpty():集合中是否有元素。
4,获取:
int size():集合中有几个元素。
5,取交集:
boolean retainAll(Collection) :对当前集合中保留和指定集合中的相同的元素。如果两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。
6,获取集合中所有元素:
Iterator iterator():迭代器
7,将集合变成数组:
toArray();
List列表接口
List 列表接口又叫做有序集合接口,在List接口中每一个数据都有一个索引相对应。List接口继承了Collection集合接口,所以具备了collection接口中的所有方法。
List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。
|--ArrayList:底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快。
|--LinkedList:底层的数据结构是链表,线程不同步,增删元素的速度非常快。
|--Vector:底层的数据结构就是数组,线程同步的,Vector无论查询和增删都巨慢。
整理了List特有方法。凡是可以操作角标的方法都是该体系特有的方法
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集合特有的迭代器。
List集合支持对元素的增、删、改、查。
List集合因为角标有了自己的获取元素的方式: 遍历。
for(int x=0; x<list.size(); x++){
sop("get:"+list.get(x));
}
在进行list列表元素迭代的时候,如果想要在迭代过程中,想要对元素进行操作的时候,比如满足条件添加新元素。会发生.ConcurrentModificationException并发修改异常。
导致的原因是:
集合引用和迭代器引用在同时操作元素,通过集合获取到对应的迭代器后,在迭代中,进行集合引用的元素添加,迭代器并不知道,所以会出现异常情况。
如何解决呢?
既然是在迭代中对元素进行操作,找迭代器的方法最为合适.可是Iterator中只有hasNext,next,remove方法.通过查阅的它的子接口,ListIterator,发现该列表迭代器接口具备了对元素的增、删、改、查的动作。
ListIterator是List集合特有的迭代器。
ListIterator it = list.listIterator;//取代Iterator it = list.iterator;
ArrayList类
ArrayList数组列表类,它和数组相似,但ArrayList的大小是可以改变的。ArrayList类实现了List接口中增删改查的方法。
下面通过代码演示ArrayList的具体用法:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class ArrayListDemo {
public static void main(String[] args)
{
ArrayList al=new ArrayList(); //新建一个ArrayList对象
al.add("张三"); //调用添加方法add 添加一条数据
al.add("李四");
al.add("王五");
al.add("李四");//ArrayList对象中元素是可以重复的
//al.remove(2); //删除指定索引位的元素并返回被删的元素 "李四"。
al.get(1); //通过索引获取指定元素"张三"
al.indexOf("张三"); //获取指定元素第一次出现的索引位,如果该元素不存在返回-1
al.lastIndexOf("张三");//反向索引指定元素的位置
al.subList(0, 2);//获取子列表 (角标为0~2:但不包括2)
al.set(0, "老刘");//对指定索引位进行元素的修改
Iterator it=al.iterator(); //创建迭代器对象
while(it.hasNext())//判断:如果存在内容,遍历
{
System.out.println(it.next()); //打印
}
//list集合特有的迭代器 ListIterator listIterator()
ListIterator iterator=al.listIterator(); //通过调用listIterator()创建迭代器对象
while(iterator.hasNext())
{
Object obj=iterator.next();
if(obj.equals("java02"))
{
//在迭代时,不可以通过集合对象的方法操作集合中的元素。
//al.add("java009"); × 错误用法
//必须通过迭代器操作集合中元素
iterator.add("老王");
}
}
}
}
LinkedList 链状列表
在LinkedList 中,所有元素是以链状的形式存在。相邻的几个数据组成一个链,具体数量和链的多少都是有系统自动分配的,不受程序员控制。
特点:当查找某一个数据时需要首先找到它所在的链,然后在该链中再查找数据,所以LinkedList 的查找效率低于ArrayList。
下面通过代码演示LinkList的具体用法:
import java.util.*;
public class LinkList {public static void main(String[] args){LinkedList link =new LinkedList(); //创建LinkedList对象link.add("java1.0"); //向LinkedList对象中添加元素link.add("java2.0");link.add("java3.0");link.add("java3.0"); //可以添加相同元素sop(link);link.addFirst("C++");//将指定元素插入此列表的开头link.offerFirst("带头大哥");//在JDK1.6中的替代方法:在此列表的开头插入指定的元素sop(link);link.addFirst("addFirst起始");//将指定元素插入此列表的末尾link.offerFirst("offerFirst末尾");//在JDK1.6中的替代方法:将指定元素插入此列表的末尾/*link.get(0);//获得返回此列表中指定位置处的元素link.removeFirst(); //取出第一个,其他元素删除link.removeLast();//取出第一个,其他元素删除*/link.peekFirst(); //获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。link.peekLast(); //获取元素,但不删除元素。如果集合中没有元素,会返回null。link.pollFirst(); //获取并移除此列表的第一个元素;如果此列表为空,则返回 null。link.pollLast(); //获取元素,但是元素被删除。如果集合中没有元素,会返回null。sop(link);sop(link.peekFirst());sop(link.size());//sop(link.getFirst());//sop(link.getLast());//while (!link.isEmpty()) {//sop(link.removeLast());//}//sop(link.removeFirst());//sop(link.removeFirst());//sop(link.removeFirst());//sop(link.removeFirst());//sop(link);//sop(link.size());}public static void sop(Object obj) {System.out.println(obj);}}
迭代器
迭代是取出集合元素的一种方式,因为Collection类中有iterator方法,所以每一个子类集合对象都具备迭代器。
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.*;
/*
List集合特有的迭代器。ListIterator是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素。
因为会发生ConcurrentModificationException异常。
所以,在迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,
只能对元素进行判断,取出,删除的操作,
如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。
该接口只能通过List集合的listIterator方法获取。
注意:迭代器的next方法返回的是Object类型,所以要记得类型装换
*/
public class Iterator {
public static void main(String[] args)
{
//演示列表迭代器
ArrayList al=new ArrayList();
al.add("java01"); //add(Object obj);
al.add("java02");
al.add("java03");
al.add("java04");
ListIterator li=al.listIterator();
//list集合特有的迭代器 ListIterator
while(li.hasNext())
{
Object obj=li.next(); //迭代器的next方法返回的是Object类型
if(obj.equals("java02"))
{//在迭代时,不可以通过集合对象的方法操作集合中的元素。
//al.add("java009"); × 错误用法
li.add("替换后的java02");
//用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。
//li.set("java006");
}
}
while(li.hasPrevious())
{ // 使用列表迭代器 逆向遍历列表,有多个元素
sop("hasPrevious---"+li.previous());
}
//sop("hasNext---"+li.hasNext());
//sop("hasPrevious---"+li.hasPrevious());
sop(al);//输出al
/*普通迭代
//在迭代过程中,准备添加或删除元素
Iterator it=al.iterator();
while(it.hasNext())
{
Object obj=it.next();
if(obj.equals("java02"))
//al.add("java08");//当方法检测到对象的并发修改,但不允许这种修改时,抛出异常。
it.remove(); //将"java02"的引用从集合中删除了
sop("obj="+obj);
}
sop("al="+al);
*/
}
public static void sop(Object obj) {
System.out.println(obj);
}
}
|--Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。
|--HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。
注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的 hashcode和equals方法。
|--TreeSet:可以对Set集合中的元素进行排序。
底层数据结构是二叉树。
保证元素唯一性的依据:compareTo方法return 0.
TreeSet排序的第一种方式:让元素自身具备比较性。
元素需要实现Comparable接口,覆盖compareTo方法。
也种方式也成为元素的自然顺序,或者叫做默认顺序。
TreeSet的第二种排序方式。
当元素自身不具备比较性时,或者具备的比较性不是所需要的。
这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。
hashset的应用
public class HashSetDemo {
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
HashSet hs=new HashSet();
sop(hs.add("java03"));
sop(hs.add("java03")); //无法添加重复 false
hs.add("java01");
hs.add("java02");
hs.add("java04");
Iterator it=hs.iterator();
while(it.hasNext())
{
sop(it.next());
}
}
}
import java.util.HashSet;/*往hashSet集合中存入自定对象姓名和年龄相同为同一个人,重复元素。HashSet是如何保证元素唯一性的呢?是通过元素的两个方法,hashCode和equals来完成。如果元素的HashCode值相同,才会判断equals是否为true。如果元素的hashcode值不同,不会调用equals。*/class Person2{private int age;private String name;public Person2(String name,int age) {this.name=name;this.age=age;}public String getName(){return name;}public int getAge(){return age;}public int hashCode(){//覆盖父类中的hashCode方法System.out.println(this.name+"...hashCode");//return 60;return name.hashCode()+age*11; //避免重复}//覆盖父类中的equals方法public boolean equals(Object obj){if(!(obj instanceof Person2))return false; Person2 p=(Person2)obj;System.out.println(this.name+"--equals--"+p.name);return this.name.equals(p.name)&&this.age==p.age;}}public class HashsetTest {public static void sop(Object obj){System.out.println(obj);}public static void main(String[] args){HashSet hs=new HashSet();hs.add(new Person2("a1", 11));hs.add(new Person2("a2", 12));hs.add(new Person2("a3", 22));//hs.add(new Person2("a2", 12));//hs.add(new Person2("a3", 22));//sop("a1:"+hs.contains(new Person2("a1", 11)));sop(hs.remove(new Person2("a2", 12)));//Iterator it2=hs.iterator();//while(it2.hasNext())//{//Person2 p=(Person2)it2.next();//sop(p.getName()+"-"+p.getAge());//}}}
TreeSet
import java.util.*;
/*
当元素自身不具备比较性,或者具备的比较性不是所需要的。
这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
当两种排序都存在时,以比较器为主。
定义一个类,实现Comparator接口,覆盖compare方法。
*/
class Student2 implements Comparable //该接口强制让学生具备比较性
{
private String name;
private int age;
Student2(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Object o)
{
//返回负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。
if(!(o instanceof Student2))
throw new RuntimeException("不是学生对象");
Student2 s=(Student2)o;
System.out.println(this.name+"..compare.."+s.name);
if(this.age>s.age)
return 1;
if(this.age==s.age)
{
return this.name.compareTo(s.name);
}
return -1;
}
}
public class TreeSetDemo2 {
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
TreeSet ts=new TreeSet(); //接收了一个接口子对象
//必须具备比较性
ts.add(new Student2("lisi01", 22));
ts.add(new Student2("lisi02", 21));
ts.add(new Student2("lisi97", 19));
ts.add(new Student2("lisi97", 17));
ts.add(new Student2("lisi03", 20));
Iterator it=ts.iterator();
while(it.hasNext())
{
Student2 s=(Student2)it.next();
sop(s.getName()+"---"+s.getAge());
}
}
}
//定义比较器,实现Comparator接口,覆盖compare方法。
class MyCompare implements Comparator
{
@Override
public int compare(Object o1, Object o2)
{
Student2 s1=(Student2)o1;
Student2 s2=(Student2)o2;
int num=s1.getName().compareTo(s2.getName());
if(num==0)
{
new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
/*
if(s1.getAge()>s2.getAge())
return 1;
if(s1.getAge()<s2.getAge())
return -1;
return 0;
*/
}
return num;
}
}