一.框架概述
在开发过程中,java提供的集合框架(Collection)技术给我们提供了很多的便利,他封装了很多的数据结构(如数组,链表,堆,栈等),并未这些数据结构提供了很多的成员方法供我们很方便的去使用这些数据结构。
java中的框架技术,我们先看一下他的结构:
Collection
|-List
|-ArrayList
|-LinkedList
|-... ...
|-Set
|-HashSet
|-TreeSet
|-... ...
Map
|-HashTable
|-HashMap
|-TessMap
|-... ...
以上结构中列出的是我们需要理解掌握的具体的集合类,java中的集合框架,其实就是对存放数据的容器进行的封装,我们需要学会的是如何对这些容器进行操作,来实现我们需要的增删改查。
二.List
首先将讲一下List,他的存储方式是有序的,所谓有序就是,以什么顺序存进去,就会以什么顺序遍历获得,允许存放相同的元素,实现他的两个重要的子类是ArrayList跟LinkedList。
ArrayList与LinkedList的区别:
ArrayList:数据原型为数组,带有索引,用于数据获取比较方便,对于插入,删除数据的操作比较慢。线程不同步。
LinkedList:数据原型为链表,对于数据的插入,删除比较高效,数据的检索获取就相对低效一点。线程不同步。
他们的的增删改查操作比较简单,无非就是一些add,remove,set等操作,查阅api即可,主要看一下遍历的问题。在集合中,java提供了Iterator(迭代器)来遍历集合:
List l = new LinkedList();
l.add("qwer");
...//添加操作
Iterator iterator = l.iterator();
while(iterator.hasNext()){
sop(iterator.next());
}
由于Iterator值提供了hasNext(), next(), remove()三个方法,功能比较简单,无法实现逆向遍历的操作,在LinkedList中提供了listIterator(int index),返回一个ListIterator接口对象,在java源码中我们可以发现LickedList中有一个实现了ListIterator的内部类,它实现了ListIterator里面的方法,因此,返回的ListIterator对象可以实现逆向遍历的操作,详细操作参阅API。
三.Set
Set的存储方式是无序的,元素不可重复的。我们以a->b->c->d的顺序想Set中存储数据时,我们不能够保证以a->b->c->d的顺序取出,Set的底层是有Map实现的。Set的相关操作也是参阅API,比较简单,遍历方式也是与ArrayList一样的,获取一个Iterator对象进行遍历,这边我们主要需要了解的是他们是根据什么来判断存储的先后顺序的。
HashSet:他的底层数据结构是哈希表,线程不同步。
我们看一下下面这段代码:
public class Demo {
public static void main(String[] args) {
HashSet<Student> s = new HashSet<Student>();
s.add(new Student("zhangsan", 22));
s.add(new Student("zhangsan", 21));
s.add(new Student("lisi", 21));
sop(s);
}
private static void sop(Object obj){
System.out.println(obj);
}
}
class Student {
public String name;
public int age;
public Student(String name, int age){
this.name = name;
this.age = age;
}
public int hashCode(){
System.out.println(this.name + " -----> hashCode");
return name.hashCode();
}
public boolean equals(Object obj){
if(!(obj instanceof Student))
return false;
Student s = (Student)obj;
System.out.println(this.name + " +++ equals +++ " + s.name);
return (this.age == s.age);
}
public String toString(){
return this.name + ":" + this.age;
}
}
然后我们看一下他的运行结果
zhangsan -----> hashCode
zhangsan -----> hashCode
zhangsan +++ equals +++ zhangsan
lisi -----> hashCode
[lisi:21, zhangsan:21, zhangsan:22]
我们不难发现,在向HashSet中添加元素的时候,首先会调用对象的hashCode方法比较哈希值,如果哈希值相等,还会调用equals方法进行进一步的比较。所以,他是通过hashCode与equals两个方法去实现插入元素的唯一性的。
TreeSet:他的底层数据结构是二叉树,线程不同步,他实现插入唯一性有两种方法:
(1)插入对象实现Comparable接口,实现里面的compareTo方法
只需要将上述的Student做一下修改即可
class Student implements Comparable<Student>{
/*
* ... ...
*/
@Override
public int compareTo(Student s) {
// TODO Auto-generated method stub
int i = this.name.compareTo(s.name);
if(i == 0)
return Integer.valueOf(this.age).compareTo(Integer.valueOf(s.age));
return i;
}
(2)新建一个类,实现Comparator接口,实现里面的compare方法,然后将该类作为参数传进TreeSet的构造函数
public class SplitDemo {
public static void main(String[] args) {
TreeSet<Student> s = new TreeSet<Student>(new stuCompare());
s.add(new Student("zhangsan", 21));
s.add(new Student("zhangsan", 22));
s.add(new Student("lisi", 21));
sop(s);
}
private static void sop(Object obj){
System.out.println(obj);
}
}
class stuCompare implements Comparator<Student>{
@Override
public int compare(Student stu1, Student stu2) {
System.out.println(stu1.name + " ===> " + stu2.name);
int i = stu1.name.compareTo(stu2.name);
if(i == 0)
return Integer.valueOf(stu1.age).compareTo(Integer.valueOf(stu2.age));
return i;
}
}
四.Map
Map主要是以键值对(Key-Value)的形式进行存储的,他的存储也是无序的,不可重复的。Map接口中并没有提供Iterator,所以,他的遍历不是以Iterator实现的,Map中实现遍历有以下两种方式:
(1)keSet方法
Map<String, String> m = new HashMap<String, String>();
m.put("1", "hello1");
m.put("2", "hello2");
m.put("3", "hello3");
m.put("4", "hello4");
m.put("5", "hello5");
Set<String> set = m.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
String str = (String)iterator.next();
sop(str + ":" + m.get(str));
}
(2)entrySet方法
Set<Map.Entry<String,String>> set = m.entrySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
Map.Entry<String, String> entry = (Map.Entry<String, String>)iterator.next();
sop(entry.getKey() + ":" + entry.getValue());
}
HashTable:根据哈希值存储,允许存储null键null值,支持线程同步,存储无序
HashMap:根据哈希值存储,不允许存储null键null值对象,不支持线程同步,存储无序
TreeMap:存储有序,其有序原理与TreeSet一样,在构造函数中将实现Comparator的类对象传入,此处不再做过多介绍。