黑马程序员-【集合框架】

来源:互联网 发布:网络冗余设计 编辑:程序博客网 时间:2024/05/24 04:18

                                                        -------android培训java培训、期待与您交流! ----------

一、什么是集合框架

 

     如果要保存一组对象,按照之前的的做法只能使用对象数组,但是使用对象数组有一个限制,就是数组有长度的限制,而通过一些数据结构操作,如链表,可以完成动态数

组的操作,但这些如果全部由开发人员来做是比较麻烦的,

 

     集合框架的出现就解决的以上的难题,所谓的集合框架就是一个动态的对象数组,是对一些实现好的数据结构进行了包装,这样在使用时就会非常方便而且长度也不会像数

组那样有所限制,可见集合框架就是对一种对象容器,通过对一些数据结构的封装,可以很好的实现对对象的存取操作。

 

   集合类都位于Java.util包下,为解决线程并发问题Java5之后还在Java.util.concurrent包下提供了支持多线程的集合类。其与数组不一样,数组元素可以保存基本数据类型的

值,也可以是对象;而集合类只允许保存对象的引用变量。

 

    集合框架主要有两个接口:Collection和Map,这两个接口是java集合框架的根接口,这两个接口又包含了一些子接口或实现类,其中Collection接口的主要Set子接口和List子接口,其中Set中数据是无序,且不可重复的,List中的数据有序,且可以重复。Map代表具有映射关系的集合,有两个具有映射关系的数据组成,

 

二、集合类之间的层次关系

 

   Collection


         |--List:元素是有序的,元素可以重复。因为飞机和体系有索引。

                |--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度快,但是增删稍慢。

                |--LinkedList:底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。

                |--Vector:底层是数组结构,线程同步,被ArrayList替代了,因为效率低。


         |--Set:元素是无序的(存入和取出的顺序不一定一致),元素不可以重复。

                 |--HashSet:底层数据结构是哈希表。HashSet通过hashCode和equals判断元素是否重复。如果元素的hashCode值相同,才会判断equals是否为true。如果元素的

hashCode值不同,不会调用equals。对于判断元素是否存在,以及删除等操作依赖的方法是元素的hashCode和equals方法。

                 |--LinkedSet:通过hashcode确定元素的存储位置,同时通过链表确定元素的顺序。

                 |--TreeSet:可以对Set集合中的元素进行排序,底层数据结构是二叉树。它保证元素唯一性的依据是compareTo方法的return。TreeSet默认顺序采用的是红黑树。

TreeSet允许自定义排序方式:当元素自身不具备比较性时,或者具备的比较性不是所需的,这是就需要让集合自身具备比较性,在集合初始化时就让其具有比较性方式。

                 |--EnumSet:专门为枚举类设计的集合类,其中所有的元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式的指定。EnumSet的集合元素

也是有序的,EnumSet以枚举值在Enum内部的定义顺序来决定集合元素的顺序


        |--Map:用于保存具有映射关系的数据。


           |--HashMap:底层数据结构是哈希表。HashMap通过hashCode和equals判断元素是否重复。如果元素的hashCode值相同,才会判断equals是否为true。如果元素的

hashCode值不同,不会调用equals。

           |--TreeMap:可以对Map集合中的元素进行排序,底层数据结构是二叉树。它保证元素唯一性的依据是compareTo方法的return。TreeSet默认顺序采用的是红黑树。

TreeSet允许自定义排序方式:当元素自身不具备比较性时,或者具备的比较性不是所需的,这是就需要让集合自身具备比较性,在集合初始化时就让其具有比较性方式。

           |--EnumMap:与枚举类一起使用的Map实现,EnumMap中的所有KEY都必须是单个枚举类的枚举值。创建EnumMap的时候必须显式或隐式的指定它对应的枚举类。

           |--Queue:用于模拟队列这种数据结构,不允许随机访问。

           |--PriorityQueue:数据的存储不按照加入队列的顺序,而是按照队列元素的大小重新排序。因此,当取出队列元素时,取出的并不是最先加入队列元素而是最小的元素。

           |--ArrayQueue:基于数组实现的是双端队列。

 

