---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
一、集合类概述
1、为什么出现集合类
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储。集合就是存储对象最常用的一种方式。
2、数组和集合类同是容器,两者有何区别
数组虽然也可以存储对象,但其长度是固定的,数组中可以存储基本数据类型;集合长度是可变的,集合只能存储对象。
3、集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
4、集合类的关系图
1)java集合的框架大致可分为两大类,一类是Collection,另一类是Map。Collection接口实现了Iterator接口,即迭代器接口,这意味着Collection接口的实现类可以调用Iterator接口的方法实现迭代器行为。Collection和Map的不同之处在于,Collection类集合存储单个对象,而Map类集合存储两个对象的键值对。
2)java提供了Collections、Arrays两个工具类,提供诸如集合排序、数组和集合的互换等功能。
3)Collection接口下又分为List接口与Set接口。List接口的主要实现类为ArrayList、LinkedList、Vector;Set接口的主要实现类为HashSet、TreeSet、LinkedHashSet。
二、Collection
1、常见操作
因为Collection为接口,不能建立对象,我们以ArrayList为例,
例1:
import java.util.*;
class Test
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
//打印集合
System.out.println("原集合:"+al);
//判断元素
System.out.println("java03是否存在:"+al.contains("java03"));
System.out.println("集合是否为空:"+al.isEmpty());
//获取个数,即集合长度
System.out.println("Size="+al.size());
//删除元素
al.remove("java02");
System.out.println("删除后集合:"+al);
//清空集合
al.clear();
System.out.println("清空后集合:"+al);
}
}
输出结果:
原集合:[java01, java02, java03]
java03是否存在:true
集合是否为空:false
Size=3
删除后集合:[java01, java03]
清空后集合:[]
2、迭代器
什么是迭代器呢?其实就是集合取出元素的方式。
例2:
import java.util.*;
class Test
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
al.add("AAA");
al.add("BBB");
al.add("CCC");
//获取迭代器
Iterator it=al.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
}
}
输出结果:
AAA
BBB
CCC
元素是有序的,元素可以重复。因为该集合体系有索引。
1、List特有方法:凡是可以操作角标的方法都是该体系特有的方法。
例3:
import java.util.*;输出结果:
class Test
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
//添加元素
al.add("java001");
al.add("java002");
al.add("java003");
al.add("java004");
al.add("java005");
//List特有方法
//在指定位置添加元素
al.add(1,"java");
//删除指定位置元素
al.remove(2);
//修改元素
al.set(2,"java007");
//通过角标获取元素
for (int x=0;x<al.size() ;x++ )
{
System.out.println("al("+x+")="+al.get(x));
}
}
}
al(0)=java001
al(1)=java
al(2)=java007
al(3)=java004
al(4)=java005
2、List特有的迭代器:ListIterator
在迭代时,不可以通过集合对象的方法操作集合中的元素,否则会发生ConcurrentModificationException(并发修改异常)。所以,在迭代时,只能用迭代器的方法操作元素。然而,Iterator提供的方法十分有限,只能对元素进行判断、取出及删除的操作。如果想要进行其他的操作,如添加、修改等,就需要使用其子接口:ListIterator。
该接口只能通过List集合的listIterator()方法获取。
例4:
import java.util.*;
class Test
{
public static void main(String[] args)
{
ArrayList al=new ArrayList();
//添加元素
al.add("java001");
al.add("java002");
al.add("java003");
al.add("java004");
al.add("java005");
//并发修改异常
/*
Iterator it=al.iterator();
while(it.hasNext())
{
Object obj=it.next();
if(obj.equals("java002"))
al.add("java007"); //错误,并发修改异常
}
*/
//利用ListIterator在迭代过程中,添加\修改\删除元素
ListIterator lit=al.listIterator();
while (lit.hasNext())
{
Object obj=lit.next();
if(obj.equals("java002"))
lit.add("java008"); //正确,不会发生并发修改异常
System.out.println("obj="+obj);
}
System.out.println(al);
}
}
输出结果:
obj=java001
obj=java002
obj=java003
obj=java004
obj=java005
[java001, java002, java008, java003, java004, java005]
Collection
|--List:元素是有序的,元素可以重复,因为该集合体系有索引。
|--ArrayList:底层使用数组数据结构。特点:查询速度很快,但是增删稍慢。线程不同步。
|--LinkedList:底层使用链表数据结构。特点:增删速度很快,查询稍慢。
|--Vector:底层使用数组数据结构。特点:线程同步,被ArrayList替代了。
1)ArrayList:
基于数组实现的,可以理解为可变数组,允许null元素,集合内部的元素顺序排列。
2)LinkedList:
基于双向链表实现的,允许null元素。通过提供特有的offerFirst()、peekFirst()、pollFirst()等方法,使LinkedList可用于模拟堆栈(stack)、队列(queue)或双向队列(deque)数据结构。
例5:使用LinkedList模拟一个队列数据结构。队列:先进先出
class DuiLie
{
private LinkedList ll;
DuiLie()
{
ll=new LinkedList();
}
public void add(Object obj)
{
ll.addFirst(obj);
}
public Object get()
{
return ll.removeLast();
}
public boolean isEmpty()
{
return ll.isEmpty();
}
public int size()
{
return ll.size();
}
public void sop()
{
for (int x=0;x<ll.size() ;x++ )
{
System.out.println(ll.get(x));
}
}
}
延伸阅读:请参阅When to use LinkedList<> over ArrayList<>?来了解,两种集合的使用场合。
3)Vector
Vector非常类似ArrayList,两者的主要区别在于,Vector是同步的,而ArrayList不同步。因此,在性能上Vector稍差于ArrayList。
4、List集合判断元素是否相同的依据
List集合根据元素的equals()来判断元素是否相同。contains()、remove()等方法在判断集合中的元素是否为目标元素时,也是调用equals()方法。
例6:
import java.util.*;
class Test
{
public static void main(String[] args)
{
//去除重复学生案例
ArrayList al=new ArrayList();
al.add(new Student("A",1));
al.add(new Student("A",1));
al.add(new Student("B",1));
al.add(new Student("B",1));
al.add(new Student("B",1));
al.add(new Student("B",2));
al.add(new Student("C",3));
al.add(new Student("C",3));
System.out.println("old:"+al);
al=SingleElement(al);
System.out.println("new:"+al);
}
//去除重复元素。此例证明,List集合通过equals()方法判断元素是否相同。
//即,contains(),remove()方法,底层调用的都是equals()方法;
public static ArrayList SingleElement(ArrayList al)
{
ArrayList newAl=new ArrayList();
Iterator it=al.iterator();
while (it.hasNext())
{
Object obj=it.next();
if(!newAl.contains(obj))
newAl.add(obj);
}
return newAl;
}
}
class Student
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写equals方法,学生的名字和年龄都相同时才认为两者相同
public boolean equals(Object obj)
{
if (!(obj instanceof Student))
return false;
Student st=(Student)obj;
return (this.name.equals(st.name))&&(this.age==st.age);
}
public String toString()
{
return "("+this.name+","+this.age+")";
}
}
输出结果:
old:[(A,1), (A,1), (B,1), (B,1), (B,1), (B,2), (C,3), (C,3)]
new:[(A,1), (B,1), (B,2), (C,3)]
四、Set
Set:元素是无序的(存入和取出的顺序不一定一致),元素不可以重复。
|--HashSet:底层使用哈希表数据结构。线程是非同步的
|--TreeSet:底层使用二叉树数据结构。可以对元素进行排序。
1、HashSet:
底层使用哈希表数据结构。线程是非同步的。
保证元素唯一性原理:判断元素的hashCode值是否相同,若相同,则继续通过equals方法判断元素是否相同。
两个元素相同,则它们的hashCode值一定相同,但若两个元素不相同,则它们的hashCode值不一定不相同。
例7:
import java.util.*;
class Test
{
public static void main(String[] args)
{
/*去除重复学生案例2
/*此例证明,HashSet集合通过hashCode()、equals()方法判断元素是否相同。
/*即,contains(),remove()方法,底层调用的都是这两个方法;
/******************************************************************/
HashSet hs=new HashSet();
hs.add(new Student("A",1));
hs.add(new Student("A",1));
hs.add(new Student("B",1));
hs.add(new Student("B",1));
hs.add(new Student("B",1));
hs.add(new Student("B",2));
hs.add(new Student("C",3));
hs.add(new Student("C",3));
System.out.println("Set:"+hs);
}
}
class Student
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写equals方法,学生的名字和年龄都相同时才认为两者相同
public boolean equals(Object obj)
{
if (!(obj instanceof Student))
return false;
Student st=(Student)obj;
return (this.name.equals(st.name))&&(this.age==st.age);
}
//复写hashCode方法
public int hashCode()
{
return name.hashCode()+age*37;
}
public String toString()
{
return "("+this.name+","+this.age+")";
}
}
输出结果:
Set:[(A,1), (B,1), (B,2), (C,3)]
2、TreeSet
底层使用二叉树数据结构。可以对元素进行排序。
保证元素唯一性的原理:compareTo()方法返回0,则认为两个元素相同。
TreeSet有两种排序方式:
1)元素自身具有比较性
方法:让元素所属的类继承Comparable接口,并重写compareTo()方法,则元素自身就具有了比较性。这种方式也称为元素的自然顺序。
例8:
import java.util.*;
class Test
{
public static void main(String[] args)
{
TreeSet ts=new TreeSet();
ts.add(new Student("asd",20));
ts.add(new Student("aasdfd",10));
ts.add(new Student("gedd",22));
ts.add(new Student("re",23));
ts.add(new Student("2013d",12));
ts.add(new Student("x",9));
System.out.println("排序后"); //即添加元素时,集合自动排序
Iterator it=ts.iterator();
while (it.hasNext())
{
Student s=(Student)it.next();
System.out.println(s.getName()+"..."+s.getAge());
}
}
}
//TreeSet第一种排序法,让元素具有比较性
class Student implements Comparable
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写compareTo方法,以学生的年龄为主要条件,姓名为次要条件,升序排序
public int compareTo(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException();
Student s=(Student)obj;
System.out.println(s.name+"..."+s.age);
int num=new Integer(this.age).compareTo(new Integer(s.age));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public String toString()
{
return name+"@"+age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
输出结果:
asd...20
asd...20
asd...20
asd...20
gedd...22
asd...20
aasdfd...10
asd...20
aasdfd...10
排序后
x...9
aasdfd...10
2013d...12
asd...20
gedd...22
re...23
由结果可知,1)TreeSet在添加元素时,会利用compareTo方法依次与集合中已有的元素进行比较;2)根据compareTo方法的返回值判断两个元素的先后顺序,从而完成排序。
2)让集合自身具有比较性
当元素自身不具有比较性,或者具备的比较性不是所需要的,这时就需要让集合自身具备比较性。
方法:自定义一个比较器类继承Comparator接口,重写compare()方法。
例9:
import java.util.*;
class Test
{
public static void main(String[] args)
{
TreeSet ts=new TreeSet(new MyComparator());//构造TreeSet时,传入自定义比较器
ts.add(new Student("asd",20));
ts.add(new Student("aasdfd",10));
ts.add(new Student("gedd",22));
ts.add(new Student("re",23));
ts.add(new Student("2013d",12));
ts.add(new Student("x",9));
System.out.println("排序后"); //即添加元素时,集合自动排序
Iterator it=ts.iterator();
while (it.hasNext())
{
Student s=(Student)it.next();
System.out.println(s.getName()+"..."+s.getAge());
}
}
}
//TreeSet第一种排序法,让元素具有比较性
class Student implements Comparable
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写compareTo方法,以学生的年龄为主要条件,姓名为次要条件,升序排序
public int compareTo(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException();
Student s=(Student)obj;
System.out.println(s.name+"..."+s.age);
int num=new Integer(this.age).compareTo(new Integer(s.age));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public String toString()
{
return name+"@"+age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
//TreeSet第二种排序方法,让集合具有比较性
class MyComparator implements Comparator
{
//重写compare方法,以学生名字的长度为主要条件,学生的年龄为次要添加,升序排序
public int compare(Object o1,Object o2)
{
Student s1=(Student)o1;
Student s2=(Student)o2;
int num=new Integer(s1.getName().length()).compareTo(new Integer(s2.getName().length()));
if(num==0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}
输出结果:
排序后由结果可知,TreeSet采用第二种排序方式时,是根据比较器中的compare()方法来对元素进行比较排序的。
x...9
re...23
asd...20
gedd...22
2013d...12
aasdfd...10
五、Map
Map:该集合存储键值对,而且键值不可重复。
|--HashTable:底层是哈希表数据结构,不可以存入null键和null值。该集合是线程同步的,效率低。
|--HashMap:底层是哈希表数据结构,允许存入null键和null值。该集合是不同步的,效率高。
|--TreeMap:底层是二叉树数据结构。线程不同步。可以给集合中的键进行排序。
Map和Set很像,其实Set的底层就是使用Map实现的。
1、基本功能
例10:
import java.util.*;
class Test
{
public static void main(String[] args)
{
//基本功能
HashMap<String,String> hm=new HashMap<String,String>();
//添加元素。如果添加时,键相同,则该键对应的值会覆盖原有键对应的值,并返回原来的值
System.out.println(hm.put("01","A"));
System.out.println(hm.put("01","D"));
hm.put("02","B");
hm.put("03","C");
System.out.println(hm.containsKey("01"));//判断是否包含某键
System.out.println(hm.get("01")); //通过键查找对应的值
Collection<String> coll=hm.values(); //获得Map集合中所有的值
System.out.println(coll);
System.out.println(hm);
}
}
输出结果:
null2、取出元素方式
A
true
D
[D, B, C]
{01=D, 02=B, 03=C}
Map集合提供两种取出方式:
1)Set<K> keySet():将Map中所有的键存入到一个Set集合。Set集合具备迭代器,可以通过迭代方式取出所有的键,然后再用get()方法获取每一个键对应的值。
2)Set<Map.Entry<K,V>> entrySet():将Map中的所有映射关系存入到一个Set集合中,而这个映射关系的数据类型就是:Map.Entry
例11:
import java.util.*;
class Test
{
public static void main(String[] args)
{
//学生案例
TreeMap<Student,String> hm=new TreeMap<Student,String>(new StuNameComparator());
hm.put(new Student("AAA",20),"BeiJing");
hm.put(new Student("B",22),"HangZhou");
hm.put(new Student("CC",23),"ChengDu");
//第一种取出方式
Set<Student> ks=hm.keySet(); //先获取Map集合的所有键的Set集合:keySet()
Iterator<Student> it=ks.iterator(); //获取迭代器
while (it.hasNext())
{
Student s=it.next();
String add=hm.get(s); //有了键,通过Map集合的get()方法获取其对应的值
System.out.println(s+"..."+add);
}
//第二种取出方式
Set<Map.Entry<Student,String>> es=hm.entrySet(); //将Map集合中的映射关系存入到Set集合中,entrySet()
Iterator<Map.Entry<Student,String>> it2=es.iterator();
while (it2.hasNext())
{
Map.Entry<Student,String> me=it2.next();
Student s=me.getKey(); //Map.Entry特有的获取键的方法
String str=me.getValue(); //Map.Entry特有的获取值的方法
System.out.println(s+"///"+str);
}
}
}
class Student implements Comparable<Student>
{
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 boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException();
Student s=(Student)obj;
return this.name.equals(s.name)&&this.age==s.age;
}
public int hashCode()
{
return name.hashCode()+10*age;
}
public int compareTo(Student s)
{
int num=this.name.compareTo(s.name);
if(num==0)
return new Integer(this.age).compareTo(new Integer(s.age));
return num;
}
public String toString()
{
return name+"@"+age;
}
}
class StuNameComparator implements Comparator<Student>
{
public int compare(Student s1,Student s2)
{
int num=new Integer(s1.getName().length()).compareTo(new Integer(s2.getName().length()));
if(num==0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
return num;
}
}
输出结果:
B@22...HangZhou
CC@23...ChengDu
AAA@20...BeiJing
B@22///HangZhou
CC@23///ChengDu
AAA@20///BeiJing
六、Collections与Arrays工具类
1、Collections
对Collection类集合进行操作的工具类。
常见方法:
static <T extends Comparable<? super T>> void sort(List<T> list) //对List进行排序例12:
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) //根据比较器的排序,返回集合中的最大元素
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) //以二分法的形式,在List集合中查找某元素
static <T> void fill(List<? super T> list, T obj) //将集合中的元素全部替换成指定元素
import java.util.*;
class CollectionsTest
{
public static void main(String[] args)
{
ArrayList<String> list=new ArrayList<String>();
list.add("fcg");
list.add("defd");
list.add("aa");
list.add("aaa");
list.add("d");
//sort()
System.out.println("排序前:"+list);
Collections.sort(list,new StrLenComparator());
System.out.println("排序后:"+list);
//反转
Collections.reverse(list);
System.out.println("reverse后:"+list);
//max()
System.out.println("默认max:"+Collections.max(list));
System.out.println("根据比较器的max:"+Collections.max(list,new StrLenComparator()));
//binarySearch()
System.out.println("aaa在第"+Collections.binarySearch(list,"aaa")+"个");
//fill(),全部替换
Collections.fill(list,"aa");
System.out.println("fill后:"+list);
//replaceAll
Collections.replaceAll(list,"aa","bb");
System.out.println("replaceAll后:"+list);
}
}
class StrLenComparator implements Comparator<String>
{
public int compare(String str1,String str2)
{
int num=new Integer(str1.length()).compareTo(new Integer(str2.length()));
if(num==0)
return str1.compareTo(str2);
return num;
}
}
输出结果:
排序前:[fcg, defd, aa, aaa, d]
排序后:[d, aa, aaa, fcg, defd]
reverse后:[defd, fcg, aaa, aa, d]
默认max:fcg
根据比较器的max:defd
aaa在第2个
fill后:[aa, aa, aa, aa, aa]
replaceAll后:[bb, bb, bb, bb, bb]
延伸阅读:
关于 Java Collections API 您不知道的 5 件事,第 1 部分与关于 Java Collections API 您不知道的 5 件事,第 2 部分
2、Arrays
用于操作数组的工具类。
我们只介绍其中一个:
数组变集合:
static <T> List<T> asList(T... a) //将数组转成集合将数组转成List集合后,可以使用集合的思想和方法操作数组中的元素,但不可以使用集合的增删方法。因为数组的长度是固定的。
集合变数组:
Collection中的方法:
public <T> T[] toArray(T[] a) //将集合转成数组将集合转成数组后,就限定了对元素的操作,不能再进行增删操作了。
例13:
import java.util.*;
class CollectionsTest
{
public static void main(String[] args)
{
//数组变集合
String[] arr={"a","bb","ccc"};
List<String> list=Arrays.asList(arr);
//list.add("dddd");//错误,不能使用集合的增删功能
ArrayList<String> newList=new ArrayList<String>(Arrays.asList(arr));
newList.add("dddd");//OK!
System.out.println(list);
//数组变集合2
int[] num={1,2,3};
System.out.println(Arrays.asList(num));//数组元素必须为对象,否则只将该数组作为集合中的元素存在
Integer[] num2={1,2,3};
System.out.println(Arrays.asList(num2));//这样就没问题了
//集合变数组
String[] arr1=list.toArray(new String[list.size()]);
for (String str:arr1)
{
System.out.print(str+" ");
}
}
}
输出结果:
[a, bb, ccc]
[[I@188edd79]
[1, 2, 3]
a bb ccc
延伸阅读:
欲深入了解ArrayList、HashMap、HashSet等的实现原理,请参考
深入Java集合学习系列:ArrayList的实现原理等系列。
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com