java(十二)list、set、queue迭代器

集合容器概述

什么是集合

集合框架:用于存储数据的容器。

集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。
任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。

接口:表示集合的抽象数据类型。接口允许我们操作集合时不必关注具体实现,从而达到“多态”。在面向对象编程语言中,接口通常用来形成规范。

实现:集合接口的具体实现,是重用性很高的数据结构。

算法:在一个实现了某个集合框架中的接口的对象身上完成某种有用的计算的方法,例如查找、排序等。这些算法通常是多态的,因为相同的方法可以在同一个接口被多个类实现时有不同的表现。事实上,算法是可复用的函数。
它减少了程序设计的辛劳。

集合框架通过提供有用的数据结构和算法使你能集中注意力于你的程序的重要部分上,而不是为了让程序能正常运转而将注意力于低层设计上。
通过这些在无关API之间的简易的互用性,使你免除了为改编对象或转换代码以便联合这些API而去写大量的代码。 它提高了程序速度和质量。

集合的特点

集合的特点主要有如下两点:

对象封装数据,对象多了也需要存储。集合用于存储对象。

对象的个数确定可以使用数组,对象的个数不确定的可以用集合。因为集合是可变长度的。

集合和数组的区别

数组是固定长度的;集合可变长度的。

数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。

数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。

数据结构:就是容器中存储数据的方式。

对于集合容器,有很多种。因为每一个容器的自身特点不同,其实原理在于每个容器的内部数据结构不同。

集合容器在不断向上抽取过程中,出现了集合体系。在使用一个体系的原则:参阅顶层内容。建立底层对象。

使用集合框架的好处

容量自增长;
提供了高性能的数据结构和算法,使编码更轻松,提高了程序速度和质量;
允许不同 API 之间的互操作,API之间可以来回传递集合;
可以方便地扩展或改写集合,提高代码复用性和可操作性。
通过使用JDK自带的集合类,可以降低代码维护和学习新API成本。

Collection

集合,集合是java中提供的一种容器,可以用来存储多个数据。

Java 容器分为 Collection 和 Map 两大类,Collection集合的子接口有Set、List、Queue三种子接口。我们比较常用的是Set、List,Map接口不是collection的子接口。

Collection集合主要有List和Set两大接口

List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。

集合体系图

collection的方法

迭代器

迭代器 Iterator 是什么?

一种对一个容器对象中的各个元素进行访问,而又不暴露该对象容器的内部细节的方法。

Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。

Iterator的实现

package java.util;
public interface Iterator<E> {
    boolean hasNext();//判断是否存在下一个对象元素
    E next();//获取下一个元素
    void remove();//移除元素
}

Iterator 的特点是只能单向遍历,但是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。

如何边遍历边移除 Collection 中的元素?

常见错误

for (Object s:list) {
    list.remove(s);
}

解决方法

Iterator<Integer> it = list.iterator();
while(it.hasNext()){
   *// do something*
   it.remove();
}

使用foreach遍历集合的优势在于代码更加的简洁,更不容易出错,不用关心下标的起始值和终止值。

ListListIterator只能用来遍历list

ListIterator listIterator = list.listIterator();

List

list接口的实现类,常用ArrayList以及LinkedList ,Vector较少使用

ArrayList

优点:

  • ArrayList 底层以数组实现,是一种随机访问模式。ArrayList 实现了 RandomAccess 接口,因此查找的时候非常快。
  • ArrayList 在顺序添加一个元素的时候非常方便。

缺点:

  • 删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性能。
  • 插入元素的时候,也需要做一次元素复制操作,缺点同上。

和数组相互转换

  • 数组转 List:使用 Arrays. asList(array) 进行转换。
  • List 转数组:使用 List 自带的 toArray() 方法。

ArrayList 扩容

查看源码,有3个构造方法,new ArrayList ()的时候不指定,默认初始容量为10

/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

为什么扩容每次扩容1.5倍

add()方法会将每次存入的数据放入一个数组elementData

  public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;     //放入数据
        return true;
    }

查看源码add()方法,发现所有的add()都会调用ensureCapacityInternal()方法,继续向下ensureExplicitCapacity(),

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)  //当容量超出时
        grow(minCapacity);
}

grow()方法,此处就是扩容代码的核心了,

int newCapacity = oldCapacity + (oldCapacity >> 1);

LinkedList

LinkedList 是双向链表的数据结构实现

补充:数据结构基础之双向链表

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

优点:

在非首尾的增加和删除操作

缺点:

  • 查询速度较慢,
  • 更耗费内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。

Vector