三、集合类常见方法

 

1、Collection接口的常见方法

1)添加


Boolean add (Object obj);
Boolean add(Collection cool)

2)删除


Boolean remove (object obj)
Boolean removeAll(Collection coll)
Void clear();

3)判断


Boolean contains(Object obj)
Boolean containsAll(collection coll)
Boolean IfEmpty():判断集合中是否有元素

4)获取


Int size();
Iteratot iterator() :取得元素的方法  迭代器

该对象必须依赖于具体容器,因为每一个容器的数据结构都不同,所以该迭代器对象是在容器中进行内部实现的,对于使用容器者而言,具体的实现不重要,只要通过容器获取

到该实现的迭代器的对象即可,也就是iterator方法

Iterator接口就是对所有的Collection容器进行元索取出的公告接口

5)其他


Boolean retainAll(Collection coll) 取得交集
Object[] toArray() 将集合转成数组

 

2、list特有的常见方法:有一个同性特点就是可以操作角标

1)添加


Void add(index,element)
Void add(index,collection)

2)删除


Object remove(index,element)

3)修改


Object set(index,element)

4)获取


Object get(index)
Int indexOf(object)
Int lastIndexof(object)
List subList(from,to)


3、LinkedList 基本方法:


  1、addFirst                                                 2、addlast 


  3、getFirst(获取第一个但是不删除 没有第一个元素时抛出异常)   4、getLast


  5、removeFirst(获取并删除)


  1.6版本的新特性 :


peekFirst与getFirst的区别是取得元素为空时,不会抛出异常

集合类的方法如下:

