黑马程序员-Java基础:集合

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

集合总结


一、集合概述
1.概述集合是Java中用于存储对象的容器,由于Java是面向对象的语言,为了方便对多个对象进行操作,同时解决数组长度固定,不能满足变化需求的问题,因此提供了集合类。
集合的两个关键特点
<1>长度可变
<2>可以存储任意类型的对象(不可以存储基本数据类型,但可以存储基本数据类型包装类)
2.集合和数组的对比
由于数组也是可以存储对象的容器,但相对于集合来说在存储数据方面就有了一些局限性。
<1>数组的存储元素的个数是固定的,而集合可以存储任意多个元素,即长度可变
<2>数组中可以存储基本类型变量和引用类型变量,集合中只能存储引用类型变量
<3>数组中的元素必须是相同类型,集合中的元素可以是不同类型

二、集合的继承体系
由于集合是用来存储和操作多个元素的容器,因此不同类型的元素存储在集合中的数据结构可能会有差异,操作元素的方式也可能会不同,因此Java提供了底层数据结构不同的类,并将集合中操作元素的共性方法抽取称为接口,因此出现了集合的继承体系。
集合继承体系图
集合继承体系图
上图中虚线框代表接口,实线框代表具体实现类

三、单列集合
要学习集合,需要从继承体系的顶层接口开始,先学习共性方法,然后学习不同集合的特有方法,从而掌握集合的使用。
1.单列集合的顶层接口-Collection
Collection接口中描述了所有单列集合都应该具备的操作元素的方法,按照方法的功能分为以下几类分别介绍:
<1>添加功能:

boolean add(Object obj) : 添加一个元素,操作成功返回true,否则false
boolean addAll(Collection c) : 添加一个集合的所有元素,操作成功返回true,否则false

<2>删除功能:

boolean remove(Object o):移除一个元素,操作成功返回true,否则false
boolean removeAll(Collection c):移除一个集合的元素
例如A.removeAll(B),则移除A集合中包含的B集合中的所有元素,操作成功返回true,否则false
void clear():移除集合中所有元素

<3>判断功能:

boolean contains(Object o):判断集合中是否包含指定的元素
boolean containsAll(Collection c):判断集合中是否包含指定的集合元素,即判断参数集合是否是调用集合的子集。
boolean isEmpty():判断集合是否为空

<4>获取长度:

int size():返回集合中元素的个数

<5>取交集:

boolean retainAll(Collection c):
假设有两个集合A,B,进行如下调用
A.retainAll(B):A对B做交集,最终的结果保存在A中,B不变。返回值表示的是A是否发生过改变。

<6>转换为数组:

Object[] toArray():把集合转成数组

<7>获取元素-迭代器:

Iterator iterator() :用于迭代集合中的所有元素,后面详细讲解该方法的使用。

2.集合的遍历方式
<1>转换为数组的形式遍历(不常用):
代码示例:

//1.通过toArray()方法将集合转换为Object类型的数组
Object[] elements = collection.toArray();
//2.通过循环遍历数组中的元素,即遍历集合中的元素
for(int i=0; i<elements.length ; i++) {
    Object element = elements[i];
    .....
    // do something with element
    .....
}

<2>集合特有的遍历方式-迭代器
迭代器是集合提供的专门用于遍历集合中元素的一个接口,这个接口提供了下面两种主要用于遍历元素的方法:

Object next():获取当前元素,并将指向元素的指针移动到下一个元素。
boolean hasNext():判断是否仍有元素可以迭代,是则返回 true。

通过上面两个方法,可以通过一个简单的循环实现集合中元素的遍历
代码如下:

//1.通过集合对象获取迭代器对象
Iterator it = collection.iterator();
//2.通过迭代器方法遍历集合中的元素
while(it.hasNext()) {
    Object element = it.next();
    .....
    // do something with element
    .....
}

注意:任意单列集合都可以通过以上两种方式进行元素的遍历,但通常使用迭代器的方式进行遍历。

<3>使用迭代器遍历集合的注意事项
[1].为保证正常迭代集合中的所有元素,请不要在循环中多次调用next()方法,因为每次调用都会使迭代器指向元素的指针向指向下一个元素。
[2].如果要在迭代过程中使用元素实际类型的特有方法,需要对元素进行强制类型转换,或在定义集合时声明泛型(泛型将在后面说明)。
[3].如果在迭代过程中通过集合变量调用添加或删除元素的方法对集合中元素个数进行修改,则会引发并发修改异常。在迭代器迭代过程中只能使用迭代器的特有方法对元素进行修改。