查看源码发现,Vector 里面方法添加了synchronized

初始容量10

扩容方法,默认是每次扩容2倍,

int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                 capacityIncrement : oldCapacity);

或者自己指定

public Vector(int initialCapacity, int capacityIncrement) 
//capacityIncrement 每次增加的容量

比较

ArrayList 和 LinkedList 的区别是什么?

  • 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现,实现同时实现了队列接口。
  • 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  • 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。
  • 内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
  • 线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

ArrayList 和 Vector 的区别是什么?

这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合

  • 线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
  • 性能:ArrayList 在性能方面要优于 Vector。
  • 扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。

Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。
Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。

多线程场景下如何使用 ArrayList?

ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。

List<String> synchronizedList = Collections.synchronizedList(list);

Set

set的常用类HashSet及其子类HashSet、TreeSet, HashSet 是基于 HashMap 实现的

HashSet

HashSet类按照哈希算法来存取集合中的对象,存取速度比较快。

​ 1.Set中是不能出现重复数据的。

​ 2.Set中可以出现空数据。

​ 3.Set中的数据是无序的。

LinkedHashSet

​ 这个相对于HashSet来说有一个很大的不一样是LinkedHashSet是有序的。LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。

TreeSet

​ TreeSet的特点是:

​ 1.不能写入空数据

​ 2.写入的数据是有序的。

​ 3.不写入重复数据

public static void main(String[] args) {
    Set<String> set = new HashSet<String>();
    //Set<String> set = new LinkedHashSet<String>();
    //Set<String> set = new TreeSet<String>();
    for(int i= 0;i<6;i++){
        set.add(i+"");
    }
    set.add("3");     //重复数据,不会写入
    set.add(null);    //不可以写入空数据
    Iterator<String> iter = set.iterator();
    while(iter.hasNext()){
        System.out.println(iter.next());      //输出是有序的
    }}

遍历

list遍历四种方法

 List list = new ArrayList();
 list.add("1");
//1
 Iterator iterator = list.iterator();
 while (iterator.hasNext()){
     System.out.print(iterator.next());
 }
 //2
 for (int i = 0; i < list.size(); i++) {
     System.out.print(list.get(i));
 }
 //3
 for (Object o:list) {
     System.out.print(o);
 }
 //4
 list.forEach(s ->{
     System.out.println(s);
 });

set遍历的两种方法

//1
Set<String> set = new HashSet<String>();
set.add("1");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
    String str = it.next();
    System.out.println(str);
}
// 2
for (String str : set) {
    System.out.println(str);
}
//3
set.forEach(s ->{
    System.out.println(s);
});

queue

Queue接口与List、Set同一级别,都是继承了Collection接口

LinkedList实现了Deque接 口

java.util.concurrent 中加入了 BlockingQueue 接口和五个阻塞队列类。它实质上就是一种带有一点扭曲的 FIFO 数据结构。不是立即从队列中添加或者删除元素,线程执行操作阻塞,直到有空间或者元素可用。
五个队列所提供的各有不同:

  • ArrayBlockingQueue :一个由数组支持的有界队列。
  • LinkedBlockingQueue :一个由链接节点支持的可选有界队列。
  • PriorityBlockingQueue :一个由优先级堆支持的无界优先级队列。
  • DelayQueue :一个由优先级堆支持的、基于时间的调度队列。
  • SynchronousQueue :一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制。

队列

add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true 如果队列已满,则返回false
poll 移除并返问队列头部的元素 如果队列为空,则返回null
peek 返回队列头部的元素 如果队列为空,则返回null
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞

add、remove、element、offer 、poll、peek 其实是属于Queue接口。

put、take 是属于Queue接口的子接口BlockingQueue接口

双端队列

Deque有三种用途:

//普通队列(一端进另一端出):
Queue queue = new LinkedList(); //或Deque deque = new LinkedList()
//双端队列(两端都可进出)
Deque deque = new LinkedList();
//堆栈
Deque deque = new LinkedList();

注意:Java堆栈Stack类已经过时,Java官方推荐使用Deque替代Stack使用。Deque堆栈操作方法:push()、pop()、peek()。

Deque是一个线性collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。

此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。插入操作的后一种形式是专为使用有容量限制的 Deque 实现设计的;在大多数实现中,插入操作不能失败。

Queue方法等效Deque方法
add(e)addLast(e)
offer(e)offerLast(e)
remove()removeFirst()
poll()pollFirst()
element()getFirst()
peek()peekFirst()

堆栈

形态常用方法

push(e)添加
pop()移除
peek()查看

双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类(继承自Vector)。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

研程序笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值