import java.util.*;/* 1.add方法的参数类型是Object,以便于接受任意类型对象 2.方法中存储的都是对象的引用或者地址 */class CollectionDemo {public static void main(String[] args) {// method_2();// base_method();get_method();}public static void sop(Object obj) {System.out.println(obj);}public static void base_method() {// 创建一个集合容器,使用Collection接口的子类:ArrayListArrayList al = new ArrayList();// 1,添加元素al.add("java01");// add()参数类型是Object类型的,因为可以是任意对象al.add("java02");al.add("java03");al.add(4);sop("原集合:" + al);// 2.获取个数,集合长度size();sop("size=" + al.size());// 3.删除元素al.remove("java02");sop("改变后的集合:" + al);al.clear();// 清空集合sop(al);// 4.判断集合是否为空sop("判断元素是否存在" + al.contains("java01"));sop("判断集合是否为空" + al.isEmpty());}public static void method_2() {ArrayList al1 = new ArrayList();al1.add("java01");// add()参数类型是Object类型的,因为可以是任意对象al1.add("java02");al1.add("java03");ArrayList al2 = new ArrayList();al2.add("java05");// add()参数类型是Object类型的,因为可以是任意对象al2.add("java06");al2.add("java04");al2.retainAll(al1);// 取交集,al1中只会保留和al2中相同的元素sop("al1:" + al1);sop("al2:" + al2);}public static void get_method() {ArrayList al = new ArrayList();al.add("java01");al.add("java02");al.add("java03");al.add(4);/* * Iterator it = al.iterator();//返回的是Iterator接口类型的对象,Iterator是一个集合的内部类 * while (it.hasNext()) { sop(it.next()); } */for (Iterator it = al.iterator(); it.hasNext();)// 这样写不用在迭代结束后还保留it对象,因为it是for循环的一个内部变量,便于内存管理{sop(it.next());}}}
import java.util.*;/* 1.add方法的参数类型是Object,以便于接受任意类型对象 2.方法中存储的都是对象的引用或者地址 */class CollectionDemo {public static void main(String[] args) {// method_2();// base_method();get_method();}public static void sop(Object obj) {System.out.println(obj);}public static void base_method() {// 创建一个集合容器,使用Collection接口的子类:ArrayListArrayList al = new ArrayList();// 1,添加元素al.add("java01");// add()参数类型是Object类型的,因为可以是任意对象al.add("java02");al.add("java03");al.add(4);sop("原集合:" + al);// 2.获取个数,集合长度size();sop("size=" + al.size());// 3.删除元素al.remove("java02");sop("改变后的集合:" + al);al.clear();// 清空集合sop(al);// 4.判断集合是否为空sop("判断元素是否存在" + al.contains("java01"));sop("判断集合是否为空" + al.isEmpty());}public static void method_2() {ArrayList al1 = new ArrayList();al1.add("java01");// add()参数类型是Object类型的,因为可以是任意对象al1.add("java02");al1.add("java03");ArrayList al2 = new ArrayList();al2.add("java05");// add()参数类型是Object类型的,因为可以是任意对象al2.add("java06");al2.add("java04");al2.retainAll(al1);// 取交集,al1中只会保留和al2中相同的元素sop("al1:" + al1);sop("al2:" + al2);}public static void get_method() {ArrayList al = new ArrayList();al.add("java01");al.add("java02");al.add("java03");al.add(4);/* * Iterator it = al.iterator();//返回的是Iterator接口类型的对象,Iterator是一个集合的内部类 * while (it.hasNext()) { sop(it.next()); } */for (Iterator it = al.iterator(); it.hasNext();)// 这样写不用在迭代结束后还保留it对象,因为it是for循环的一个内部变量,便于内存管理{sop(it.next());}}}


 

import java.util.*;/* 1.add方法的参数类型是Object,以便于接受任意类型对象 2.方法中存储的都是对象的引用或者地址 */class CollectionDemo {public static void main(String[] args) {// method_2();// base_method();get_method();}public static void sop(Object obj) {System.out.println(obj);}public static void base_method() {// 创建一个集合容器,使用Collection接口的子类:ArrayListArrayList al = new ArrayList();// 1,添加元素al.add("java01");// add()参数类型是Object类型的,因为可以是任意对象al.add("java02");al.add("java03");al.add(4);sop("原集合:" + al);// 2.获取个数,集合长度size();sop("size=" + al.size());// 3.删除元素al.remove("java02");sop("改变后的集合:" + al);al.clear();// 清空集合sop(al);// 4.判断集合是否为空sop("判断元素是否存在" + al.contains("java01"));sop("判断集合是否为空" + al.isEmpty());}public static void method_2() {ArrayList al1 = new ArrayList();al1.add("java01");// add()参数类型是Object类型的,因为可以是任意对象al1.add("java02");al1.add("java03");ArrayList al2 = new ArrayList();al2.add("java05");// add()参数类型是Object类型的,因为可以是任意对象al2.add("java06");al2.add("java04");al2.retainAll(al1);// 取交集,al1中只会保留和al2中相同的元素sop("al1:" + al1);sop("al2:" + al2);}public static void get_method() {ArrayList al = new ArrayList();al.add("java01");al.add("java02");al.add("java03");al.add(4);/* * Iterator it = al.iterator();//返回的是Iterator接口类型的对象,Iterator是一个集合的内部类 * while (it.hasNext()) { sop(it.next()); } */for (Iterator it = al.iterator(); it.hasNext();)// 这样写不用在迭代结束后还保留it对象,因为it是for循环的一个内部变量,便于内存管理{sop(it.next());}}}

 

四、集合类的一些特点

 

1、栈和队列的特点

 

      堆栈:先进后出 First In Last Out  FILO

      队列:先进先出 First In First Out FIFO

 

 

2、set集合:元素不可以重复,是无序的。

 

     set接口中的方法和Collection一致

 

    1)hashset :内部结构是hash表 是不同步的

    2)treeset:

 

    hash表内部构成:

           根据专门的算法(hash算法)确定位置

 

    hash表如何判断元素是否相同

          1、判断两个元素的哈希值是否相同

          2、当哈希值相同时再判断两个元素内容是否相同

 (判断哈希值相同,用的是对象的hashcode方法,判断内容用的是equals方法 )

 

3、自己定义的类,应该重新 hashcode() 和 equals() 方法

 

    contains方法也是根据equals方法判断元素是否相同

  (不同对象的判断方法是不同的)

 

    Hashset子类LinkedHashSet:是哈希表和链表的结合

 

4、TreeSet的特点:

 

         可以对Set集合中的元素进行制定排序,是不同步的

         TreeSet集合不根据hashset和equals判断元素唯一性,而是根据比较方法返回结果是否为0,是0就是相同元素,不存

 

         TreeSet对元素进行排序的方式一:

              让元素自身具备比较功能,就需要实现comparable接口,和覆盖compareTo方法

 

       如果不要按照对象中具备的自然顺序进行排序,如果对象中不具备自然排序,怎么办?

 

       可以使用TreeSet集合第二种排序方式二:

       让集合自身具备比较功能,定义一个类实现comparator接口,覆盖compare方法。将该类对象作为参数传递给TreeSet集合的构造函数

 实例:

import java.util.Comparator;import java.util.HashSet;import java.util.Iterator;import java.util.TreeSet;public class HashSetTest {public static void main(String[] args) {Per p1 = new Per(12);p1.idCard = "1";Per p2 = new Per(33);p2.idCard = "1";HashSet<Per> hs = new HashSet<Per>();hs.add(p1);hs.add(p2);p2.idCard = "3";System.out.println(hs.size());Iterator<Per> it = hs.iterator();while (it.hasNext()) {System.out.println(it.next());}Per p3 = new Per(22);Per p4 = new Per(15);Per p5 = new Per(17);// 实现 Comparator接口 设置排序方式TreeSet<Per> ts = new TreeSet<Per>(new Comparator<Per>() {@Overridepublic int compare(Per o1, Per o2) {// TODO Auto-generated method stubreturn o1.age - o2.age;}});ts.add(p1);ts.add(p2);ts.add(p3);ts.add(p4);ts.add(p5);System.out.println(ts.size());Iterator<Per> it2 = ts.iterator();while (it2.hasNext()) {System.out.println(it2.next());}}}class Per {String idCard;String name;int age;public Per() {}public Per(int age) {this.age = age;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + ((idCard == null) ? 0 : idCard.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Per other = (Per) obj;if (idCard == null) {if (other.idCard != null)return false;} else if (!idCard.equals(other.idCard))return false;return true;}@Overridepublic String toString() {return "Per [idCard=" + idCard + ", name=" + name + ", age=" + age + "]";}}

 

Map集合

 

一次添加一对元素,Collection一次添加一个元素

map也可以成为双列集合,Collection集合成为单例集合

其实map集合中存储的就是键值对

map集合中必须保证键的唯一性

 

常用方法:

 

1、添加

 

Value put():返回前一个和key关联的值,如果没有返回null

 

2、删除

 

Void clear();清空map集合

Value remove(key) 根据制定的key删除这个键值对

 

3、判断

 

Boolean containsKey(key)

Boolean containsvalue(value)

           boolean isEmpty();

 

4、获取

 

Value get(key) :通过键获取值,如果没有该键返回null

当然可以通过返回null,来判断是非包含制定键

Int size():获取键值对的个数

 

 Map常用的子类

 

|--HashTable :内部结构是哈希表,是同步的。不允许null作为建,nul作为值

                         |--LinkedHashMap:是有序的

 

  |--Properties:用来储存键值对型的配置文件的信息,可以和IO技术相结合

 

|--HashMap:内部结构是哈希表,不是同步的。允许null作为键,null作为值

     |--TreeSet:内部结构是二叉树,不是同步的。可以对Map集合中的键进行排序。

 

Map集合的取出原理:将map集合转换成set集合再通过迭代器取出

 

import java.util.*;public class MapDemo2 {public static void main(String[] args) {Map<String, String> map = new HashMap<String, String>();map.put("02", "zhangshan02");map.put("03", "zhangshan03");map.put("01", "zhangshan01");map.put("04", "zhangshan04");// 先获取map集合的所有键的Set集合,keySet();Set<String> keySet = map.keySet();// 有了Set集合,就可以获取其迭代器Iterator<String> it = keySet.iterator();while (it.hasNext()) {String key = it.next();// 有了键可以通过map集合的get方法获取其对应的值String value = map.get(key);System.out.println("key--" + key + ",value" + value);}}}


五、新特性

 

1、泛型



JDK1.5出现的新特性,安全机制



好处:

1、将运行时期的问题ClassCastException转到了编译时期

2、避免了强制转换的麻烦。

3、提高了编译时期的安全



<>什么时候用?当操作的引用数据类型不确定的时候,就使用<>,将要操作的引用数据类型传入即可,其实<>就是一个用于接收具体引用数据类型的参数范围。



在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型。



泛型技术是给编译器使用的技术,用于编译时期,确保了时期的安全



运行时,会将泛型去掉,生成的Class文件中是不带泛型的,这个称为泛型的擦除。

为什么擦除呢?因为为了兼容运行时的类加载器,为了解决擦除后的强转类型问题,增加了补偿程序,根据传入的元素类型,进行转换



泛型的补偿:在运行时,通过获取元素的类型进行转换动作,不用使用者再强制转换了。



泛型在集合中的应用比较广泛,其他应用较少



在JDK1.5后,使用泛型未接受类中要操作的引用数据类型。

泛型类。什么时候用?当类中的操作的引用数据类型不确定的时候,就使用泛型来表示。



可以将将泛型定义在方法上



当方法静态时,不能访问类上定义的泛型,如果静态方法使用泛型,只能讲泛型定义在方法上



泛型接口,将泛型定义在接口上



泛型可以对类型进行限定

       ? Extend E,接收E类型或者E的子类型对象。上限!

       ? Super E:接收E类型或者E的父类型,下限



什么时候用下线呢?通常对集合中的元素进行取出操作时,可以使用下限

2、Foreach 语句:

 

格式:For(类型 变量 :Collection集合|数组)

传统for和高级for的区别?

传统for可以完成对语句执行很多次,因为可以定义控制循环的增量和条件。

 

高级for是一种简化形式。

它必须有被遍历的目标。该目标要是数组,要么是Collection单例集合

 

对数组的遍历如果仅仅是获取数组中的元素,可以使用高级for

如果要对数组的角标进行操作建议使用传统for

 

3、函数的可变参数。

其实就是就是一个数组,但是接收的是数组的元素

自动将这些元素封装成数组,简化了调用者的书写

 

注意:可变参数类型,必须定义在参数列表的结尾


六、常见问题


1、两个对象值相同(x.equals(y)== true),但却可有不同的hashcode,这句话对不对?

 

对。

如果对象要保存在HashSet或HashMap中,它们的equals相等,那么,它们的hashcode值就必须相等。

如果不是要保存在HashSet或HashMap,则与hashcode没有什么关系了,这时候hashcode不等是可以的,例如arrayList存储的对象就不用实现hashcode,当然,我们没有理由不实现,通常都会去实现的。

 

2、TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常!

 

public class Parent implements Comparable {private int age = 0;public Parent(int age){this.age = age;}public int compareTo(Object o) {// TODO Auto-generated method stubSystem.out.println("method of parent");Parent o1 = (Parent)o;return age>o1.age?1:age<o1.age?-1:0;}}public class Child extends Parent {public Child(){super(3);}public int compareTo(Object o) {// TODO Auto-generated method stubSystem.out.println("method of child");//Child o1 = (Child)o;return 1;}}public class TreeSetTest {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubTreeSet set = new TreeSet();set.add(new Parent(3));set.add(new Child());set.add(new Parent(4));System.out.println(set.size());}}


 

0 0