2.List集合
List是Collection的子接口,存储在List集合中的每一个元素都有对应的索引值,而且元素的存入顺序与取出顺序一致,即元素有序,因此该集合也称为序列。实现此接口的集合可以通过索引对集合中的元素进行访问和操作。List集合允许元素重复
<1>特有方法
List集合在继承Collection接口的基础上,增加了用于操作索引的特有方法,具体方法如下:
[1].添加方法

void add(int index,E element) : 在指定的索引位置添加元素

[2].删除方法

E remove(int index) 删除指定索引处的元素

[3].获取方法

E get(int index) : 获取指定索引处的元素

[4].修改方法

E set(int index,E element):修改指定索引处的元素,返回修改被修改的元素。

[5].特有迭代方法

ListIterator listIterator() : List集合的特有迭代方法,返回List集合的特有迭代器对象。

<2>.List集合特有的遍历方式
[1]. size()和get()方法结合使用
代码:

for(int i=0; i<list.size(); i++) {
    Object element = list.get(i);
    .....
    // do something with element
    .....
}

[2]. List特有的迭代器遍历
List集合提供了获取ListIterator迭代器的方法,ListIterator迭代器接口中定义了一些用于List集合迭代过程中操作元素的方法,具体如下:
添加方法

void add(E e) 在迭代过程中在当前索引处添加新元素

删除方法

void remove() 在迭代过程中删除当前元素

修改方法

void set(E e)在迭代过程中修改当前迭代的元素

获取索引

int previousIndex() 获取前一个元素的索引
E nextIdex() 获取后一个元素的索引

反向迭代(不常用)
boolean hasPrevious()
E previous()

通过上面的方法可以知道,通过ListIterator迭代List集合中的元素时可以在迭代过程中增加,删除,修改元素。
代码示例:

ListIterator li = list.listIterator();
while(li.hasNext()) {
    Object element = li.next();
    .....
    //do something with element
    .....
}

其实在使用上与普通迭代器没有多大差别,仅仅是在迭代过程中可以对集合元素进行的操作增多了。

3.List集合的几个具体子类
<1>ArrayList 集合
ArrayList集合是List集合的具体实现类,它存储元素的底层数据结构为数组结构,而数组结构操作元素的重要特点就是:增删慢,查询快
因此如果在程序中经常要对元素进行查询操作,建议使用ArrayList集合来存储。
优点:查询快,效率高
缺点:增删慢,线程不安全
<2>LinkedList集合
LinkedList集合也是List集合具体实现类,它存储元素的底层数据结构为链表结构。
优点:增删快,效率高
缺点:查询慢,线程不安全
LinkedList的特有功能:
添加功能:

public void addFirst(Object e) 添加元素到集合的首部
public void addLast(Object e) 添加元素到集合尾部

获取功能:

public Object getFirst() 获取第一个元素
public Obejct getLast() 获取最后一个元素

删除功能:

public Object removeFirst() 删除第一个元素
public Object removeLast() 删除最后一个元素

<3>Vector集合
Vector集合的底层数据结构也是数组结构,但是相对于ArrayList它增加了线程同步机制,因此使得操作元素的效率大大降低,在实际开发中不常用。
优点:线程安全
缺点:效率低
Vector的特有功能:
添加功能:

public void addElement(Object obj) – 等价于add()

获取功能:

public Object elementAt(int index) – 等价于get()
public Enumeration elements() – 等价于Iterator iterator()

特有迭代方式:

Enumeration en = vector.elements(); // 返回的是实现类的对象
while (en.hasMoreElements()) {
    Object element = en.nextElement();
    .....
    //do something with element
    .....
}

4.Set集合
Set接口也是Collection的子接口,实现Set接口的集合中没有重复元素,但元素存入顺序与取出顺序不一致,即无序
<1>HashSet集合
HashSet集合是Set接口的具体实现类,该集合的底层数据结构为哈希表结构,它通过集合元素的equals(),hashCode()方法来保证元素的唯一性
因此在将元素存储到HashSet集合时,应该保证元素对象重写了Object类的equals(),和hashCode()方法。

