---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
一、概述
Java中的集合框架,其初一看是个复杂庞大的综合体,许多初学者对此也是望而止步。确实,如果不仔细理清其中的关系脉络,深入弄清楚每个集合的用法,找出它们的区别和联系,那么便会一直都云里雾里,找不着北,就是再努力再用心,也是无用。集合框架呢,大体上就是两部分,就是Collection和Map,而这两部分自然是区别很大,Collection中存储的是一个个对象,而Map中存储的是键值对(Key——Value),简而来说就是一一对应的关系。而这二者的实现,内部其实是有联系的,简单来讲,Map的底层实现就是Set,当然这是很简单的讲的!
下图是集合框架中接口的示意图
二、简介
对于初学者来说,弄清楚为什么学习一个知识点很重要,集合当然也不例外。首先学习集合框架,我们得要知道为什么会出现集合?其实很简单,在Java中集合是一种最常用的容器,而容器,顾名思义就是存储东西的,在Java中万物皆对象,很显然集合就是存储对象用的。对于有些基础的同学来说,可能会有疑问,数组也是容器,也是存储东西的,但是二者有何区别呢?仔细想想其实很简单,首先数组的长度是固定的而集合的长度是可变的,这是其一,再有就是,数组中只能存储基本数据类型的,集合却是可以存储对象的。
下图是集合框架类图
Collection为集合根接口,下面有两个子接口List和Set接口,下面的几个集合类为根据具体数据结构存储对象的具体实现。Map是与Collection同级的接口,都同为集合框架下的两大体系的根接口。
关于Collection下两个接口List和Set的区别,这还是有必要说的。首先存储在List集合中的元素是有序的,这里所说的有序自然就是你怎么存进去的就怎么存储,而且List集合中的元素是可以重复,这其实是因为List集合中的每一个元素都是有索引的,有索引自然是可以根据索引来区分重复的元素,所以可以重复存放元素也是必然的。但是Set集合就是没有索引的,所以其存放元素是无序的,因而其中存放元素也就不能重复。当然这只是从总体上来说,其具体的实现又各有不同,下面我们就来说说List和Set集合的具体实现类的区别。
三、Collection下集合类的实现和区别
1.ArrayList
ArrayList是使用频率非常高的一个集合。其底层的实现使用了数组的数据结构。对于数组对于数组我们知道,在数组中每增加一个元素,那么这个位置之后的所有的元素都要往后面移动,每删除一个元素,那么删除的元素之后的所有元素的位置都要向前移动,这就是数组这种数据结构的特性,这种特性导致了数组中对元素的增删操作时非常的消耗的资源,而事实上底层使用数组作为数据结构来设计的ArrayList集合,其增删元素操作的效率自然也是低的,但是作为数组,因为其中的每个元素都是带有索引的,所以根据索引去查找起来速度当然是很快的,查找快,修改自然也不再话下。
说到这里有个知识点一定要说下的,那就是如何取出集合中的元素。关于这一点,也很简单Collection接口继承了Iterable接口,Iterable接口中的Iterator<T> iterator();方法返回一个迭代器对象,而Iterator对象提供了三个方法,boolean hasNext();E next();void remove();来对元素迭代操作,所以Collection的所有子类都可以使用Iterator迭代器对象来取出元素,包括我们后面要讲的Vector、LinkedList、HashSet、TreeSet。但是有一点一定要记住,就是在迭代操作的时候,不能集合的增删方法和迭代器的增删方法一起使用,这样会造成并发异常,所以一般在迭代操作的过程中,不要使用集合的方法来增删操作,而是使用迭代器提供的方法操作。
如下例子:
import java.util.*;
class ArrayListDemo
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
al.add("java05");
for(Iterator it = al.iterator(); it.hasNext(); )
{
System.out.println(it.next());
}
}
}
还有一点,ArrayList的用法和作用几乎跟Vector一模一样,这二者的区别就在于,ArrayList是线程不同步的,Vector是线程同步,众所周知,线程同步需要消耗大量的资源,其效率比较低下,所以在jdk1.2版本之后出现的ArrayList就是用来取代Vector的,所以在平时的工作应用中推荐使用ArrayList,哪怕需要线程同步的情况下,关于ArrayList的线程同步,这里就不再赘述,Java也是提供了相当便于我们使用的解决方案的。
有些同学可能会有疑问,在ArrayList中的像boolean contains(Object obj);、boolean remove(Object obj);方法,判断集合中是否有指定元素,删除一个元素,这些类似的方法首先都要拿传进来的对象到集合中去比较是否有相同的对象,那么ArrayList集合是如何来进行比较的呢?其实AyyayList集合底层是使用的集合中的对象的boolean equals(Object obj);方法来进行两个对象的比较的,返回false为不相同,true为相同,这是Object类中的一个方法,所以如果ArrayList集合中存储的是自定义对象的话,最好覆写下这个boolean equals(Object obj);方法。
2.LinkedList
LinkedList底层实现是基于链表。对于链表这种基础的数据结构,我们知道,其对于元素的操作是基于指针的,所以对于这种结构来说,查找是低效的,修改的话自然也是要先查找的,所以LinkedList对元素的查找修改比较慢,而增删的效率很高。
LinkedList中比较元素也是使用的元素的boolean equals(Object obj);方法,这个跟AyyayList是一样的,其实只要是List集合中,都是使用这个方式来比较的。LinkedList也是线程不同步的。
/*
使用LinkedList模拟一个堆栈或者队列的数据结构
(堆栈:先进后出 如同一个杯子)
(队列:先进先出 如同一个水管)
*/
import java.util.*;
class DuiLie
{
private LinkedList link;
DuiLie()
{
link = new LinkedList();
}
public void myAdd(Object obj)
{
link.addFirst(obj);
}
public Object myGet()
{
return link.removeLast();//removeFirst()就是堆栈
}
public boolean isNull()
{
return link.isEmpty();
}
}
class LinkedListTest
{
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());
}
}
}
3.HashSet
HashSet底层的实现是基于的哈希表数据结构。HashSet集合中的元素是不能重复的,也就是说HashSet集合中不能存储相同的对象,一个对象存储HashSet集合中的时候,跟此集合中的每个元素进行比较,如果没有相同的元素,则存进去,否则,判断有相同的元素,不存进来。但是其是如何进行元素之间的比较的呢?首先,两个元素先根据int hashCode();方法比较哈希值,如果哈希值相同,则根据boolean equals(Object obj);方法比较,如果equals方法返回false,也就是元素不相同,则会在这个地址上顺延存储对象,相同的话则不会存进来。显然,HashSet集合中的boolean contains(Object obj);、boolean remove(Object obj)方法就是依据的这两个方法。
//往HashSet中存放Person对象,姓名和年龄相同算是对象相同
import java.util.*;
class Person
{
private String name;
private int age;
Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int hashCode()
{
return name.hashCode() * 39 + age;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
return this.name.equals(p.getName()) && this.age == p.getAge();
}
}
class HashSetTest
{
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add(new Person("张三002", 26));
hs.add(new Person("张三001", 27));
hs.add(new Person("张三001", 27));//已有,不存
hs.add(new Person("张三002", 26));//已有,不存
for(Iterator it = hs.iterator(); it.hasNext(); )
{
Person p = (Person)it.next();
System.out.println(p.getName() + "..." + p.getAge());
}
}
}
4.TreeSet
TreeSet集合的底层实现的数据结构是二叉树,其可以对Set集合中的元素进行排序。存入TreeSet集合中的元素都必须有自然顺序,否则存不进去,或者说只能存一个元素进去,因为没有办法进行元素的顺序的比较。所谓的自然顺序元素类实现Comparable接口,覆写int compareTo(T o);,使用这个方法来比较元素的顺序,这就是所谓的元素的自然顺序。
若是某个类真的没有实现自然顺序或者自然顺序不是我们想要的,就是说存到TreeSet集合中的元素不具备比较性(元素不具备自然顺序),那怎样才能解决这个问题?其实元素不具备自然顺序,那么我们可以给容器具备排序性,让容器来决定元素的顺序,我们可以在集合初始化的时候传入自定义的比较器对象(Comparator),自定义自己的比较器类实现Comparator接口,覆写int compare(T o1, T o2);
//往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 compareTo(Object obj)//自然顺序为年龄排序,年龄相同的依据名字排
{
if(!(obj instanceof Student))
throw new RuntimeException("不是Student对象");
Student stu = (Student)obj;
if(this.age == stu.getAge())
{
return this.name.compareTo(stu.getName());
}
if(this.age > stu.getAge())
return 1;
return -1;
}
}
class TreeSetDemo
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet(/*new MyComparator()*/);//也可以根据下面的比较器排
ts.add(new Student("张三001", 23)); //姓名排
ts.add(new Student("张三002", 23));
ts.add(new Student("张三001", 24));
ts.add(new Student("张三004", 23));
ts.add(new Student("张三001", 23));
for (Iterator it = ts.iterator(); it.hasNext(); )
{
Student stu = (Student)it.next();
System.out.println(stu.getName() + "..." + stu.getAge());
}
}
}
class MyComparator implements Comparator//自定义比较器
{
public int compare(Object obj1, Object obj2)
{
Student s1 = (Student)obj1;
Student s2 = (Student)obj2;
if(s1.getName() == s2.getName())
return s1.getAge() - s2.getAge();
return s1.getName().compareTo(s2.getName());
}
}
---------------------- ASP.Net+Unity开发、 .Net培训、期待与您交流! ----------------------