JAVA笔记十一:集合类(二)

一、Java集合框架

Java集合库将接口(interface)与实现(implementation)分离。

以队列说明:

队列接口是指可以在对列添加元素,在队头删除元素,并且可以查找队列中的元素。简称为“先进先出”。

队列(Queue)接口最简形式可能如下:

public interface Queue<E>
{
    void element(E element);
    E remove();
    int size;
}

队列的实现都可以用一个实现了Queue接口的类表示,应当注意的是Java库中没有名为CircularArrayQueue、LinkedListQueue类,这里只是用来解释集合接口与实现在概念的区分?。在实际开发中数组队列(ArrayDeque<>())、列表队列(LinkedList<>())。

//实现Queue接口的类
public class CircularArrayQueue<E> implements Queue<E>
{
    private int head;
    private int tail;
    private E[] elements;
    CircularArrayQueue(int capacity){...}
    public int add(E element){...}
    public E remove(){...}
    public int size(){...}
}



public class LinkedListQueue<E> implements Queue<E>
{
    private Link head;
    private Link tail;
    private E[] elements;
    LinkedListQueue(int capacity){...}
    public int add(E element){...}
    public E remove(){...}
    public int size(){...}
}

 而实际代码如下:

class xxx<E> implements java.util.Queue<E>
{

    @Override
    public int size() {
        return 0;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<E> iterator() {
        return null;
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return null;
    }

    @Override
    public boolean add(E e) {
        return false;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return false;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return false;
    }

    @Override
    public void clear() {

    }

    @Override
    public boolean offer(E e) {
        return false;
    }

    @Override
    public E remove() {
        return null;
    }

    @Override
    public E poll() {
        return null;
    }

    @Override
    public E element() {
        return null;
    }

    @Override
    public E peek() {
        return null;
    }
}

队列有两种实现方式:循环数组(Arrayqueue);链表队列(LinkedList)。

java.util.Queue<Integer> queue=new ArrayDeque<>();//循环数组队列
java.util.Queue<Integer> queue1 = new LinkedList<>();//链表队列

循环数组是一个有界集合,容量有限,如果程序中所收集的对象数量没有上限最好利用链表实现。

一、Collection接口

在Java库中,集合类的基本接口是Collection接口。其接口的方法参考笔记十集合类

public interface Collection<E>

add方法若元素确实被改变返回ture,反之返回false,在set集合中添加元素,而这个元素已经在set中存在,则add方法没有实效,因为set集合中元素不能重复。

 二、迭代器

Iterator接口包含四个方法

  • E next()
  • boolean hasNext()
  • void remove()
  • default void forEachRemaining(Consumer<? spuer E>action)

元素的访问

以此代码为例,利用Collection接口创建了一个ArrayList集合对象。

Collection<Integer> collection = new ArrayList<>();
Iterator<Integer> iterator = collection.iterator();

 1、反复调用next方法即可逐个获取集合中的元素,但当集合末尾时next方法会抛出NoSuchElementException,所以在调用next方法时,先利用hasNext方法判断。

 while(iterator.hasNext())
 {
      int it = iterator.next();
      System.out.println(it);
 }

2、也可以用foreach循环。

for(Integer element:collection)
            System.out.println(element);

3、使用lambda表达式

iterator.forEachRemaining(element->{
            while(iterator.hasNext())
                element = iterator.next();
    });

访问元素的顺序取决于集合类型,若迭代处理一个ArrayList则索引从0开始,每次加一,若迭代处理为HashSet元素,会按照一种基本上随机的顺序访问,无法预知各元素的顺序。

迭代原理:

Java迭代器查找一个元素只能使用next方法,当调用next方法时,迭代器会越过下一个元素,返回上一个元素的引用。因此可以认为Java迭代器位于两个元素之间。

 元素的删除

注意注意注意!!!!此时是在迭代器中,而并非在集合类中。

Interator接口的remove方法会删除上次next方法时返回的对象。netx方法与remove方法存在依赖性。如果调用remove之前未调用next方法则是不合法的,将会抛出IllegalStateException异常。

即在删除指定元素时,应当先越过要删除的元素。

例、以队列为例,添加1、2、3删除1

public class Queue {
    public static void main(String args[])
    {
        java.util.Queue<Integer> queue = new ArrayDeque<>();
        queue.add(1);
        queue.add(2);
        queue.add(3);
        Iterator<Integer> Queue_iterator = queue.iterator();
        //echo(Queue_iterator);
        Queue_iterator.next();
        Queue_iterator.remove();
        echo(Queue_iterator);

    }
    public static void echo(java.util.Iterator<Integer> E)
    {
        while(E.hasNext())
        {
            int element = E.next();
            System.out.println(element);
        }

    }
}

