黑马程序员-Java 集合框架(一)-概述、List、Set

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

一、概述

集合框架是用于储存数组的容器,具有统一的父类接口Collection,主要的集合关系类如下图:

然而集合和数组的是有区别的,区别如下:
1:数组是固定长度的;集合可变长度的。
2:数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
3:数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。

集合类主要的区别是他们的数据结构有所不同。

二、Collection接口

Collection接口作为根接口,List、Set、Queue接口均直接继承自它。
Collection接口提供了一些集合的共性方法,如下:
1,添加:
add(object):添加一个元素
addAll(Collection) :添加一个集合中的所有元素。
2,删除:
clear():将集合中的元素全删除,清空集合。
remove(obj) :删除集合中指定的对象。注意:删除成功,集合的长度会改变。
removeAll(collection) :删除部分元素。部分元素和传入Collection一致。
3,判断:
boolean contains(obj) :集合中是否包含指定元素 。
boolean containsAll(Collection) :集合中是否包含指定的多个元素。
boolean isEmpty():集合中是否有元素。
4,获取:
int size():集合中有几个元素。
5,取交集:
boolean retainAll(Collection) :对当前集合中保留和指定集合中的相同的元素。如果两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。
6,获取集合中所有元素:
Iterator iterator():迭代器
7,将集合变成数组:
toArray();

三、Iterator接口

Iterator(迭代器)是一种集合的取出方式,用于集合的数据的遍历等。不同容器的数据结构不同,所以取出的动作实现不同,但是他们都共有内容判断和取出的方法,因此形成了一个接口Iterator。
这样做的好处是:如何实现的具体取出细节,这个不用关心,这样就降低了取出元素和具体集合的耦合性。
下面是一个通常用法:

public static void main(String[] args) {
    ArrayList alist = new ArrayList();

    alist.add("java01");
    alist.add("java02");
    alist.add("java03");
    alist.add("java04");
    for(Iterator it = alist.iterator(); it.hasNext();) {
              System.out.println(it.next());
         }
       /* 另一种循环方式
        Iterator it = coll.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
        */
}

四、List接口

List是Collection的子接口之一,具备了Collection的所有方法。
List的特点是:元素有序,可以重复,有索引。
List主要操作函数如下:
1.add(index, element)指定位置添加元素,addAll(index, Collection)指定位置添加一个集合的元素。
2.remove(index)删除指定为的元素。
3.set(index, element)修改指定位置的元素。
4.get(index)获取指定位置的元素。
5.subList(from, to)获取从[from]到[to-1]的子List。
6.listIterator()获取List特有的迭代器。

for(int x=0; x<list.size(); x++){
    system.out.print("get:"+list.get(x));
}

List拥有自己特有的迭代器:ListIterator
ListIterator主要函数为:
void add(E e); 将指定的元素插入列表(可选操作)。
boolean hasNext() ;以正向遍历列表时,如果列表迭代器有多个元素,则返回 true
boolean hasPrevious(); 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
E next() ;返回列表中的下一个元素。
int nextIndex() ;返回对 next 的后续调用所返回元素的索引。
E previous() 返回列表中的前一个元素。
int previousIndex() 返回对 previous 的后续调用所返回元素的索引。
void remove() 从列表中移除由 next 或 previous 返回的最后一个元素(可选操作)。 void set(E e) 用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。

ListIterator不仅可以正向遍历,而且也有逆向遍历功能。代码如下:

public static void main(String[] args) {

    ArrayList alist = new ArrayList();
    alist.add("L01");
    alist.add("L02");
    ListIterator it = alist.listIterator();
    while(it.hasNext()) {    //正向
        System.out.println(it.next());
    }
    while(it.hasPrevious()) {   //逆向
    System.out.println(it.previous());
    }
}

List有三个具体的子类,分别是ArrayList、LinkedList和Vector。

  1. ArrayList:
    底层数据结构为数组结构,线程非同步,查询速度快,增删速度慢,效率比Vector高。
    ArrayList是按照原数组的50%延长。构造一个初始容量为 10 的空列表。

  2. LinkedList
    底层数据结构为链表结构,线程非同步,查询速度慢,增删速度快。
    LinkedList的特有函数有:
    1.void addFirst(E e);添加到头部。
    2.void addLast(E e);添加到尾部。
    3.E getFirst();获取头部元素。
    4.E getLast();获取尾部元素。
    5.E removeFirst();删除头部元素,并返回元素。
    6.E removeLast();删除尾部元素,并返回元素。
    下面是我们通过linkedlist模拟栈进出的练习:

import java.util.*;
class QueueTest {
    private LstedList Lst;
    QueueTest() {
        Lst = new LinkedList();
    }
    public boolean isNull() {
        return Lst.isEmpty();
    }
    public void qAdd(Object obj) {
        Lst.addFirst(obj);
    }
    public Object qGet() {
        return Lst.removeFirst();
    }
}

class LstedListTest {
    public static void main(String[] args) {
        MyQueue q = new QueueTest();
        q.qAdd("L01");
        q.qAdd("L02");

        while(!q.isNull()) {  //先进后出
            System.out.println(q.qGet());
        }
    }
}
 3. Vector

Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe)。
Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。

Vector特有的取出方式是枚举(Enumeration),和迭代器方法类似,代码如下:

import java.util.*;
class LstedListTest {
    public static void main(String[] args) {
        Vector v = new Vector();
        v.add("L01");
        v.add("L02");

        Enumeration en = v.elements();
        while(en.hasMoreElements()) {     //通过枚举来输出
            System.out.println(en.nextElement());
        }
    }
}

五、Set接口

Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种,迭代器。
Set元素无序(存入与取出的顺序不一致),元素不可重复。
有两个主要子类:HashSet和TreeSet。

  1. HashSet:
    HashSet的底层数据结构为哈希表。
    哈希表的原理:
    1,对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。
    2,哈希值就是这个元素的位置。
    3,如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。
    4,存储哈希值的结构,我们称为哈希表。
    5,既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。

从上可知, HashSet是根据hashCode()和equals()方法进行存储。add时,如果是第一次添加返回true(添加成功),否则返回false(添加失败)。
所以当HashSet存储自定义对象时,需要覆盖hashCode和equals方法,当hashCode值相同、equals返回真时,认为是同一个对象。添加时,当hashCode值不同时,不会调用equals比较,当hashCode的值相同时,才会调用equals方法比较两个对象。
示例代码如下:

import java.util.*;
class Person {
    private String name;
    private int age;
    Person(String name,int age) {
        this.name = name;
        this.age = age;
    }

    public int hashCode() {
        return name.hashCode() + age*37;
    }

    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;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

class HashSetTest {
    public static void main(String[] args) {
        HashSet hs = new HashSet();
        hs.add(new Person("a1",11));
        hs.add(new Person("a2",12));
        hs.add(new Person("a2",12));//重复添加

        Iterator it = hs.iterator();
        while(it.hasNext()) {       // 遍历集合
            Person p = (Person)it.next();
            System.out.println(p.getName()+" "+p.getAge());
        }
    }
}

上例中重复添加的经过hashCode() 和equals()比较 返回true,所以添加失败,集合里一共有2个元素。

  1. TreeSet
    TreeSet的底层数据结构为二叉树,可以对集合中的元素进行排序。数据添加后就会进行排序。
    注意:如果元素不具备比较性,在运行时会发生ClassCastException异常。
    因此如果是自定义对象的排序的话,需要自定义对象自己实现Comparable接口或者提供比较器(Comparator接口)。 这两种方式区别如下:
    1:让元素自身具备比较性,需要元素对象实现Comparable接口,覆盖compareTo方法。
    2:让集合自身具备比较性,需要定义一个实现了Comparator接口的比较器,并覆盖compare方法,并将该类对象作为实际参数传递给TreeSet集合的构造函数。
    第二种方式较为灵活。

下面是第一种方法实例:

import java.util.*;
//学生类
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 hashCode()//复写hashCode   
    {
        return name.hashCode()+age;
    }   
    public boolean equals(Object obj)//复写equals
    {
        if(!(obj instanceof Student))
            throw new RuntimeException();
        Student s = (Student)obj;
        return this.name.equals(s.name)&&this.age==s.age;
    }

    public int compareTo(Object obj)//复写compareTo以便TreeSet集合调用
    {
        Student s=(Student)obj;
        if(this.age==s.age)
            return this.name.compareTo(s.name);
        return this.age-s.age;
    }
}

class  TreeSetTest
{
    public static void main(String[] args) 
    {
        TreeSet<Student> t=new TreeSet<Student>();
        t.add(new Student("wo1",12));
        t.add(new Student("wosj",2));
        t.add(new Student("wo",22));        
        for (Iterator<Student> it=t.iterator(); it.hasNext(); )
        {
            Student s=it.next();
            System.out.println(s.getName()+" "+s.getAge());
        }
    }
}

这种方式也被称为元素的自然顺序,或者叫做默认顺序。

第二比较方法实例如下:

import java.util.*;
//学生类
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 hashCode()//复写hashCode   
    {
        return name.hashCode()+age;
    }   
    public boolean equals(Object obj)//复写equals
    {
        if(!(obj instanceof Student))
            throw new RuntimeException();
        Student s = (Student)obj;
        return this.name.equals(s.name)&&this.age==s.age;
    }

    public int compareTo(Object obj)//复写compareTo以便TreeSet集合调用
    {
        Student s=(Student)obj;
        if(this.age==s.age)
            return this.name.compareTo(s.name);
        return this.age-s.age;
    }
}

class  TreeSetTest
{
    public static void main(String[] args) 
    {
        TreeSet<Student> t=new TreeSet<Student>(new CompareLen());//初始化创建时候就要把自定义的比较类添加进去
        t.add(new Student("wo1",12));
        t.add(new Student("wosj",2));
        t.add(new Student("wo",22));        
        for (Iterator<Student> it=t.iterator(); it.hasNext(); )
        {
            Student s=it.next();
            System.out.println(s.getName()+" "+s.getAge());
        }
    }
}

//定义一个比较器,以姓名长度为主要比较  
class CompareLen implements Comparator<Student>  
{  
    public int compare(Student s1,Student s2)  
    {  
        int num=new Integer(s1.getName().length()).compareTo(new Integer(s2.getName().length()));  
        return num;  
    }  
}

这种方法比较的一般都是元素不具备比较方式的时候,通过自定义比较器来实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值