黑马程序员-day14-集合框架(Colection)

来源:互联网 发布:长沙淘宝主播招聘 编辑:程序博客网 时间:2024/05/21 17:03
------- android培训、java培训、期待与您交流! ----------

集合类
思考:
1.为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
2.数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。

集合框架的构成及分类



集合概述:
Java是面向对象语言,如果我们要针对多个对象进行操作,就必须对多个对象进行存储。
而对多个元素进行存储,前面我们学习过数组,数组的弊端,长度固定。这样,数组将不能满足变化的要求。所以,Java就提供了集合供我们使用。

集合的特点:
1.长度可以发生改变。
2.只能存储对象。
3.可以存储多种类型对象(一般存储的还是同一种,因为1.5JDK的新特性 泛型)。

集合和数组的区别:
1.长度
  数组固定。
  集合可变。
2.存储元素
  数组可以是基本类型,也可以是引用类型。
  集合只能是引用类型。(JDK1.5以后还可以存储基本数据类型,因为JDK1.5自动装箱拆箱)
3.储存是否是同一类型
  数组元素类型一致。
  集合元素类型可以不一致。(建议还是同一种类型,因为JDK1.5出现了泛型)
4.功能是否一致
  数组只能对数据进行存取操作,当然删除数组中的元素还可以删除,就是null。
  集合不但可以对数据进行基本操作,还提供了更强大的功能,比如删除 修改...。

集合体系的由来
集合是存储多个元素的容器,但是,由于数据结构不同,Java就提供了多种集合类。
而这多种集合类有共性的功能,所以,通过不断的向上抽取,最终形成了集合体系
结构。

为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。
数据结构:数据存储的方式。

如何学习和使用一个继承体系呢?
学习顶层:因为顶层定义的是共性内容。
使用底层:因为底层才是具体的实现。
简单一句话:参阅顶层内容,建立底层对象。


集合框架中的常用接口
1.Collection接口有两个子接口:
  List(列表) ,Set(集)
2.List和Set的区别?
  List:可存放重复元素,元素存取是有序的。因为该集合体系有索引。
  Set:不可以存放重复元素,元素存取是无序的(存入和取出顺序不一定一致)。

Collection定义了集合框架的共性功能。
1.添加
add(e);
addAll(collection);
2.删除
remove(e);
removeAll(collection);
clear();
3.判断。
contains(e);
isEmpty();
4.获取
iterator();
size();
5.获取交集。
retainAll();
6.集合变数组。
toArray();