 二、集合框架的接口

集合有两个基本接口Collection、Map

 Map集合包含一个映射,读取值时应使用get方法。无迭代器,未继承Collection接口。

 List为一个有序集合,有两种访问方式:得迭代器、下标索引,其中下标索引被称为随机访问。

 Set集合不能添加相同元素,不要求有相同顺序。适当使用equals方法检查。包含相同元素的Set          集合使用hashCode会返回相同的散列码。

三、具体集合

其中List列表接口的实现类有LinkedList (链表)、ArrayList(数组列表)。

一、链表(linked list)

使用动态ArrayList类与数组在删除或添加时候极大的降低效率。当其中一个元素删除或增加时,其后所有的元素都要移动。

在Java中所有的链表都是双向链表,即每个链接存放这前去与后继的引用。

例、实现链表集合,添加三个元素,并删除第二个元素

import java.util.Iterator;
import java.util.List;

import static cn.abc.nb.Queue.echo;
public class LinkedList {
    public static void main(String args[])
    {
        var linkedList = new java.util.LinkedList<Integer>();
        //等价于List<Integer> linkedList = new linkedList<Integer>();
        linkedList.add(1);
        linkedList.add(2);
        linkedList.add(3);
        Iterator<Integer> LinkedIterator = linkedList.iterator();
        LinkedIterator.next();
        LinkedIterator.next();
        LinkedIterator.remove();
        //迭代遍历后,再次遍历需要重新创建构造器对象
        Iterator<Integer>  iterator = linkedList.iterator();
        echo(iterator);

    }
}
//result 1 3

迭代器

迭代遍历后,再次遍历需要重新创建构造器对象

对于自然有序的集合使用迭代器添加元素才有意义。所以Iterator接口中没有add方法,所以利用子接口ListIterator。其中在集合库类有ListIterator子接口其中包含了

void add(E element)     //这个方法默认会改变链表无返回值

E previous()                  

boolean hasPrevious()

这两个方法用来反向遍历

 LinkedList类的listIterator方法实现了一个ListIterator的迭代器对象。以上述代码为例

ListIterator<Integer> iter = linkedlist.listItetor();

注意:一定要记得创建迭代器对象使用的是方法,而不是使用构造器。当用一个刚由lisItetor方法返回并指向表头的迭代器时,此时使用add方法,新的元素将变成表头。当迭代器越过最后一个元素时(hasNext==false),添加的元素将变成表尾。

如下:

利用迭代器增加表头、表尾元素

//使用ListIterator接口中的listIterator方法创建迭代器
    ListIterator<Integer> listIterator_= linkedList.listIterator();
    listIterator_.add(0);//表头增加

向表尾增加,next循环,当hasNext为空时增加元素即可。

 while(listIterator_.hasNext())
       listIterator_.next();
 if (listIterator_.hasNext()==false)
       listIterator_.add(4);

 而在实际开发时,选用List类中的方法进行增加或删除操作。

二、数组列表(ArrayList)

ArrayList类也实现了List接口,并拥有了List接口中的方法,且ArrayList为动态数组。

综上:有序列表有两种访问方式:迭代访问、使用get()、set()随机访问。当查看元素却不知             其位置时即可使用迭代器访问。