HashSet存储元素时的步骤:
先看hashCode()值是否相同
相同:继续走equals()方法
—返回true: 说明元素重复,就不添加
—返回false:说明元素不重复,就添加到集合
不同:就直接把元素添加到集合

如果类没有重写这两个方法,默认使用的Object()的方法
注意:在使用HashSet存储自定义对象时,务必确保自定义对象中重写了hashCode()和equals()方法

一个重写了hashCode()和equals()方法的自定义类代码示例:

public class Student {
    private String name;
    private int age;

    .....
    省略了成员变量的get/set方法的代码以及构造方法
    .....
    //重写hashCode()方法
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    //重写equals()方法
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Student other = (Student) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

<2>LinkedHashSet集合
底层数据结构由哈希表和链表组成。
哈希表保证元素的唯一性。
链表保证元素有素。(存储和取出是一致)

<3>TreeSet集合
TreeSet集合是Set集合的实现类,底层数据结构为红黑二叉树。
元素特点:存入集合中的元素会自动进行排序,但需要提供元素的比较方式,以保证正常排序。
TreeSet集合保证元素自动排序的方式有两种:

1.自然排序(元素具备比较性),让元素所属的类实现Comparable接口
2.比较器排序(集合具备比较性),让集合构造方法接收Comparator的实现类对象

两种排序方式的代码示例:
示例1:元素实现Comparable接口

/*
 * 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
 */
public class Student implements Comparable<Student> {
    private String name;
    private int age;

    .....
    省略了成员变量的get/set方法的代码以及构造方法
    .....

    @Override
    public int compareTo(Student s) {
        // 按照年龄排序,主要条件
        int num = this.age - s.age;
        // 次要条件
        // 年龄相同的时候,还得去看姓名是否也相同
        // 如果年龄和姓名都相同,才是同一个元素
        int num2 = num == 0 ? this.name.compareTo(s.name) : num;
        return num2;
    }
}

示例2:通过匿名内部累的方式自定义比较器

TreeSet<Student> ts = new TreeSet<Student>(
    new Comparator<Student>() {
        @Override
        public int compare(Student s1, Student s2) {
        // 姓名长度
        int num = s1.getName().length() - s2.getName().length();
        // 姓名内容
        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
        // 年龄
        int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
        return num3;
    }
});

二、泛型
1.概念:泛型是一种把类型明确的工作推迟到创建对象或者调用方法才去明确的特殊类型,即把类型当作参数进行传递,参数化类型。
泛型主要应用在集合中。
2.集合中使用泛型的格式:

Collection<String> collection = new ArrayList<String>()
在定义集合时通过泛型指定集合中存储元素的类型,那么当迭代集合过程中,需要操作元素的特有方法或属性时,就不需要进行向下转型,因为编译器通过泛型得知了集合中元素的实际类型。
代码示例:

Set<String> set = new HashSet<String>();
set.add("Hello");
set.add("World");
for(String element : set) {
    do something with element..;
}

3.自定义泛型
泛型类:

/*
 * 泛型类:把泛型定义在类上
 */
public class ObjectTool<T> {
    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }
}

/*
 * 泛型类的测试
 */
public class ObjectToolDemo {
    public static void main(String[] args) {
        ObjectTool<String> ot = new ObjectTool<String>();
        // 下面被注释的语句无法编译通过,因为有泛型检查
        // ot.setObj(new Integer(27)); 
        ot.setObj(new String("Hello"));
        String s = ot.getObj();
        System.out.println("element is : " + s);
    }
}

泛型方法:

/*
 * 泛型方法:把泛型定义在方法上
 */
public class ObjectTool {
    public <T> void show(T t) {
        System.out.println(t);
    }
}

泛型接口:

/*
 * 泛型接口:把泛型定义在接口上
 */
public interface Inter<T> {
    public abstract void show(T t);
}

泛型高级通配符:
? : 代表任意类型
? extends E:任意的子类类型
? super E:任意的父类类型

三、增强for循环
for循环的一种,用于遍历的简便方式
格式:

for(元素的数据类型 变量名 : 数组或者Collection集合的对象) {
使用该变量即可,该变量其实就是数组或者集合中的元素。

好处:简化了数组和集合的遍历
注意:增强for循环的目标不能为null。建议在使用前,先判断是否为null。

增强for改进集合遍历:

定义一个集合collection:任意集合都可以
添加元素:add方法
for(Object e : collection) {
    do something with e....;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值