Collection共性功能实例
import java.util.List;import java.util.ArrayList;public class CollectionDemo{public static void main(String[] args){ArrayList al = new ArrayList();//添加一个元素al.add("张三");al.add("李四");al.add("王五");sop(al.get(1));//输出一个元素al.remove("王五");//删除一个元素sop(al.size());//打印下集合的长度 结果为2.因为删了一个al.clear();//清空集合sop(al.contains("王五"));//判断该列表中是否有这个元素sop(al.isEmpty);//判断该列表是否为空method();}//取交集和取交集public static void method(){List list1 = new ArrayList();list1.add("张三");list1.add("李四");list1.add("王五");List list2 = new ArrayList();list2.add("张三");list2.add("李四");list2.add("赵六");//list1.retainAll(list2);//取交集。拿list1元素跟list2的元素比,如果相同就保留。调用父类的方法//sop(list1);list1.removeAll(list2);//删除交集。拿list1的元素跟list2的元素比,如果相同就删除。调用父类的方法sop(list1);}public static void sop(Object obj){System.out.println(obj);}}
注意:
1.直接存储基本类型值也是可以的,因为JDK1.5后,有自动装箱技术,会将基本数据类转换成数据对象。JDK1.4绝对不行,因为集合中存放的对象的引用,当直接存放基本类型值的时候,会将这个基本类型值变成一个对象(自动装箱)。
2.集合对象中存储的其实都是元素对象的引用(地址)。
3.add方法的参数是Object类型可以接受所有类型的对象。但会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。集合框架中存放的都是对象的地址。当我们想用对象的特有方法的时候必须向下转型。当我们想直接打印一个集合,其实是调用了这个对象的toString方法,当我们要打印一个对象的长度的时候,首先我们会想到length方法。但是由于我们添加数据的时候用的add方法,而add方法的参数类型是Object,也就说把存进去的数据向上转型了。但是我们都知道Object是没有length方法,此时无法使用length方法。那么就要向下转型才可以,只有使用子类的特有方法size。


List接口中常用的类
List可存放重复元素,元素存取是有序的。因为该集合体系有索引。
1.ArrayList:底层是数组数据结构,查询速度很快,但是增删稍慢,线程不同步,默认10个元素。(线程不同步)
2.LinkedList:底层是链表数据结构,查询很慢,增删速度很快。(线程不同步)
3.Vector:底层是数组数据结构,和ArrayList一样,查询,增删,都很慢,Vector是1.0出现,ArrayList是1.2出现,(线程同步)但已经不用了。

取出List集合中元素的方式:
1.get(int index):通过脚标获取元素。
2.遍历取出:
  Iterator():通过迭代方法获取迭代器对象。
  ListIterator():Iterator()的子类,只能用于List集合。
注意:Iterator里面的方法是有限的,只能对元素进行判断,取出,删除的操作
如果想要其他的操作,如添加、修改等,就需要使用其子接口,ListIterator。

迭代器(Iterator)
1.迭代是取出集合中元素的一种方式。如同抓娃娃游戏机中的夹子。
2.因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。
3.用法:
  (1)Iterator iter = l.iterator();
     while(iter.hasNext()){
      System.out.println(iter.next());
     }
  (2)for(Iterator iter = iterator();iter.hasNext();){
      System.out.println(iter.next());
     }

迭代器的使用
1.使用步骤
  (1)通过集合对象获取迭代器对象。
  (2)通过迭代器对象判断。
  (3)通过迭代器对象获取。
2.迭代器原理
  由于多种集合的数据结构不同,所以存储方式不同,所以,取出方式也不同。这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种集合的时候,只要该集合内部实现这个接口即可。
3.集合的常见使用步骤
  (1)创建集合对象。
  (2)创建元素对象。
  (3)把元素添加到集合中。
  (4)遍历集合。
     通过集合对象获取迭代器对象。
     通过迭代器对象判断。
     通过迭代器对象获取。

遍历自定义对象实例(Iterator)
import java.util.ArrayList;import java.util.Iterator;/* * 遍历自定义对象(Iterator) *///学生类class Student{private String name;private int age;Student(String name, int age){this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}public class IteratorDemo {public static void main(String[] args) {ArrayList al = new ArrayList();al.add(new Student("张三", 20));al.add(new Student("李四", 21));al.add(new Student("王五", 22));for(Iterator it = al.iterator();it.hasNext();){Student stu = (Student)it.next();System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());}}}

Iterator迭代注意事项
1.迭代器是取出方式,会直接访问集合中的元素。所以将迭代器通过内部类的形式来进行描述。通过容器的iterator()方法获取该内部类的对象。
2.迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
3.迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
4.迭代器的next方法返回值类型是Object,所以要记得类型转换。
5.在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。

思考:为什么next方法的返回类型是Object的呢?
因为add方法的参数是Object类型可以接受所有类型的对象。会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。

列表迭代器(ListIterator)
1.由来:由于Iterator迭代器对数据的操作功能太少,只有对数据进行获取和删除等功能,所以就有了ListIterator迭代器。ListIterator迭代器不仅可以对迭代器进行获取和删除,还可以对迭代器进行添加和修改。
2.特有方法:
  (1)添加
add(E e):将指定的元素插入列表(可选操作)。
  (2)修改 
set(E e):用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。
  (3)逆向
hasPrevious():逆向遍历列表
previous():返回列表中的前一个元素。

遍历自定义对象及ListIterator特有方法实例(ListIterator)
import java.util.ArrayList;import java.util.ListIterator;/* * 遍历自定义对象,及ListIterator特有方法 *///学生类class Student{private String name;private int age;Student(String name, int age){this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}public class ListIteratorDemo {public static void main(String[] args) {ArrayList al = new ArrayList();al.add(new Student("张三", 20));al.add(new Student("李四", 21));al.add(new Student("王五", 22));ListIterator it = al.listIterator();//遍历元素sop(al);//正向遍历列表while(it.hasNext()){Student stu = (Student)it.next();if(stu.getName() == "李四"){//删除元素it.remove();//添加元素it.add(new Student("赵六", 24));}else if (stu.getName() == "王五") {//修改元素it.set(new Student("贺七", 23));}}System.out.println("修改后:");//遍历元素sop(al);System.out.println("逆向遍历列表:");//逆向遍历列表while(it.hasPrevious()){Student stu = (Student)it.previous();System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());}}//遍历元素,ArrayList的get方法public static void sop(ArrayList al){for(int x = 0; x<al.size(); x++){Student stu = (Student)al.get(x);System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());}}}

面试题:并发修改异常
并发修改异常的产生原因:用迭代器遍历集合,用集合去操作集合。
解决方案:
1.使用集合操作。
2.使用列表迭代器操作。


Vector-枚举(Enumeration)
1.特点:
特有的取出方式:枚举(Enumeration)
2.为什么枚举被迭代器替代了?
  因为枚举的名称以及方法的名称都过长。

Vector实例:
import java.util.*;class VectorDemo {public static void main(String[] args) {Vector v = new Vector();v.add("java01");v.add("java02");v.add("java03");v.add("java04");Enumeration en = v.elements();while(en.hasMoreElements()){System.out.println(en.nextElement());}}}


LinkedList
1.LinkedList的特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException

removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException

在JDK1.6出现了替代方法。
offerFirst();
offerLast();
peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。

pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。

LinkedList实例:
import java.util.*;class LinkedListDemo {public static void main(String[] args) {LinkedList link = new LinkedList();link.addLast("java01");link.addLast("java02");link.addLast("java03");link.addLast("java04");//sop(link);//sop(link.getFirst());//sop(link.getFirst());//sop(link.getLast());//sop(link.removeFirst());//sop(link.removeFirst());//sop("size="+link.size());while(!link.isEmpty()){//移除并返回此列表的最后一个元素sop(link.removeLast());}}public static void sop(Object obj){System.out.println(obj);}}

常见的数据结构
加钱:数据结构+算法+UML+设计模式。

数据结构:
栈,队列,数组,链表。
栈:先进后出。    如同一个杯子
队列:先进先出。如同一个水管
数组:查询快,增删慢。
链表:查询慢,增删快。


栈和队列实例(LinkedList模拟)
/*使用LinkedList模拟一个堆栈或者队列数据结构。*/import java.util.*;class DuiLie{private LinkedList link;DuiLie(){link = new LinkedList();}public void myAdd(Object obj){link.addFirst(obj);}public Object myGet(){return link.removeFirst();}public boolean isNull(){return link.isEmpty();}}class  LinkedListTest{public static void main(String[] args) {DuiLie dl = new DuiLie();dl.myAdd("java01");dl.myAdd("java02");dl.myAdd("java03");dl.myAdd("java04");while(!dl.isNull()){System.out.println(dl.myGet());}}}


Set接口中常用的类
元素是无序的(存入和取出顺序不一定一致),元素不可以重复。
1.HashSet:
  (1)定义:底层数据结构是哈希表。线程是不同步的,会出现安全问题,存取速度快。
  (2)HashSet的子类LinkedHashSet:有序。
  内部使用散列以加快查询速度,同时使用链表维护元素插入的次序,在使用迭代器遍历Set时,结果会按元素插入的次序显示。 
2.TreeSet:底层的数据结构是二叉树,线程是不同步的,可以对Set集合中的元素进行排序。
  (1)对Set集合中的元素的进行指定(我们指定的比较器)顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。
  
注意:
1.对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。
2.集合只能保存引用数据类型,也就是存储的是对象的地址值,而不是对象本身。集合中的元素相当于引用类型变量。
3.如果使用的集合涉及到了频繁的插入,建议使用LinkedList。


HashSet
import java.util.HashSet;import java.util.Iterator;public class HashSetDemo {public static void main(String[] args) {HashSet hs = new HashSet();hs.add("java-01");hs.add("java-02");hs.add("java-03");Iterator it = hs.iterator();while(it.hasNext()){sop(it.next());}}public static void sop(Object obj){System.out.println(obj);}}
思考:
1.HashSet是如何保证元素唯一性的呢?
  是通过两个方法,hashCode和equals来完成。如果元素hashCode值相同,才会判断equals是否为true。如果元素的hashCode不同,不会调用equals方法。
2.怎么重写hashCode()和equals()方法呢?
  (1)hashCode():把对象的所有成员变量值相加即可。如果是基本数据类型,就加值。如果是引用类型,就加哈希值。
return name.hasCode()+ age * 27;
  (2)equals(obj):先判断是否是同一类型的对象,再把传递进来的对象进行向下转型。再判断对象的内容是否相同。
if(!(obj instanceof Student)) ;
Student stu = (Student)obj; 
return this.name.equals(stu.name) && this.age == stu.age;
   所有成员变量的值比较。基本类型用==,引用类型用equals()。
注意:
1.对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
2.HashSet在存对象时,自动调用了HashCode()生成哈希码值,如果生成的哈希码值和该HashSet里存在元素的哈希码值相同,再自动调用equals(),反之则不调用equals()。


保证元素唯一性实例:
import java.util.HashSet;import java.util.Iterator;/*往hashSet集合中存入自定义对象,姓名和年龄相同为同一个人,重复元素。*///人类class Person{private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Person(String name, int age) {this.name = name;this.age = age;}public int hashCode(){HashSetTest.sop(name + ":hashCode");return name.hashCode()+age*50;}public boolean equals(Object obj){//判断是不是同一个类型的对象if(!(obj instanceof Person)){return false;}Person p = (Person)obj;HashSetTest.sop(name + " equals " + p.name);return this.name.equals(p.name) && this.age == p.age;}}public class HashSetTest {public static void main(String[] args) {HashSet hs = new HashSet();hs.add(new Person("张三", 20));hs.add(new Person("李四", 20));hs.add(new Person("张三", 20));hs.add("张三");Iterator it = hs.iterator();while (it.hasNext()) {Object obj = it.next();if(obj instanceof Person){Person p = (Person)obj;sop("name:" + p.getName() + ",age:" + p.getAge());}else {sop(obj);}}}public static void sop(Object obj){System.out.println(obj);}}


TreeSet
思考:
TreeSet的排序是如何进行的呢?
通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。

TreeSet集合两种实现排序方式:
1.自然排序(元素具备比较性)
  让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。也种方式也成为元素的自然顺序,或者叫做默认顺序。调用TreeSet的无参构造,要求对象所属的类实现Comparable接口。
2.比较器排序(集合具备比较性)
  当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。调用TreeSet的带参构造,要求构造方法接收一个实现了Comparator接口的对象。

自然排序实例:
import java.util.Iterator;import java.util.TreeSet;/** * 需求: * 1.演示TreeSet的一个特点:把Set集合中元素进行排序 * 2.往TreeSet集合中存储自定义对象学生。 *   想按照学生的年龄进行排序。 *  *   记住,排序时,当主要条件相同时,一定判断一下次要条件。 *///学生类class Student implements Comparable{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 int compareTo(Object obj){if(!(obj instanceof Student)){throw new RuntimeException("不是学生对象");}//向下转型Student stu = (Student)obj;System.out.println(this.name + "compareTo age" + stu.name);//比较    if(this.age > stu.age){    return 1;    }else if(this.age == stu.age){    return this.name.compareTo(stu.name);    }return -1;}}public class TreeSetDemo {public static void main(String[] args) {/*1.演示TreeSet的一个特点:把Set集合中元素进行排序TreeSet ts = new TreeSet();ts.add("abc");ts.add("abc1");ts.add("1abc");ts.add("bbc");//迭代器取出元素for(Iterator iterator = ts.iterator(); iterator.hasNext();){iptOut(iterator.next());}*///2.往TreeSet集合中存储自定义对象学生。TreeSet ts = new TreeSet();ts.add(new Student("张三", 18));ts.add(new Student("李四", 20));ts.add(new Student("王五", 29));ts.add(new Student("赵六", 18));//迭代器取出元素for(Iterator iterator = ts.iterator(); iterator.hasNext();){Student stu = (Student)iterator.next();out(stu.getName() + ":" + stu.getAge());}}public static void out(Object e){System.out.println(e);}}

比较器排序实例:
import java.util.Comparator;import java.util.Iterator;import java.util.TreeSet;/*当元素自身不具备比较性,或者具备的比较性不是所需要的。这时需要让容器自身具备比较性。定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。定义一个类,实现Comparator接口,覆盖compare方法。*///学生类,实现Comparable借口,使元素具备比较性class Student implements Comparable{private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}Student(String name, int age){this.name = name;this.age = age;}//根据学生的年龄来比较,当年龄相同时再比较姓名public int compareTo(Object obj){//判断是不是本类对象if(!(obj instanceof Student)){throw new RuntimeException("不是Student类对象");}//向下转型Student stu = (Student)obj;System.out.println(this.name + "compareTo age" + stu.name);//比较if(this.age > stu.age){return 1;}else if (this.age == stu.age) {System.out.println(this.name + "compareTo name" + stu.name);return this.name.compareTo(stu.name);}return -1;}}//比较器class MyComparator implements Comparator{public int compare(Object obj1, Object obj2){Student stu1 = (Student)obj1;Student stu2 = (Student)obj2;//先比较姓名,再比较年龄int num = stu1.getName().compareTo(stu2.getName());if(num == 0){if(stu1.getAge() > stu2.getAge()){return 1;}else if(stu1.getAge() < stu2.getAge()){return -1;}return 0;}return num;}}class TreeSetDemo2 {public static void main(String[] args) {TreeSet ts = new TreeSet(new MyComparator());ts.add(new Student("张三 ",20));ts.add(new Student("李四 ",19));ts.add(new Student("王五 ",22));ts.add(new Student("张三 ",20));Iterator it = ts.iterator();while (it.hasNext()) {Student stu = (Student)it.next();System.out.println("name:" +stu.getName() + ",age:" + stu.getAge());}}}
注意:当两种排序都存在时,以比较器为主。


0 0
原创粉丝点击