集合框架总结
一、集合框架概述
1、集合的由来:
我们学习的是面向对象语言,而面向对象语言对事物的描述是通过对象体现的,为了方便对多个对象进行操作,我们就必须把这多个对象进行存储。而要想存储多个对象,就不能是一个基本的变量,而应该是一个容器类型的变量,在我们目前所学过的知识里面,有哪些是容器类型的呢?数组和StringBuffer。但是呢?StringBuffer的结果是一个字符串,不一定满足我们的要求,所以我们只能选择数组,这就是对象数组。而对象数组又不能适应变化的需求,因为数组的长度是固定的,这个时候,为了适应变化的需求,Java就提供了集合类供我们使用。2、数组和集合的区别?
A:长度区别:数组的长度固定,集合长度可变B:内容不同:数组存储的是同一种类型的元素,而集合可以存储不同类型的元素
C:元素的数据类型问题:数组可以存储基本数据类型,也可以存储引用数据类型,集合只能存储引用类型
根据存储元素的数据结构,有很多集合与之对应,我们把它们的共性不断的向上提取,最终就形成了集合的继承体系结构图。
3、集合的继承体系结构
请看下面的这张图:
集合体系理解:先看中间部分,超级类是collection,接口collection中有两个子接口,List和Set,Set:元素是无序的(取出和存入的顺序是不一致的),元素不可重复, Set集合的功能和Collection的功能是一样的 ,List中的元素是有序的,即有索引。List接口中有三个(一个图为画出)具体的类,ArrayList,LinkedList,Vector,ArrayList底层数据结构是基于数组的,LinkedList底层数据结构是基于链表的,Vector底层数据结构也是基于数组的,可以看到ArrayList和Vector的存储方式有重复,为什么呢?因为集合框架是从JDK1.2版本出现的,而Vector是从JDK1.0开始的,Vector是元老级别的,当然新版本的出现必须兼容老版本,所以有了新容器,Vector用的也就少了,其他两个容器选用哪一个,当然看需求,主要评估因素是集合增删查改的频繁程度,两个类除了拥有父类Collection中的方法外,也有自己的特性方法,不过大差不差。Set接口也有两个子类,HashSet和TreeSet,其中HashSet的底层数据结构是基于哈希表的,
TreeSet的底层数据结构是基于二叉树的。
二、集合类介绍
现在我们对集合的顶层接口Collection的功能进行一下归纳1、Collection的功能概述:
(1).添加功能
boolean add(Object obj):添加一个元素boolean addAll(Collection c):添加一个集合的元素
(2).删除功能
void clear():移除所有元素boolean remove(Object o):移除一个元素
boolean removeAll(Collection c):移除一个集合的元素(是一个还是所有)
(3).判断功能
boolean contains(Object o):判断集合中是否包含指定的元素boolean containsAll(Collection c):判断集合中是否包含指定的集合元素(是一个还是所有)
boolean isEmpty():判断集合是否为空
(4):获取功能
Iterator<E> iterator()(重点)(5).长度功能
int size():元素的个数面试题:数组有没有length()方法呢?字符串有没有length()方法呢?集合有没有length()方法呢?
(6).交集功能
boolean retainAll(Collection c):两个集合都有的元素?思考元素去哪了,返回的boolean又是什么意思呢?(7).把集合转换为数组
Object[] toArray()2、List的功能概述
(1).元素有序,可以重复
(2).注意事项:
对集合的操作方式有两种,第一种是集合自身具有的方法,第二种是用迭代器存放集合元素引用来操作集合,一个类同时具有两种操作方式,这就可能产生冲突。代码演示如下:
String s=“java”;
Iterator it=al.iterator();
While(it.hasNext)
{
Object obj=it.next();
If(obj.equals(s))
{
al.add(“java01”);
}
}
代码分析:
当Iterator it=al.iterator();执行时,迭代器就已经知道集合中所有元素的引 用,当 al.add(“java01”);再次添加元素时,迭代器是不知道的,这就造成了两 者的不一致,JVM会抛出 ConcurrentModificationException异常。因为迭代器仅有三个方法,我想要用迭代器方式进行添加怎么办呢?因为迭代器的本身局限性,就诞生出它的一个小弟,特地针对List.这个小弟就是ListIterator,它不仅可以实现add()方法,还可以正向和逆向遍历。
3、ArrayList的功能概述
(1).对于增删改查,Collection中的共性方法
方法说明:boolean add(Object obj);对于这个方法,是继承与Collection的,所以接收参数类型为Object类的比较合理,这个才可包容一切对象。
(2). 遍历用迭代器
Student stu=(Studeng)it.iterator();使用迭代器遍历对象时,必须进行强转,因为add()方法接收的是Object类,任何传入此方法中的类型都将得到提升,对象中的特性方法就不在具有,因此
想获取对象内部信息,就得强转,否则,多态编译失败。
(3).去除重复元素
boolean Contains(Object obj);方法原理:这个方法用来判断一个集合中是否包含特定对象,其底层依赖的是equals()方法,即通过对象的地址值比较两个对象是否相等,所以就要注意了,当你new 的
对象用add()添加时,对象都不相等,所以就不能去除,对此,我们可以覆盖我们自定义对象中的equals方法,指定比较的对象成员属性,当有多个属性时,
先比主要的,在比次要的。
(4).存储原理
底层数据结构是数组,数组是固定长度的,但是集合长度是要求可以改变的,所以JAVA采用了一种可变长度数组机制,new ArrayList()首先构造一个初始容量为 10 的空列
表,之后若空间不够,就会new一个新的数组,长度以50%延长,也就是变成15,再把
原来老数组中的元素copy进来,而Vector是以100%延长,所以ArrayList更节省空间
这也是Vector被淘汰的一个原因。除此,Vector是同步的,所以在多线程操作增删查
改的时候容易出现问题,而ArrayList是不同步的。
(5).ArrayList、Vector与LinkedList的比较。
从底层存储方式入手,ArrayList是基于数组的,LinkedList是基于链表的,所以对于ArrayList,查询速度很快,但是增删稍慢。线程不安全,效率高。对应的LinkedList增删速度很快,而查询速度稍慢,线程不安全,效率高。Vector底层数据结构是数组,查询快,增删慢。线程安全,效率低。这是选用容器的重要的衡量因素。
(6).实例代码
需求: 键盘录入多个数据,以0结束,要求在控制台输出这多个数据中的最大值
public class ArrayListDemo {
public static void main(String[] args) {
// 创建键盘录入数据对象
Scanner sc = new Scanner(System.in);
// 键盘录入多个数据,我们不知道多少个,所以用集合存储
ArrayList<Integer> array = new ArrayList<Integer>();
// 以0结束,这个简单,只要键盘录入的数据是0,我就不继续录入数据了
while (true) {
System.out.println("请输入数据:");
int number = sc.nextInt();
if (number != 0) {
array.add(number);
} else {
break;
}
}
// 把集合转成数组
// public <T> T[] toArray(T[] a)
Integer[] i = new Integer[array.size()];
// Integer[] ii = array.toArray(i);
array.toArray(i);
// System.out.println(i);
// System.out.println(ii);
// 对数组排序
// public static void sort(Object[] a)
Arrays.sort(i);
// 获取该数组中的最大索引的值
System.out.println("数组是:" + arrayToString(i) + "最大值是:"
+ i[i.length - 1]);
}
public static String arrayToString(Integer[] i) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int x = 0; x < i.length; x++) {
if (x == i.length - 1) {
sb.append(i[x]);
} else {
sb.append(i[x]).append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
4、Set的功能概述
1.set特点
元素无序(存入顺序与取出不一样),不可以重复(唯一性),其功能和collection 一样。
2、理解的两个儿子 HashSet、TreeSet。
2.1、HashSet的重要点介绍
a.底层数据结构是哈希表,哈希表就是存放一堆哈希值的表,如果元素哈希值相等且内容不同,在同一位置往下顺延,如果不等则直接往后存储。当然如果哈希值相等,元素内容也一样,则就不能存进去。b.唯一性是如何保证的?
HashSet的唯一性依赖两个它自动调用的方法,hashCode()和equals(),先比较对象的哈希值是否相等,如果相等,再调用equals(),如果值为true,就被踢出,不存入, 反之就存入。当然, 第一次调用hashCode()时返回的哈希值相等时,则再调用equals()。注意hashCode别写成hasCode。
c.主要方法
add(),clear(),contains(),size(),clone(),再加上迭代器的3个方法。contains()和remove()依赖的方法是hashCode和equals(),原理同上。
d.示例代码
需求:编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
public class HashSetDemo {
public static void main(String[] args) {
// 创建随机数对象
Random r = new Random();
// 创建一个Set集合
HashSet<Integer> ts = new HashSet<Integer>();
// 判断集合的长度是不是小于10
while (ts.size() < 10) {
int num = r.nextInt(20) + 1;
ts.add(num);
}
// 遍历Set集合
for (Integer i : ts) {
System.out.println(i);
}
}
}
2.2、TreeSet的重要点介绍
a.排序的第一种方式:
底层数据结构是二叉排序树,所以TreeSet可以对添加的对象进行自然排序,所以这就 要求我们添加的元素具有比较性,想要元素具有比较性,那就得实现Comparable接口并覆盖其compareTo()方法,这个方法自动被调用,让对象强制具有比较性,如果不实现将会抛出 ClassCastException异常,JAVA中很多类都已经实现了Comparable接 口,比如String实现Comparable后的compareTo方法就按照字母顺序来排序,但是 当我们自定义对象时,那就必须实现Comparable接口了。b.排序的第二种方式:
排序依据:如果元素不具有比较性或者具备的比较性不是所需要的时,可以使集合自身 具有比较性,在集合初始化时就具有了比较方式,所以这需要参与构造函数,定义比较 器,将比较器的对象传给TreeSet的构造函数。 备注:当两种排序方式都具备时,以比较器的方法为准c.比较器代码演示:
class Mycomp implements Comparator
{
public int compare(Object o1, Object o2) {
String s1=(String)o1;
String s2=(String)o2;
int num=s1.length()-s2.length();
if(num==0)
{
num=s1.compareTo(s2);
}
return num;
}
}
TreeSet ts=new TreeSet(new Mycomp());
d.实例代码
需求:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
备注:学生类在此省去
public class TreeSetDemo {
public static void main(String[] args) {
// 创建一个TreeSet集合
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 总分从高到低
int num = s2.getSum() - s1.getSum();
// 总分相同的不一定语文相同
int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
// 总分相同的不一定数序相同
int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2;
// 总分相同的不一定英语相同
int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3;
// 姓名还不一定相同呢
int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName())
: num4;
return num5;
}
});
System.out.println("学生信息录入开始");
// 键盘录入5个学生信息
for (int x = 1; x <= 5; x++) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第" + x + "个学生的姓名:");
String name = sc.nextLine();
System.out.println("请输入第" + x + "个学生的语文成绩:");
String chineseString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的数学成绩:");
String mathString = sc.nextLine();
System.out.println("请输入第" + x + "个学生的英语成绩:");
String englishString = sc.nextLine();
// 把数据封装到学生对象中
Student s = new Student();
s.setName(name);
s.setChinese(Integer.parseInt(chineseString));
s.setMath(Integer.parseInt(mathString));
s.setEnglish(Integer.parseInt(englishString));
// 把学生对象添加到集合
ts.add(s);
}
System.out.println("学生信息录入完毕");
System.out.println("学习信息从高到低排序如下:");
System.out.println("姓名\t语文成绩\t数学成绩\t英语成绩");
// 遍历集合
for (Student s : ts) {
System.out.println(s.getName() + "\t" + s.getChinese() + "\t"
+ s.getMath() + "\t" + s.getEnglish());
}
}
}
5、Map的功能概述
(1).public interface Map<K,V>
此集合存入的是关系,一对一对的,键值对应将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。(2).主要方法介绍:
V put(K key ,V value)添加元素对,如果是基于哈希表结构的,新值会覆盖旧值,并返回旧值
V remove ( Object key )
通过键,返回值,并从集合中移除
V get (Object key )
通过键,获取值,可以看出这有局限,每次只能取一个值
boolean containsKey( Object key )
通过键,判断是否包含值
Clooection <V> values()
取出集合中所有的值,存进Collection之中
(3).Map集合最重要的两个方法,即取出方法。
a、第一种:set<K> Keyset()从JDK文档看到,Map集合并没有迭代器,因为迭代器针对的是单例集合,而Map是一个双例集合,那么它的元素对是怎样被取出的呢?所以JAVA就想到了前者转化成后者,此时。
我们可以通过下面两个之中的任何一个方法。
set<K> Keyset()
返回Map集合的所有键,并存入到set集合中,再用迭代器的方法遍历集合, 同时用V V get(Object key),得到集合里面的值。
代码演示:
Map<String,String> m=new HashMap<String,String>();
m.put("1", "zhangsan1");。。。。。。
Set<String> keySet=m.keySet();
//对键值集合进行遍历
for(Iterator<String> it=keySet.iterator();it.hasNext();)
{
String str=it.next();
System.out.print(str+" ");
String s=m.get(str);
System.out.println(s);
}
b、第二种:set<map.Entry<K,V>> entrySet ()
把Map集合中的映射关系存入到set集合之中,对于这种映射关系,我们可以把它哦也看成一种数据类型,而这个类型就是map.Entry,只是这种数据类型很猛,保存的是数据关系。而对set所呈现的仍是一个类型。
代码演示:
Map<String,String> m=new HashMap<String,String>();
m.put("1", "zhangsan1");。。。。。。
set<map.Entry<String,String>> =map.entrySet();
Iterator<map.Entry<String,String>> it =m.itraror();
while(it.hasNext())
{
<map.Entry<String,String>> me=it.next();
String s1=me.getKey();
String s2=me.getValue();
… … … … …
}
(4)HashMap嵌套HashMap
一所大学有多个学院,一个学院有多个专业,专业实体类包括两个属性,专业名称,该专业的学费,示例代码如下
public class HashMapDemo {
public static void main(String[] args) {
// 创建学校集合对象
HashMap<String, HashMap<String, Integer>> universityMap = new HashMap<String, HashMap<String, Integer>>();
// 创建软件学院集合对象
HashMap<String, Integer> softCollegeMap = new HashMap<String, Integer>();
// 添加元素
softCollegeMap.put("软件工程", 10000);
softCollegeMap.put("信息与计算科学",5000);
// 把基础班添加到大集合
universityMap.put("softCollegeMap", softCollegeMap);
// 创建外语学院集合对象
HashMap<String, Integer> foreignMap = new HashMap<String, Integer>();
// 添加元素
foreignMap.put("英语", 4500);
foreignMap.put("日语", 4500);
// 把外语学院添加到大集合
universityMap.put("foreignMap", foreignMap);
//遍历集合
Set<String> universitySet = universityMap.keySet();
for(String universitySetMapKey : universitySet){
System.out.println(universitySetMapKey);
HashMap<String, Integer> universityMapValue = universityMap.get(universitySetMapKey);
Set<String> universityMapValueMapValueSet = universityMapValue.keySet();
for(String universityValueKey : universityMapValueMapValueSet){
Integer czbkMapValueValue = universityMapValue.get(universityValueKey);
System.out.println("\t"+universityValueKey+"---"+czbkMapValueValue);
}
}
}
}
6、Hashtable简单示例
public class HashtableDemo {
public static void main(String[] args) {
// HashMap<String, String> hm = new HashMap<String, String>();
Hashtable<String, String> hm = new Hashtable<String, String>();
hm.put("it001", "hello");
// hm.put(null, "world"); //NullPointerException
// hm.put("java", null); // NullPointerException
System.out.println(hm);
}
}
最后:花费了一天的时间将集合的内容简单的回顾了一下,集合在JAVA编程中的作用之大无可厚非,无论是写服务端的项目还是Android端的项目,集合都一直陪伴着我们,所以集合的体系结构一定要熟悉,在什么情况下选用什么集合要清楚,好啦,今晚就到这里。