——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
一、概述
集合框架是用于储存数组的容器,具有统一的父类接口Collection,主要的集合关系类如下图:
然而集合和数组的是有区别的,区别如下:
1:数组是固定长度的;集合可变长度的。
2:数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
3:数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
集合类主要的区别是他们的数据结构有所不同。
二、Collection接口
Collection接口作为根接口,List、Set、Queue接口均直接继承自它。
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();
三、Iterator接口
Iterator(迭代器)是一种集合的取出方式,用于集合的数据的遍历等。不同容器的数据结构不同,所以取出的动作实现不同,但是他们都共有内容判断和取出的方法,因此形成了一个接口Iterator。
这样做的好处是:如何实现的具体取出细节,这个不用关心,这样就降低了取出元素和具体集合的耦合性。
下面是一个通常用法:
public static void main(String[] args) {
ArrayList alist = new ArrayList();
alist.add("java01");
alist.add("java02");
alist.add("java03");
alist.add("java04");
for(Iterator it = alist.iterator(); it.hasNext();) {
System.out.println(it.next());
}
/* 另一种循环方式
Iterator it = coll.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
*/
}
四、List接口
List是Collection的子接口之一,具备了Collection的所有方法。
List的特点是:元素有序,可以重复,有索引。
List主要操作函数如下:
1.add(index, element)指定位置添加元素,addAll(index, Collection)指定位置添加一个集合的元素。
2.remove(index)删除指定为的元素。
3.set(index, element)修改指定位置的元素。
4.get(index)获取指定位置的元素。
5.subList(from, to)获取从[from]到[to-1]的子List。
6.listIterator()获取List特有的迭代器。
for(int x=0; x<list.size(); x++){
system.out.print("get:"+list.get(x));
}
List拥有自己特有的迭代器:ListIterator
ListIterator主要函数为:
void add(E e); 将指定的元素插入列表(可选操作)。
boolean hasNext() ;以正向遍历列表时,如果列表迭代器有多个元素,则返回 true
boolean hasPrevious(); 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
E next() ;返回列表中的下一个元素。
int nextIndex() ;返回对 next 的后续调用所返回元素的索引。
E previous() 返回列表中的前一个元素。
int previousIndex() 返回对 previous 的后续调用所返回元素的索引。
void remove() 从列表中移除由 next 或 previous 返回的最后一个元素(可选操作)。 void set(E e) 用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。
ListIterator不仅可以正向遍历,而且也有逆向遍历功能。代码如下:
public static void main(String[] args) {
ArrayList alist = new ArrayList();
alist.add("L01");
alist.add("L02");
ListIterator it = alist.listIterator();
while(it.hasNext()) { //正向
System.out.println(it.next());
}
while(it.hasPrevious()) { //逆向
System.out.println(it.previous());
}
}
List有三个具体的子类,分别是ArrayList、LinkedList和Vector。
ArrayList:
底层数据结构为数组结构,线程非同步,查询速度快,增删速度慢,效率比Vector高。
ArrayList是按照原数组的50%延长。构造一个初始容量为 10 的空列表。LinkedList
底层数据结构为链表结构,线程非同步,查询速度慢,增删速度快。
LinkedList的特有函数有:
1.void addFirst(E e);添加到头部。
2.void addLast(E e);添加到尾部。
3.E getFirst();获取头部元素。
4.E getLast();获取尾部元素。
5.E removeFirst();删除头部元素,并返回元素。
6.E removeLast();删除尾部元素,并返回元素。
下面是我们通过linkedlist模拟栈进出的练习:
import java.util.*;
class QueueTest {
private LstedList Lst;
QueueTest() {
Lst = new LinkedList();
}
public boolean isNull() {
return Lst.isEmpty();
}
public void qAdd(Object obj) {
Lst.addFirst(obj);
}
public Object qGet() {
return Lst.removeFirst();
}
}
class LstedListTest {
public static void main(String[] args) {
MyQueue q = new QueueTest();
q.qAdd("L01");
q.qAdd("L02");
while(!q.isNull()) { //先进后出
System.out.println(q.qGet());
}
}
}
3. Vector
Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe)。
Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
Vector特有的取出方式是枚举(Enumeration),和迭代器方法类似,代码如下:
import java.util.*;
class LstedListTest {
public static void main(String[] args) {
Vector v = new Vector();
v.add("L01");
v.add("L02");
Enumeration en = v.elements();
while(en.hasMoreElements()) { //通过枚举来输出
System.out.println(en.nextElement());
}
}
}
五、Set接口
Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种,迭代器。
Set元素无序(存入与取出的顺序不一致),元素不可重复。
有两个主要子类:HashSet和TreeSet。
- HashSet:
HashSet的底层数据结构为哈希表。
哈希表的原理:
1,对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。
2,哈希值就是这个元素的位置。
3,如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。
4,存储哈希值的结构,我们称为哈希表。
5,既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。
从上可知, HashSet是根据hashCode()和equals()方法进行存储。add时,如果是第一次添加返回true(添加成功),否则返回false(添加失败)。
所以当HashSet存储自定义对象时,需要覆盖hashCode和equals方法,当hashCode值相同、equals返回真时,认为是同一个对象。添加时,当hashCode值不同时,不会调用equals比较,当hashCode的值相同时,才会调用equals方法比较两个对象。
示例代码如下:
import java.util.*;
class Person {
private String name;
private int age;
Person(String name,int age) {
this.name = name;
this.age = age;
}
public int hashCode() {
return name.hashCode() + age*37;
}
public boolean equals(Object obj) {
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
class HashSetTest {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a2",12));//重复添加
Iterator it = hs.iterator();
while(it.hasNext()) { // 遍历集合
Person p = (Person)it.next();
System.out.println(p.getName()+" "+p.getAge());
}
}
}
上例中重复添加的经过hashCode() 和equals()比较 返回true,所以添加失败,集合里一共有2个元素。
- TreeSet
TreeSet的底层数据结构为二叉树,可以对集合中的元素进行排序。数据添加后就会进行排序。
注意:如果元素不具备比较性,在运行时会发生ClassCastException异常。
因此如果是自定义对象的排序的话,需要自定义对象自己实现Comparable接口或者提供比较器(Comparator接口)。 这两种方式区别如下:
1:让元素自身具备比较性,需要元素对象实现Comparable接口,覆盖compareTo方法。
2:让集合自身具备比较性,需要定义一个实现了Comparator接口的比较器,并覆盖compare方法,并将该类对象作为实际参数传递给TreeSet集合的构造函数。
第二种方式较为灵活。
下面是第一种方法实例:
import java.util.*;
//学生类
class Student implements Comparable
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int hashCode()//复写hashCode
{
return name.hashCode()+age;
}
public boolean equals(Object obj)//复写equals
{
if(!(obj instanceof Student))
throw new RuntimeException();
Student s = (Student)obj;
return this.name.equals(s.name)&&this.age==s.age;
}
public int compareTo(Object obj)//复写compareTo以便TreeSet集合调用
{
Student s=(Student)obj;
if(this.age==s.age)
return this.name.compareTo(s.name);
return this.age-s.age;
}
}
class TreeSetTest
{
public static void main(String[] args)
{
TreeSet<Student> t=new TreeSet<Student>();
t.add(new Student("wo1",12));
t.add(new Student("wosj",2));
t.add(new Student("wo",22));
for (Iterator<Student> it=t.iterator(); it.hasNext(); )
{
Student s=it.next();
System.out.println(s.getName()+" "+s.getAge());
}
}
}
这种方式也被称为元素的自然顺序,或者叫做默认顺序。
第二比较方法实例如下:
import java.util.*;
//学生类
class Student implements Comparable
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int hashCode()//复写hashCode
{
return name.hashCode()+age;
}
public boolean equals(Object obj)//复写equals
{
if(!(obj instanceof Student))
throw new RuntimeException();
Student s = (Student)obj;
return this.name.equals(s.name)&&this.age==s.age;
}
public int compareTo(Object obj)//复写compareTo以便TreeSet集合调用
{
Student s=(Student)obj;
if(this.age==s.age)
return this.name.compareTo(s.name);
return this.age-s.age;
}
}
class TreeSetTest
{
public static void main(String[] args)
{
TreeSet<Student> t=new TreeSet<Student>(new CompareLen());//初始化创建时候就要把自定义的比较类添加进去
t.add(new Student("wo1",12));
t.add(new Student("wosj",2));
t.add(new Student("wo",22));
for (Iterator<Student> it=t.iterator(); it.hasNext(); )
{
Student s=it.next();
System.out.println(s.getName()+" "+s.getAge());
}
}
}
//定义一个比较器,以姓名长度为主要比较
class CompareLen implements Comparator<Student>
{
public int compare(Student s1,Student s2)
{
int num=new Integer(s1.getName().length()).compareTo(new Integer(s2.getName().length()));
return num;
}
}
这种方法比较的一般都是元素不具备比较方式的时候,通过自定义比较器来实现。