c++ string类_【Java核心知识】容器类(一)基础知识

5073ef9ea91c629b06e4ccbb26c85356.png

【Java核心知识】容器类(一)

bc7e6804b7d818d9d626bed4c7d4c7df.png 759b2c6688077bcb42bec96e36deb3f8.png

    容器类又称为集合类,但我个人更偏向于容器类这个叫法,容器,盛放物体的东西,在《Java编程思想》的英文版中,给这一章起的标题是:holding your objects,容器类很形象的描述了这个类的目的就是从来盛放你的对象的。

    数组同样具有存储对象(引用)的功能,更多的时候,数组用来存储基本数据类型,但是数组一经创建以后,大小就已经固定,不能更改,但我们在程序中,对象的数量往往是不确定的,因此数组就不能满足我们的需求。容器类不仅可以达到存储对象的目的,而且动态改变其容量的大小,并且在JDK中,针对不同的数据结构,设计了多样的容器类来提高存取元素的效率(需要注意的是,容器类只能存储引用数据类型,不能存储基本数据类型,对基本数据类型的存储是通过自动装箱机制来完成的,并且容器类中存储的是对象的引用)。

759b2c6688077bcb42bec96e36deb3f8.png

01

容器类概述

    依照存入容器类中对象的形式来分,容器类可以分为单列容器,双列容器,单列容器内直接存储对象,双列集合以key-value的形式存储对象。单列容器的顶层接口为Collection,双列容器的顶层接口为Map。下面看JDK中设计的容器类的继承体系:

a25a44d0380806121982ffdc5e0df0f9.png

上面是集合体系完整的、标准的继承习题,但是其中涉及的很多接口是我们用不到,因此我们给出一些常用的容器类中的继承体系,该图并非实际的继承体系,只是方便记忆的简化版继承体系:

先看单列容器类:

19a74a27a520aef84c14eba8de321b7d.png

双列容器:

2cce2ab15d69a681a6941cd18930799a.png

以上两个继承体系图中涉及的类及其特点请面试的朋友务必记忆。

02

单列容器类顶层接口Collection

单列容器类的顶层接口为Collection,其源码中常用的方法如下:

public interface Collection<E> extends Iterable<E> {      //返回容器中对象个数。    int size();  //判断容器是否为空    boolean isEmpty();  //判断容器中是否包含某个对象    boolean contains(Object o);  //返回容器的迭代器对象    Iteratoriterator();  //将容器中的对象转为数组    Object[] toArray();  //将容器转为所需要类型的数组     T[] toArray(T[] a);  //向容器中添加一个元素    boolean add(E e);  //移除容器中指定的元素    boolean remove(Object o);  /**   *判断容器中是否包含另一个容器中的所有元素,   *判断某一容器是否为当前对象的子集   */    boolean containsAll(Collection> c);  //将某一容器中的元素全部添加到另一个容器    boolean addAll(Collection extends E> c);  //移除当前容器中某一子集    boolean removeAll(Collection> c);  //清空容器    void clear();    //获取Stream流    default Streamstream() {        return StreamSupport.stream(spliterator(), false);    }}

下面给出一个例子,以ArrayList为例:

public class CollectionTest {    public static void main(String[] args) {        System.out.println("==========Collection中元素的添加===========");        Collection collectionChar=new ArrayList();        collectionChar.add("a");        collectionChar.add("b");        collectionChar.add("c");        System.out.println(collectionChar);//输出[a, b, c]        Collection collectionString=new ArrayList();        collectionString.add("hello");        collectionString.add("world");        Collection collectionInteger=new ArrayList();        collectionInteger.add("1");        collectionInteger.add("2");        //将collectionChar中的元素全部添加到collectionString        collectionString.addAll(collectionChar);        System.out.println(collectionString);//输出[hello, world, a, b, c]        System.out.println("==========Collection中元素查询===========");        //判断当前容器中元素个数        System.out.println(collectionString.size());//输出5        //判断容器中是否包含某个指定元素        System.out.println(collectionString.contains("hello"));//输出true        //判断元素中是否包含另一个容器中的所有元素        System.out.println(collectionString.containsAll(collectionChar));//输出true        System.out.println(collectionString.containsAll(collectionInteger));//输出false        //从当前容器中移除其子集元素,也就是求某一子集的补集        boolean all = collectionString.removeAll(collectionChar);        System.out.println(collectionString);//[hello, world]        //清空元素        collectionString.clear();        System.out.println(collectionString.size());//0    }}

以上代码给出了一些基本方法的使用,这些方法通过方法名称和参数类型就可以知道其作用。下面详述几个重要的方法。

toCharArray方法

下面详述以下toArray,toArray(T[] a)两个方法的用法:

toArray:将Collection容器类中的对象转为数组,注意这个结果是Object[]类型的数组,需要逐个向下转型,转变为我们需要的数据类型。示例如下:

 public static void main(String[] args) {        Set hashSet=new HashSet();        hashSet.add("a");        hashSet.add("b");        hashSet.add("c");        //错误示范,会报错java.lang.ClassCastException        //String[] Strings = (String[])hashSet.toArray();        Object[] objects = hashSet.toArray();        for (Object object : objects) {            System.out.println((String)object);        }    }

注意上述代码中报错的地方,Java中的转型只适用于单个元素的,如果你想集体把Object[]转为String[],这是不允许的。

toArray(T[] a):把当前容器中的元素,转为指定的数组类型进行存储。示例如下:

public class SetTest {    public static void main(String[] args) {        Set<String> hashSet=new HashSet<String>();        hashSet.add("a");        hashSet.add("b");        hashSet.add("c");        String[] strings=new String[hashSet.size()];        String[] stringsResult = hashSet.toArray(strings);        for (String s : stringsResult) {            System.out.println(s);        }    }}

contains方法

先看这样一段代码:

public class ContainsMethod {    public static void main(String[] args) {        //新建一个字符串        String s1="hello";        //开辟内存再创建一个hello        String s2=new String("hello");        Collection strings = new ArrayList<>();        //将s1放入容器        strings.add(s1);        //判断s2是否在容器中        boolean contains = strings.contains(s2);        System.out.println(contains);    }}

contains是true还是false呢?

在本文的开篇词中,我们说过,容器中存储的是对象的引用,在上述代码中,我们分别创建了两个字符串的引用s1和s2,分别指向了不同堆内存但是堆内存存储了相同内容字符串的元素。把s1存入了容器,并未将s2存储到容器,但我们用contains方法判断容器中是否包含s2的时候却返回了true。这说明容器内是已经存在s2的。这就表明contains方法判断容器中是否包含某个元素是通过判断元素的内容的。当然,这样说还不太准确。contains方法是Collection接口中的方法,不同的实现类contains方法的实现可能不同,对我们这里看ArrayList中contains的源码:

public boolean contains(Object o) {        return indexOf(o) >= 0;    }  public int indexOf(Object o) {        if (o == null) {            for (int i = 0; i < size; i++)                if (elementData[i]==null)                    return i;        } else {            for (int i = 0; i < size; i++)                if (o.equals(elementData[i]))                    return i;        }        return -1;    }

重点显而易见,equals方法。也就是说,contains方法判断某个元素是否包含在容器中,是通过调用传入元素的equals方法的结果来判断的。因为String类中重写了equals方法,用来比较字符串的内容,所以contains判断容器中是否存在比较的是对象的内容。所以引例中输出的是true。如果集合类中的类没有重写equals方法,那么调用的Object 的equals方法,那么比较的就是内存地址了。所以一般每个实现了Collection 的类都会重写contains方法。

iterator方法

Collection 接口继承了Iterable接口,iterator方法是Iterable接口中的方法,用来返回一个Iterator对象,Iterator主要用来实现容器中元素的遍历。我们先看一下Iterator类中的方法:

public interface Iterator<E> {  //判断当前游标的下一个位置是否有元素    boolean hasNext();  //返回当前游标指向的元素,并将游标指向下一个位置。    E next();  //删除游上次调用next方法h获得的元素  default void remove() {        throw new UnsupportedOperationException("remove");    }}

下面给出一个例子展示其用法:请注意例子注释中的错误示例。

public class IteratorTest {    public static void main(String[] args) {        Collection collectionChar=new ArrayList();        collectionChar.add("a");        collectionChar.add("b");        collectionChar.add("c");        Iterator iterator = collectionChar.iterator();        //利用iterator遍历容器中的元素        while (iterator.hasNext()){            Object next = iterator.next();            System.out.println(next);        }        Iterator iteratorNew = collectionChar.iterator();        //删除容器中的元素        while (iteratorNew.hasNext()){            iteratorNew.next();            iteratorNew.remove();            System.out.println(collectionChar);        }        //        错误示范2:不可以用collection的remove//        while (iterator.hasNext()){//            String next =(String) iterator.next();//            collectionChar.remove(next);//不可以用collection的remove//        }//        错误示范2:remove()将会删除上次调用next()时返回的元素,也就是说先调用next()方法,再调用remove方法才会删除元素//        while (iterator.hasNext()){//            iterator.remove();////        }    }}

以上就是单列集合类Collection中涉及的方法。

2、1

Collection的子接口List

现在我们把目光转向容器类继承体系图中的Collection接口的左侧来看一下list接口,list接口用来描述具有如下特点的单列容器:

1、有序,有序指的是存储与读取的顺序是一样的(并非大小排序)

2、有整数的索引

3、可以存储重复元素

因为list是Collection的子接口,所以它拥有Collection接口中描述的所以方法,除此之外,还有其特殊的方法。

//在指定索引的下标处插入一个元素,其余元素后移void add(int index, E element);//获取指定索引处的元素E get(int index);//获取指定元素第一次出现位置的索引int indexOf(Object o); //获取指定元素最后一次出现位置的索引int lastIndexOf(Object o);//将指定索引处的元素替换为目标元素E set(int index, E element);

下面给出一个示例:

 public static void main(String[] args) {        LinkedList strings = new LinkedList<>();        strings.add("a");        strings.add("c");        //在第一个位置添加一个元素b        strings.add(1,"b");//输出[a, b, c]\        strings.get(2);//输出c        int b = strings.indexOf("b");//输出1        int a = strings.lastIndexOf("a");//输出0        strings.set(2, "d");//输出[a, b, d]      }

2、1、1

List中的ArrayList

ArrayList的基础用法我们在前面已经有了基础的使用,现在我们说一些ArrayList底层的知识。

1、ArrayList是实现了基于动态数组的数据结构,其增删数据较慢,但是查询数据较快。

2、ArrayList初始化的数组容量大小是10,每次扩容为当前容量的1.5倍,源码如下:

/** * 初始化容量.*/private static final int DEFAULT_CAPACITY = 10;
//扩容函数private void grow(int minCapacity) {        // overflow-conscious code        int oldCapacity = elementData.length;        int newCapacity = oldCapacity + (oldCapacity >> 1);        if (newCapacity - minCapacity < 0)            newCapacity = minCapacity;        if (newCapacity - MAX_ARRAY_SIZE > 0)            newCapacity = hugeCapacity(minCapacity);        // minCapacity is usually close to size, so this is a win:        elementData = Arrays.copyOf(elementData, newCapacity);    }

3、ArrayList是线程不安全的

2、1、2

List中的Vector

Vector是一个线程安全版的ArrayList,在Vector类内部的、涉及对元素的更改的方法都是有关键synchronized字修饰的,因此它是在多线程环境下使用的。与ArrayList不同的是,Vector的扩容是按照当前容量的2倍扩容的。

2、1、3

List中的LinkedList

ArrayList是基于数组实现的,其查找元素的时间复杂度为O(1),但是在数组中间插入元素或者删除元素,会移动大量的元素,所以对于涉及插入和删除的操作,效率较低。但是LinkedList是基于双向链表实现的,因此其增加和删除元素的效率高,但是查询元素的效率低。

2、2

Collection的子接口Set

在Collection接口中,子接口list中存储的元素是存取有序并且可以重复的,而Set接口存储的元素特点如下:

1、元素存取无序(存入的顺序和读取的顺序不一样)。因为Set元素的存入是根据元素的哈希值计算的。

2、元素不可重复

HashSet小例如下

 public static void main(String[] args) {        Set sets=new HashSet();        sets.add("c");        sets.add("b");        sets.add("a");        sets.add("a");        System.out.println(sets);//输出[a, b, c]    }

我们给出的HashSet源码如下:

public HashSet() {        map = new HashMap<>();    }

可以看出HashSet底层是通过双列容器中的HashMap实现的,其实HashSet存储的就是HashMap中的key类型。具体原理我们学完HashMap就可以明朗。

2、2

Collection中的子接口SortedSet

SortedSet接口表示的是有排序功能的接口,实现了SortedSet的类具有自动排序的功能。TreeSet就是SortedSet的一个实现类,存入中TreeSet的元素会自动排序。小例如下:

    public static void main(String[] args) {        Set sets=new TreeSet<>();        sets.add(10);        sets.add(9);        sets.add(20);        sets.add(25);        System.out.println(sets);//[9, 10, 20, 25]    }

TreeSet底层是通过TreeMap实现的,等学完TreeMap我们就会对TreeSet更加明朗。

以上就是我们这篇文章的内容,在下一篇中我们会讲双列容器。

1f1bbd484f75c725cddeec2262f56365.png f3b4e2cd5ca5748033e5f69571d638de.gif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值