集合框架————————java视频

来源:互联网 发布:redis hash key mysql 编辑:程序博客网 时间:2024/06/01 13:28

集合(java.util包中)又称为容器,数据在内存中存储方式称为数据结构,各自自身的特点不同,每一个容器对数据的存储方式不同,所以就出现了多钟容器,对多种进行共性抽取(不断向上抽取的往往是抽象的),即产生体系,称为集合框架。集合框架是用来表现和操纵集合的一个统一的体系结构。
抽取到顶层即collection,定义体系的最基本、最共性的功能。而使用一般是选择底层,因为1、顶层的抽象不能创建对象。2、创建子类对象的方法更多。看顶层基本共性,参阅底层创建对象。

共性方法:
1、add方法的参数是object,以便于接受任意类型的对象
2、集合中存储的都是对象的引用(地址)

迭代器:
迭代器就是集合的取出元素的方式,对于取出这个动作不足以用一个函数描述,需要用多个功能来体现,一般情况下,把多个功能封装到对象中去。每一个容器(如Set、List)都存在一个取出的对象(内部类操作集合中的元素更加方便),数据结构不同,每一个对象中取出的实现不用,但都有共性的内容,即判断和取出,将共性抽取形成了接口Iterator。
通过对外提供的方法iterator()获取集合的取出对象。

常见子接口
List集合:元素是有序的,元素可以重复。该集合体系有索引。
特有方法:凡是可以操作角标的方法
指定位置插入元素,可以通过索引获取,可以判断元素的位置,根据位置获取某一个元素,list集合特有的迭代器。可以改变一个位置的元素。

add(index,element);
addAll(index,Collection);

remove(index);
修改元素
set(index,element);
通过角标获取元素
get(index);
subList(from,to);包含from,不包括to
listIterator();

List集合特有的迭代器,ListIterator是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素,会发生并发修改异常,迭代过程中只能使用迭代器方法操作元素,可是Iterator方法是有限的,如果想要其他操作如添加,修改,就需要使用子接口,ListIterator。该接口只能通过List集合的listIterator方法获取。 List集合判断元素是否相同时(contains和remove),依据的是元素的equals方法(比较的都是地址)。

List中常见三个子对象(底层数据结构不一样)
1、ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快,增加删除比较麻烦。线程不同步。不断new数组产生可变长度数组。超过其默认创建数组长度(10)就开始百分之五十延长。

举例:去除ArrayListTest集合中的重复元素。

import java.util.*;class ArrayListTest{    public static void main(String[] args)    {        ArrayList al = new ArrayList();        al.add("java01");        al.add("java02");        al.add("java01");        al.add("java02");        al.add("java01");        al.add("java02");        sop(al);        al = singleElement(al);        sop(al);    }    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;    }    public static void sop(Object obj)    {        System.out.println(obj);    }}

举例2:将自定义对象作为元素存到ArrayList集合中,并且去除重复元素
比如:存人对象,同姓名,同年龄的视为同一个人,为重复元素。
思路:
1、对人描述,将数据封装进人对象
2、定义容器,将人存入
3、取出