 三、集合(Set)

集合接口的实现类有HashSet(散列集)、TreeSet(树集)、EunmSet(枚举集)

使用列表可以根据索引而找到相应元素,而当元素位置未知且元素个数较多,这时时间复杂度较大,所以当不考虑元素的顺序时,可以构造集合来实现快速查找,但缺点是无法控制元素出现的次数,因为集合中的元素是无序的。

1、散列表与散列码

利用散列表可以快速的查找对象,散列表(hash table)也称为哈希表。散列表为每一个对象计算一个整数,称为散列码(hash code)散列码是由对象的实例字段得出的一个整数。不同的数据的对象将产生不同的散列码。

2、散列码的计算

所有要实现快速查找必须先实现散列表,使用hashCode方法生成散列码。但注意计算散列码时不应该影响类中的其他元素。

在Java中散列表利用数组链表实现。每个列表被称为桶(bucket)。要查找表中对象的位置,先要计算出它的散列码,再与桶的总数取余,所得到的结果就是保存这个桶的索引。即将这个元素保存在相应的桶内。不过有时会遇到桶已经被装填的情况,称为散裂冲突(hash collision)。

1、HashSet(散列集)

Java集合库中提供了一个HashSet类,它实现了基于散列表的集。可用add方法添加元素。contains方法查找一个元素是否已在集中。它只查看桶的元素,而不必查看集合中的所有元素。

散列集迭代器可以依次访问所有的桶,所以在不关心元素中的顺序时才使用HashSet。

例、输入20个字符添加到散列集中,使用迭代处理散列集中不同的单词,输出5个。

package cn.SetCollection;
import static cn.SetCollection.Queue.echo;
import java.util.Scanner;
import java.util.Set;

/**
 * <h3>idea_test</h3>
 * <p>散列集学习</p>
 *
 * @author nb
 * @date 2023-01-13 12:36
 **/
public class HashSet {
    public static void main(String args[])
    {
        //创建Hashset集合类
        var words = new java.util.HashSet<String>();
        //输入
        try(var in  = new Scanner(System.in))
        {
            //直接使用hasNext方法死循环无法退出
            while(in.hasNext()) {
                String word= in.nextLine();
                //System.out.println(word);
                words.add(word);
            }
        }
        //迭代器访问,输出20个字符串
        java.util.Iterator<String> iterator = words.iterator();
        for (int i = 0;i<5&&iterator.hasNext();i++)
            System.out.println(iterator.next());
        System.out.println("...");





    }
}

2、TreeSet(树集)

TreeSet集合与HashSet集合相比,树集是一个有序集合。可以将任意元素插入到集合中。在对集合进行排序时,会以自然升序的输出。其排序是利用红黑树完成的。

将一个元素添加到树集要比添加到散列集慢,当查找元素时时间复杂度为log2 n。

四、队列、双端队列、优先队列

队列是一种在头部删除元素,在尾部添加元素的数据结构。双端队列(Deque)允许在队头与队尾添加或删除元素,但不允许在队列中间添加元素。

1、Queue(单端队列)

Queue接口被LinkedList类实现,我们可以把LinkedList当成Queue来用。

 普通队列(一端进另一端出):
Queue queue = new LinkedList()Deque deque = new LinkedList()

 2、Deque(双端队列)

Deque这个接口被ArrayDeque、LinkedList、LinkedBlockingDeque类中实现。且允许在队头队尾删除或添加元素。

双端队列(两端都可进出)
Deque deque = new LinkedList()

Deque<Integer> deque = new ArrayDeque<>();

例、构建一个双端队列,添加三个元素123,并删除队头、队尾与中间元素并判断是否为空。添加队头、队尾

package cn.SetCollection;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;

import static cn.SetCollection.Queue.echo;

/**
 * <h3>idea_test</h3>
 * <p>队列双向队列</p>
 *
 * @author nb
 * @date 2023-01-13 17:23
 **/
public class Quque {
    public static void main(String args[])
    {
        //构建ArrayDeque对象
        Deque<String> deque = new ArrayDeque<>();
        //向队列添加元素123
        AddToDeque(deque);
        //删除队头队尾与中间元素
        deque.remove();//先进先出删除队头
        deque.remove("2");
        deque.removeLast();//最后一个元素,删除队尾
        if (deque.peek()==null)
        {
            System.out.println("当前队列为空");
            AddToDeque(deque);
        }
        //删除之前元素,再次添加元素后输出
        echo(deque);

    }
public static void AddToDeque(java.util.Queue<String> deque)
{
    {
        String str = new String("1,2,3");
        for (int i = 0; i < str.length()-2; i++) {
            //以逗号分割字符串,并依次向队列添加元素
            String element[] = str.split(",", 3);
            deque.add(element[i]);
        }
    }
}
}

3、优先队列

优先队列(priority queue)中的元素可以按照任意顺序插入,但会按照有序的顺序进行检索。

无论何时调用remove方法,总会获得当前优先队列中最小的元素。如果使用迭代处理元素,则不需要进行排序。

优先队列使用了堆(heap)。堆是一个可以组织的二叉树,添加和删除操作可以让最小的元素移动到根。

 while(!PriorityQueue.isEmpty())
            System.out.println(PriorityQueue.remove());

循环使用remove方法,可将其从小到大排序。

猜你喜欢

转载自blog.csdn.net/m0_61598337/article/details/128642348