集合我们都知道是用来储存对象的容器,那之前的数组不也可以储存对象么,为什么要出现集合呢?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,然而集合类中提供很多方便操作对象存储的方法,要比数组更容易操作对象,而且集合的长度是可变的,然而数组长度确实固定不变的,这样不利于对对象的间隔储存。
数组和集合类同是容器,有何不同?
数组的长度是固定的,而集合的长度是可变的,集合只能存储对象,数组虽然既可以存储基本数据类型也可以存储对象,但对数组的操作过于麻烦不灵活也不方便。
集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
集合框架为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同。这个存储方式称之为:数据结构。
Java集合框架常见类图
从图中可以看出,大部分的集合的父接口是Iterator,实际上它只是集合遍历的迭代器接口,最特殊的集合父接口是Collection。Collection下面还有常用的两个集合接口List和Set,它两实现了Collection中所有方法,并各自独具特色。
各个常用集合接口及集合类分布总结:
Collection
|——List:元素是有序的(存入的顺序和取出的顺序一致),元素可以重复。因为集合体系有索引。它的储存方式和数组有类似。
|—Vector:底层是数组数据结构。线程同步,所以效率较低,被ArrayList替代了。
|—ArrayList:底层数据结构式数组,所以它查询快,但增删相对慢,线程不同步。默认长度为10,当元素超出时,自动new一个数组对象并且按原来50%增长;
|—LinkedList:底层是链表数据结构,所以查询慢,但增删相对很快,线程不同步。
|——Set :元素是无序的(存入的顺序和取出的顺序有可能不同),元素不可以重复。
|—HashSet:数据结构是哈希表。线程是非同步的,保证元素唯一性的原理:判断元素的hashCode值是否相同。如果相同,还会继续判断元素的equals方法,是否为true。
|—TreeSet:底层数据结构是二叉树。二叉树默认取值顺序是从小到大。可以对set集合中的元素进行排序.(Ascll码排序)
Iterator-->它是集合的一个迭代器接口集成,Collection集合中定义专门的生成Iterator实例的方法来方便随时操作集合Iterator<E> iterator() ;
Iterator迭代器的特点:
①迭代器其实就是集合元素的取出方式。
②每个集合的取出方式都不一样,所以对它们取出方式进行了抽取,抽取为一个接口Iterator,并抽取了3个共性方法hasnext(),next(),remove();
③迭代器降低了取出方式和数据结构之间的耦合性。
④集合的元素取出方式是集合内部的事,所以被定义为内部类,只提供了一个方法来获得取出方式,这个方法为iterator()。
⑤Iterator迭代器只能对集合进行判断,取出删除的操作,当用Iterator迭代时,不可以通过集合对象的方法操作集合中的元素不然会发生并发操作异常ConcurrentModificationException。
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorDemo {
/**
* 迭代器演示
* 集合方法iterator()
* Iterator接口方法hasNext(),next()
*/
public static void main(String[] args) {
ArrayList<String> at = new ArrayList<String>();
at.add("java01");
at.add("java03");
at.add("java02");
at.add("java04");
Iterator<String> it = at.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
Iterator迭代器对集合元素只能做取出,判断,删除操作,不然会发生并发异常,那对集合的修改平凡时,迭代器又该何去何从呢? List集合中ListIterator迭代器:
从上面的图中可以看出Iterator还有一个子接口ListIterator,从字面一看就知道它也是集合迭代器,正是它才拟补了Iterator的不足之处。
List集合特有的迭代器。ListIterator是Interator的子接口。当用Iterator迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生并发操作异常ConcurrentModificationException。所以,在用迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的。只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用某子接口ListIterator。该接口只能通过List集合的listIterator方法获取。
(1)ArrayList类
数据结构为数组,具有查找快,增删慢等特点。
由图可看出它个元素都有相对应的索引,所以有利于查询,但每次增删时,需要对后续的元素进行前移或者后移较消耗资源,所以增删较慢。
(2)LinkedList类
数组结构为链表,具有增删快,查找慢等特点。
由图可以看出链表结构每一个元素都会记住后一个元素的索引,在修改时只需对前一个元素所记的索引进行修改即可,这样来看链表结构的增删速度是很快的。但当查找元素时,只会一个接一个的进行询问查找,在元素多的时候就会很慢了。
(3)HashSet类
数据结构为哈希表,可保持元素唯一性,依赖于hashCode()和equals().
元素存入集合时会先比较之间的哈希值,如果哈希值相同,他就会比较对象是否相同,如果不同就会存入相同哈希值的顺延球下。不同哈希值的元素会存储在不同的顺延球下。
(4)TreeSet类
数据结构为二叉树,具有保持数据唯一性的特性。
元素是以树形结构排列的,遵从左小右大的顺序进行悬挂,取出的默认顺序是从左往右去,也就是从小到大取出。
TreeSet集合具有两种排序方式:
①元素具备比较性,让元素类实现Comparable接口,覆盖CompareTo()方法,这种方式也称为自然顺序,或称为默认顺序。
import java.util.Iterator;
import java.util.TreeSet;
/**
* TreeSet:可以对set集合元素进行排序,
* 底层数据结构是二叉树(左边小,右边大)
* 保证元素唯一性的依据
* compareTo方法默认return 0;
* 需要实现Comparable接口复写compareTo()方法。
* */
public class TreeSetTest {
public static void main(String[] args){
TreeSet<Student> ts = new TreeSet<Student>();
ts.add(new Student("sixi",20));
ts.add(new Student("xiaowang",20));
ts.add(new Student("lizhiyuan",18));
ts.add(new Student("zenhanqing",16));
ts.add(new Student("laomao",21));
Iterator<Student> it = ts.iterator();
while(it.hasNext()){
Student s = it.next();
System.out.println(s.name+"....."+s.age);
}
}
}
class Student implements Comparable<Student>{
String name;
int age;
Student(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
@Override
public int compareTo(Student o) {
if(this.age>o.age)
return 1;
if(this.age==o.age){
return(this.name.compareTo(o.name));
}
else
return -1;
}
}
②当元素自身不具备比较性,或者具备的比较性不是所需要的。这时需要让容器自身具备比较性。当集合初始化时,就有了比较方式。定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。当两种排序都存在时,比较器为主。定义一个类,实现Comparator接口,覆盖compare方法。 import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetDemo {
/**
* 定义比较器,让集合具备比较性。
* 比较器类需要实现Comparator接口,并覆盖Compare()方法。
* 并比较器对象传递给集合。
*/
public static void main(String[] args) {
TreeSet<Person> ts = new TreeSet<Person>(new Comp());
ts.add(new Person("sixi",20));
ts.add(new Person("sixi",21));
ts.add(new Person("laomao",20));
ts.add(new Person("xiaohai",16));
Iterator<Person> it = ts.iterator();
while(it.hasNext()){
Person p = it.next();
System.out.println(p.getName()+">>>>>>"+p.getAge());
}
}
}
class Comp implements Comparator<Person>{//比较器
@Override
public int compare(Person p1, Person p2) {
int num = p1.getName().compareTo(p2.getName());
if(num==0)
return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
return num;
}
}
在集合框架中还有一中特殊的集合Map集合。 Map是一个接口,它与Collection接口没有关系。 Map集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性。
|——HashTable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0出现,效率比较低。
|——HashMap:底层是哈希表数据结构,允许使用null值和null键,该集合是不同步的。jdk1.2效率高
|——TreeMap:底层是二叉树数据结构。线程不同步。可以用于给Map集合中的键进行排序。
Map集合和Set集合很像,其实Map集合底层就是用的Set集合。
Map集合的增删改查:
1、添加。
put(K key,V value):添加键值对
putAll(Map<? extends K,? extends V>m):将一个Map集合中的所有元素存入另一个Map集合
2、删除。
clear():清空所有元素。
remove(Object key):删除指定键所对应的值。
3、判断。
containsValue(Object value):判断集合中存在的值。
containsKey(Object key):判断集合存在的键。
isEmpty():判断集合是否为空。
4、获取。
get(Object key):获取键所对应的值。
size():获取集合的大小。
values():将所有值存储到相应的Collection集合中。
entrySet():将Map集合中的元素以键值对关系类型的方式存储到Set集合中。
keySet():将集合中的所有的键存储到Set集合中。
Map集合有两种取出方式:
① Set<k> keySet:将Map中所有的键存入到Set集合。因为Set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 迭代Map集合
* 第一种方式:
* 将Map集合中key取出保存到Set集合中,再通过Set集合具备的迭代器迭代出Map集合的key值,然后通过key值取出Map集合相应的value值。
* */
public class MapDemo {
public static void main(String[] args){
Map<String,String> map = new HashMap<String,String>();
map.put("01", "sixi");
map.put("02", "xiaohai");
map.put("03", "xiaohe");
//取出所有的键存入Set集合中。
Set<String> st = map.keySet();
Iterator<String> it = st.iterator();
while(it.hasNext()){
String key = it.next();
String value = map.get(key);
System.out.println(key+":"+value);
}
}
}
② Set<Map.Entry<k,v>> entrySet:将map集合中的映射关系存入到了 set集合中,而这个关系的数据类型就是:Map.Entry 。 Map.Entry其实Entry也是一个接口,它是Map接口中的一个内部接口。
//Map.Entry关系实例代码:
interface Map
{
public static interface Entry
{
public abstract Object getKey();
public abstract Object getValue();
}
}
class HashMap implements Map
{
class Hahs implements Map.Entry
{
public Object getKey(){}
public Object getValue(){}
}
}
//Map集合的第二种取出方式:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapDemo1 {
/**
* Map的第二种取出方式:
*将Map集合中Map.Entry关系映射到Set集合中,在通过迭代器取出。
*/
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put("01", "zhangsan1");
map.put("02", "zhangsan3");
map.put("03", "zhangsan2");
Set<Map.Entry<String, String>> entrySet = map.entrySet();
Iterator<Map.Entry<String, String>> it = entrySet.iterator();
while(it.hasNext()){
Map.Entry<String, String> entry = it.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+":"+value);
}
}
}
【练习 】 每一个学生都有对应的归属地。
学生Student,地址String。
学生属性:姓名,年龄。
注意:姓名和年龄相同的视为同一个学生。
保证学生的唯一性。
1、描述学生。
2、定义map容器。将学生作为键,地址作为值。存入。
3、获取map集合中的元素。
import.java.util.*;
class Student implements Comparable<Student>//让元素具备比较性
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
//
public int compareTo(Student s)
{
int num = new Integer(this.age).compareTo(new Integer(s.age));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public int hashCode()
{
return name.hashCode()+age*34;
}
public boolean equals(object obj)
{
if(!(obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name) && this.age == s.age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public String toString()
{
return name+":"+age;
}
}
class MapTest
{
public static void main(String[] args)
{
HashMap<Student,String> hm = new HashMap<Student,String>();
hm.put(new Student("Lisi1",21),"beijing");
hm.put(new Student("Lisi2",22),"shanghai");
hm.put(new Student("Lisi3",23),"shanghai");
hm.put(new Student("Lisi4",24),"wuhan");
//第一种取出方式keySet
Set<Student> keySet = hm.keySet();
Iterator<Student> it = keySet.iterator();
while(it.hasNext())
{
Student stu = it.next();
String addr = hm.get(stu);
System.out.println(stu+":"+addr);
}
//第二种取出方式entrySet
Set<Map.Entry<Student,String>> entryset = hm.entrySet();
Iterator<Map.Entry<Student,String>> iter = entryset.iterator();
while(iter.hasNext())
{
Map.Entry<Student,String> me = iter.next();
Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu+"......."+addr);
}
}
}
【总结】 整个Java集合框架分为Collection集合和Map集合,实际它们都有个共同特性,它们的取出方式都最终依赖于Collection集合中的迭代器,Map集合实现将Map.Entry关系存入到Set集合,再用Set集合的迭代器取出。Collection接口延续的集合接口最常用的又分为List和Set,List又分为ArrayList、LinkedList和Vector集合,Vector集合是线程同步的,比较低效,所以Vector集合被ArrayList集合给替代了。ArrayList与LinkedList都各具自己的特点,当需要频繁的增删时选择用LinkedList集合,当需要频繁的查询时选用ArrayList集合,它们具有不同的数据结构才使得它们各有不同的特点。Set集合分为HashSet和TreeSet集合,它们的数据结构分别为哈希表和二叉树各具个的特色,不能存储相同的元素,通过hashCode()和equals()方法进行操作,让它们具有比较性。TreeSet集合中具有两种方式的比较性,第一种让元素就有表性,让元素类去实现Comparator接口,在Compare()方法中定义比较关系。第二种让集合具有比较性,给TreeSet集合传递一个Comparable接口实例,并在被覆盖CompareTo()方法中定义比较关系.Map集合中又分为HashTable、HashMap和TreeMap集合,HashTable集合数据结构为哈希表,但不可以存入null键null值,线程同步并且低效,所以不常用。HashMap集合也为哈希表结构,但可以存入null键null值,线程不同步,所以比较高效。TreeMap集合是二叉树结构,线程不同步,也是比较高效的。Map集合和Set很像,实际上Map集合的底层就是用Set集合,HashMap底层用的是HashSet集合,TreeSet集合底层用的是TreeMap集合。