class Person{    private String name;    private int age;    Person(String name, int age)    {        this.name = name;        this.age = age;    }    public boolean equals(Object obj)    {        if(!(obj instanceof Person))            return false;        Person p = (Person)obj;        return this.name.equals(p.name)&&this.age ==p.age;  //这里的equals是字符串(name)的equals方法    }    public String getName()    {        return name;    }     public int getAge()    {        return age;    }}class ArrayListTest2{    public static void sop(Object obj)    {        System.out.println(obj);    }    public static void main(String[] args)    {    ArrayList al = new ArrayList();    /*ArrayList容器中判断相同的条件是使用了equals,只判断存入的对象(Person)是否相同,new Person中默认来自Object,而Object中equals判断对象相同不相同使用的是地址,所以这里的对象的地址都不同,即无法删除相同元素,按照我们的方法比较,重写equals*/    al.add(new Person("lisi01",30));  //存对象 al.add(object obj)    al.add(new Person("lisi02",34));    al.add(new Person("lisi03",33));    al.add(new Person("lisi02",34));    al.add(new Person("lisi03",33));    al = singleElement(al);    Iterator it = al.iterator();    while(it.hasNext())    {        //sop(it.next().getName()+"::"+it.next().getAge());  /*getName不是Object(存对象时存入Object,迭代器不知道里面装的是什么元素,所以往回返的都是Object,it.next()往回返的时候返回的是Object)的方法,更改如下,用子对象特有方法时,向下转型*/        Person p = (Person)it.next();        sop(p.getName()+"::"+p.getAge());    }    }    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))             //contains调用equals方法,remove方法的底层调用的也是equals,所以重写equals时,对比的还是地址。            newAl.add(obj);    }}

2、LinkedList:底层使用的是链表数据结构。查询非常慢,但是增删方便
LinkedList特有方法:
addFirst();(1.6版本中变成了offerFirst())
addLast();(1.6版本中变成了offerLast())
getFirst();(1.6版本中变成了peekFirst())
getLast();(1.6版本中变成peekLast();在这个函数中,如果列表为空,返回null,但是getLast()中如果列表为空则返回没有这个元素的异常。即NoSuchElementException)
get方法,获取元素但是不删除元素,remove也可以获取元素但是元素被删除。
removeFirst();(1.6版本中变成pollFirst();集合中没有元素返回null)
removeLast();(1.6版本中变成pollLast();)
举例:使用LinkedList模拟一个堆栈或者队列数据结构
堆栈:先进后出,如同一个杯子
队列:先进先出,如同一个水管

/*为什么要封装?直接用LinkedList是可以完成的,但是它只有自身含义,我们想做成与项目相关的容器,起一些特定的名称来用,更加方便。这个时候把原有的集合封装进我们的描述当中,并对外提供一个自己更加清楚、更能识别的名称。*/import java.util.*;class Queue{    private LinkedList link;    Queue()    {        link = new LinkedList();    }    public void myAdd(Object obj)    {        link.addFirst(obj);    }    public Object myGet()    {        return link.removeLast();    }    public boolean isNull()    {        return link.isEmpty();    }}class LinkedListTest{    public static void main(String[] args)    {        Queue d1 = new Queue();        d1.myAdd("java01");        d1.myAdd("java02");        d1.myAdd("java03");        while(!d1.isNull())        {        System.out.println(d1.myGet());        }    }}//更改myGet中的removeLast为removeFirst则为堆栈,即先进后出

3、 Vector:底层是书序数据结构,Vector比ArrayList出现的早,线程同步,被ArrayList替代了,增删与查询都慢。不断new数组产生可变长度数组。超过其默认创建数组长度(10)就开始百分之百延长。枚举是Vector特有的取出方式,发现枚举和迭代器很像,其实是一样的,由于枚举的名称以及方法的名称都过长,所以被迭代器取代了,枚举郁郁而终了。

假使元素中涉及到挺多的增删,那么就考虑使用LinkedList,如果涉及增删不频繁,那么可以使用ArrayList和LinkedList,如果涉及增删也涉及查询,那么选用ArrayList,频繁的增删不多见。

Set集合:元素是无序的(存入和取出的顺序不一定一致,有序是怎么存入,怎么取出),元素不可重复。

Set集合的功能呢和Collection是一致的。
重点关注子类对象,常见的子类:HashSet(线程非同步的。底层数据结构是哈希表:保证集合不重复的依据是HashCode值是否相同,相同则继续判断equals方法是否为真)和TreeSet(可以对Set集合中的元素进行排序)

Hash表:Hash值一样再比较对象是否一样,不是同一个对象
,这个时候会在该地址下顺延。同一个地址,同一个对象时,则不存,即元素不可重复。Hash值不同,直接另存。

往hashset集合中存入自定义对象,姓名和年龄相同都为同一个人,重复元素。

import java.util.*;class HashSetTest{    public static void sop(Object obj)    {        System.out.println(obj);    }    public static void main(String[] args)    {        HashSet hs = new HashSet();        hs.add(new Person("a1",11));        hs.add(new Person("a2",4));        hs.add(new Person("a3",21));        hs.add(new Person("a4",13));    /*四个独立的new建立了四个新的地址,对应为其hash值,重新建立Person对象自己的hash值(复写hashCode),依据判断条件生成hash值*/        Iterator it = hs.iterator();        sop("a1:"+hs.contains(new Person("a2",4)));        hs.remove(new Person("a3",21))        while(it.hasNext())        {            Person p = (Person)it.next();            sop(p.getName()+"::"+p.getAge());        }    }}class Person{    private String name;    private int age;    Person(String name, int age)    {        this.name = name;        this.age = age;    }    public int hashCode()    {        System.out.println(this.name+"……hashcode");        //帮助查看hash值什么时候调用//return 60;        /*生成Hash值,equals运行,所有hash值都是60,所以在地址上依次比较对象内容,不相同的时候,在原位置下,顺延地址往下存。每一个hash值一样,这样是不合适的,按照条件设定hash值*/        return name.hashCode()+age;        //字符串本身有hashCode的方法,为保证Hash值唯一,可以将年龄乘上某一个数据    }    public String getName()    {        return name;    }     public int getAge()    {        return age;    }    public boolean equals(Object obj)    {        if(!(obj isntanceof Person))            return false;        Person p = (Person)obj;        System.out.println(this.name+"……equals……"+p.name);        return this.name.equals(p.name)&&this.age ==p.age;  //这里的equals是字符串(name)的equals方法    }}

HashSet如何保证元素的唯一性的?是通过元素的两个方法,hashcode和equals来完成的。如果元素的hashCode值相同,才会判断equals是否为true,如果元素的hashcode不同,才会调用equals
当自已定义对象时,要复写hashCode和equals,注意复写函数的时候不要把函数名和参数写错。对于判断元素是否存在,添加或者删除等操作,依赖的方法是元素的hashCode和equals方法,先判断hashCode,如果有,则判断equals。有可能对象要存放到HashSet中,如果不复写hashCode,那么存放的hashCode都依据地址来判断,如果都是new出来的对象,地址自然不同,那么就不会判断对象内容。hashCode和equals不是自己调用的,是底层调用。查看调用过程可以通过输出语句来看看。

TreeSet:TreeSet集合可以排序,但是你没有指定按照什么方式排序的时候,元素具备比较性才能排序,TreeSet的要求是存的对象必须具备比较性,如何具备比较性?实现接口Comparable(预先使用接口中的方法),只要实现接口就具备了比较性。Comparable接口强行对实现它的类的对象整体排序,这种排序称为类的自然排序。类的comparaTo方法被称为它的自然比较方法。复写compareTo函数,此函数判断比较的主要条件和次要条件记住,排序时,当主要条件相同时,一定要判断一下次要条件。
TreeSet底层的数据结构:二叉树,也叫红黑树,元素多的时候取中间值,方便比较大小。保证元素唯一性的依据是compareTo方法return 0.

TreeSet排序的第一种方式:让元素自身具备比较性,元素需要实现Comparable接口,覆盖compareTo方法,也称为元素的自然顺序,或者叫做默认顺序。元素一定义完,自身具有比较性。
TreeSet的第二种排序方式,当元素自身不具备比较性时,或者具备比较性不是所需要的,这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。即参与构造方法,定义比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
当两种排序都存在时,以比较器为主。定义一个类,实现Comparator接口,覆盖compare方法。

原创粉丝点击