Java核心技术 卷1 Ch.9

暂时跳过的部分:

  • Collection接口: API的使用测试与重点记忆
  • 各个集合的API接口, 还没有正式熟悉并上手使用
  • 各个集合中的API需要确定比较常用的重点
  • 9.6遗留的集合, ALL, 全部跳过

Ch.IX 集合:

这里就相当于C++ の STL 库中的容器…各种各样的容器…

所以, 一切从简!

9.1 Java集合框架:

Java集合简介:

首先来一张Java集合的全家福:

img

与C++ の STL库相似, Java的集合框架中也是将接口与实现分离, 用户只需要学会使用接口而无需关系内部实现的过程

反正就当做是C++STL容器用啦

集合与数组的区别:

根据需求选用合适的容器

这里写图片描述

常用的集合の层次结构:

Collection 接口:
对象的集合(单列集合)

  • List 接口:元素按进入先后有序保存,可重复
    • LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
    • ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
    • Vector 接口实现类 数组, 同步, 线程安全
      • Stack 是Vector类的实现类
  • Set 接口: 仅接收一次,不可重复,并做内部排序
    • HashSet 使用hash表(数组)存储元素
      • LinkedHashSet 链表维护元素的插入次序
    • TreeSet 底层实现为二叉树,元素排好序
    • EnumSet 一个专为枚举设计的集合类

Map 接口:
键值对的集合 (双列集合)

  • Hashtable 接口实现类, 同步, 线程安全
  • HashMap 接口实现类 ,没有同步, 线程不安全-
    • LinkedHashMap 双向链表和哈希表实现
    • WeakHashMap
    • IdentifyHashMap
  • TreeMap 红黑树对所有的key进行排序

集合框架中的接口简介:

集合有两个基本的接口: Collection & Map 而后每个由此派生出的容器有扩充了自己的接口

所以, 总的来说, 接口的数量是非常多的…

Collection接口简介:

Collection接口中的方法概览

在这里插入图片描述

API中抠出来的各个方法的介绍

其实也不多, 几个常用的方法都在里头了:

在这里插入图片描述

在这里插入图片描述

Map接口简介:

img

从API中抠出的方法:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

集合迭代器简介:

Iterator接口:

public interface Iterator<E>
{
	E next();
	boolean hasNextO;
	void remove0;
	default void forEachRemaining(Consumer<? super E> action);
}

API中的Iterator接口, 确实也就这4个…

在这里插入图片描述

Java的迭代器和C++有大不同:

  • C++迭代器可以笼统的看做是智能指针, 指向容器中的一个元素, 可以解引用得到元素, 也可以移动迭代器使其指向下一个元素

  • 而Java迭代器则类似于InputStream输入流, 读取元素的方法是从流中获取一个元素

    如果想要理解为一个指针的话, 可以理解为指向两个元素之间的指针, 当迭代器越过元素时返回它

就此, 理解一下Iterator接口中的几个方法:

  • next()

    相当于从流中获取一个元素, 调用这个方法, 迭代器会向后移动一位, 而后返回越过的元素

  • hasNext()

    判断迭代器的后头还有没有元素, 即流是否为空

  • remove()

    删除上次调用next()时返回的元素

    注意, 这个方法与next() 有相互依赖性, 如果remove之前没有调用next将被视作非法, 抛出IllegalStateException异常

    如删除两个相邻的元素:

    //错误的用法:
    it.remove();
    it.remove0;// Error!
        
    //相反地, 必须先调用 next 越过将要删除的元素
    it,remove() ;
    it.next();
    it.remove(); // OK
    

创建迭代器对象:

	public static void main(String[] args) {
        ArrayList<Character> arrL= new ArrayList<>();
        String str= new String("F@ck♂you");
        for(int i=0;i<str.length();i++){
            arrL.add(str.charAt(i));
        }
        System.out.println(arrL);
        Iterator<Character> iter= arrL.iterator();	//调用容器对象的工厂方法
        for(;iter.hasNext();){
            System.out.printf("%c",iter.next());
        }
    }

输出结果:

[F, @, c, k, ♂, y, o, u]
F@ck♂you

9.2 Collection系列集合:

这里在Copy一下上头的常用集合养养眼:

Collection 接口:
对象的集合(单列集合)

  • List 接口:元素按进入先后有序保存,可重复
    • LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
    • ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
    • Vector 接口实现类 数组, 同步, 线程安全
      • Stack 是Vector类的实现类
    • ArrayDeque 底层为数组的双端队列
  • Set 接口: 仅接收一次,不可重复,并做内部排序
    • HashSet 使用hash表(数组)存储元素
      • LinkedHashSet 链表维护元素的插入次序
    • TreeSet 底层实现为二叉树,元素排好序
    • EnumSet 一个专为枚举设计的集合类
    • PriorityQueue 优先队列

Map 接口:
键值对的集合 (双列集合)

  • Hashtable 接口实现类, 同步, 线程安全
  • HashMap 接口实现类 ,没有同步, 线程不安全-
    • LinkedHashMap 双向链表和哈希表实现
    • WeakHashMap
  • TreeMap 红黑树对所有的key进行排序
  • IdentifyHashMap

List系列:

需要注意的是, Java中所有的链表都是双向链表(double linked)

三个主要的链表类的对比:

  • ArrayList
    底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
  • LinkedList
    底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
  • Vector:
    底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素

img

List链表有专用的迭代器ListIterator, 实现List的特有的功能:

public interface ListIterator<E> extends Iterator<E>	;

其继承自Iterator接口, 并扩展了其功能:

在这里插入图片描述

当操作list链表时, 需要使用ListIterator完成特定的操作
可以当做是在List中移动的光标, 通过add()函数添加内容, 通过remove() + next() 来删除光标前头的元素

反正就像使用C++STL的容器一样, 每个容器都有其特定的Iterator ,就这么用吧…

书中说到的是使用ListIterator的add函数完成对向LinkedList对象的中间插入元素的操作…

但是LinkedList是有专门向中间插入元素的函数add() 的…

这里有一点迷惑…

其他有迷惑的方法自行看API

List中Iterator的失效问题:

Java中同样也存在Iterator的失效:

首先先来回顾一下C++中的迭代器失效:

注意: 为了防止迭代器失效,凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素

向容器中添加或删除元素都有可能会是迭代器失效, 即失效后的迭代器不在指向任何元素(类似野指针).

对于vector & string : 添加元素后(如用push_back()), 如果导致储存空间被重新分配, 则原先指向容器的所有迭代器都会失效; 否则只是插入位置后的元素的迭代器会失效;删除元素之后的迭代器都会失效;
对于deque: 向任何位置插入&添加元素都会导致迭代器全面失效

对于list & forward_list: 不论怎么插&删, 迭代器都是有效的

Java List的迭代器就比较脆弱了, 如果一个迭代器修改了List对象 , 会导致其他迭代器直接失效, 此时如果使用了那些失效了的迭代器, 会抛出ConcurrentModificationException异常:

List<String> list = . .
ListIterator<String> iterl = list.listlteratorO ;
ListIterator<String> iter2 = list.listlteratorO ;
iterl.nextO;
iterl.remove0;
iter2.next(); // throws ConcurrentModificationException
LinkedList的查找问题:

上头说道, LinkedList底层是用链表实现的, 所以与C++链表相似, 在元素查找上, 这玩意的效率很低, 是线性查找

但是get()函数做了一点优化, 如果查找的位置大于size/2, 则从链表的尾部开始查找

对于ArrayList与Vector:

上头说道, Vector的效率低, 而ArrayList的效率高, 但是二者的底层使用的都是数组, 造成这种差异的主要原因是: Vector的线程安全性

为了保证线程安全, Vector需要在同步操作上耗费较多的时间, 而ArrayList完全没有这个问题

所以在不需要线程安全的情况下优先使用ArrayList, 而不是Vector

Set系列:

hashCode() 设计原则:

首先先了解一下hashCode的设计原则, 下头有很多集合底层使用的都是散列表, 想要用他们储存元素, 需要对应的类实现hashCode()接口

在了解了Java散列表的储存规则后, 这里可以设计的更好:

参考之前的笔记:

在这里插入图片描述

几个比较核心的设计原则:

  1. 一定要让那些我们认为相同的对象返回相同的hashCode值
  2. 尽量让那些我们认为不同的对象返回不同的hashCode值,否则,就会增加冲突的概率。
  3. 尽量的让hashCode值散列开(两值用异或运算可使结果的范围更广)
复习 Comparator & Comparable:

后头很多集合会自动排序, 需要类支持Comparator 或 Comparable 接口

到这里这俩已经有一点遗忘了, 复习一下, 同时明确这俩的区别:

Comparable:

如果自定义类需要比较, 则可以通过实现Comparable接口:

public interface Comparable<T> 
{
    public int compareTo(T o);
    //接口中只有这一个方法
}

该接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo方法被称为其自然比较方法

Comparator:

如果需要对某个类进行排序,而该类本身不支持排序(即没有实现Comparable接口),那么就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可

也就是说,可以通过新建一个比较器类, 并实现Comparator接口,然后通过这个比较器对类进行排序

@FunctionalInterface		
public interface Comparator<T>
 {
    int compare(T o1, T o2);
    boolean equals(Object obj);
    //其中 equals为Object的方法,不算入内,所以Comparator可以作为函数式接口
    //还有其他的方法, 但都是static 或是 default, 所以只需要实现这两个
 }

区别:

Comparable 相当于类的内部比较器, 而Comparator相当于外部比较器

所以Comparable被称作自然排序, 而Comparator被称作定制排序

HashSet:
HashSet特点:
  • 底层数据结构采用哈希表实现

  • 元素无序且唯一

  • 线程不安全

  • 效率高

  • 可以存储null元素

  • 元素的具有唯一性

    注意元素唯一性是依靠是否良好的重写hashCode()和equals()方法来保证的
    如果没有重写这两个方法,则无法保证元素的唯一性

构造:
  1. **HashSet() **
    构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。
  2. HashSet(Collection<? extends E> c)
    构造一个包含指定集合中的元素的新集合。
  3. **HashSet(int initialCapacity) **
    构造一个新的空集合; 背景HashMap实例具有指定的初始容量和默认负载因子(0.75)。
  4. **HashSet(int initialCapacity, float loadFactor) **
    构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负载因子。
方法:
  1. **boolean add(E e) **
    将指定的元素添加到此集合(如果尚未存在)。
  2. **void clear() **
    从此集合中删除所有元素。
  3. **Object clone() **
    返回此 HashSet实例的浅层副本:元素本身不被克隆。
  4. **boolean contains(Object o) **
    如果此集合包含指定的元素,则返回 true 。
  5. **boolean isEmpty() **
    如果此集合不包含元素,则返回 true 。
  6. **Iterator iterator() **
    返回此集合中元素的迭代器。
  7. **boolean remove(Object o) **
    如果存在,则从该集合中删除指定的元素。
  8. **int size() **
    返回此集合中的元素数(其基数)。
  9. **Spliterator spliterator() **
    在此集合中的元素上创建late-binding和故障快速 Spliterator 。
LinkedHashSet:
特点:
  • 底层数据结构采用链表和哈希表共同实现:
    根据元素hashCode值来决定元素存储位置,但它同时使用链表维护元素的次序
  • 线程不安全
  • 效率高
    但由于需要维护元素插入顺序, 所以性能上略微低于HashSet, 但是顺序访问时速度比HashSet更快

暂时用的较少, 具体API看说明文档

TreeSet:

数集底层储存的数据结构为红黑树(R-B Tree), 与C++中set的储存结构相同, 所以用起来也差不多

  • 不允许出现键值重复

  • 所有的元素都会被自动排序

  • 效率比散列表要稍慢

    很少有能够达到时间复杂度为o(1)的常量时间的算法

PriorityQueue:

这玩意的特点与C++的优先队列priority_queue基本相同

注意, 添加自定义类对象时, 自定义类需要满足Comparable接口或是Comparator对象
而后根据这两个比较出对象之间的优先级

关于优先级:

优先队列中, 通常使用1表示最优先, 所以最小的元素是最优先的!

集合使用的选择:

根据不同的需求选择合适的集合, 会使得程序开发的效率更高, 性能更优

img

9.3 Map系列集合:

map系列和C++相似, 保存了元素的键-值关系, 用于排序比较的是, 而不作为比较

Map系列集合的共有特点:

  • 内部储存的是键-值映射, 其中键&值可以是任意类
  • Map接口提供有三种查询内部元素的方法, 被称作映射视图

img

映射视图:

之前说到, Map中提供了3中访问元素的方法, 即三种映射视图:

Set<K> keySet();				//返回键集
Collection<V> values();			//返回值集
Set<Map.Entry<K, V>> entrySet();	//返回键-值映射集

知道有着三个东西就OK, 到时候使用时再看API

HashTable:

特点:
  • 与HashMap相同,Hashtable 底层使用的也是散列表,它存储的内容是键值对(key-value)映射。
  • Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
  • Hashtable 的函数都是同步的,这意味着它是线程安全的。
  • Hashtable 的key、value都不可以为null。
  • Hashtable中的映射不是有序的。

HashMap:

特点:
  • HashMap是一个散列表,它存储的内容是键值对(key-value)映射。
  • HashMap继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
  • HashMap的实现是不同步的,这意味着它线程不安全
  • HashMap的key,value都可以是null
  • HashMap中的映射不是有序的

HashMap & HashTable 的空间与时间的折中:

//HashTable构造函数:

Hashtable() 
//构造一个新的,空的散列表,默认初始容量(11)和负载因子(0.75)。  
Hashtable(int initialCapacity) 
//构造一个新的,空的哈希表,具有指定的初始容量和默认负载因子(0.75)。  
Hashtable(int initialCapacity, float loadFactor) 
//构造一个新的,空的哈希表,具有指定的初始容量和指定的负载因子。   


//HashMap构造函数:

HashMap() 
//构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。  
HashMap(int initialCapacity) 
//构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。  
HashMap(int initialCapacity, float loadFactor) 
//构造一个空的 HashMap具有指定的初始容量和负载因子。  

可以看到, HashMap & HashTable的构造函数非常相似, 其中都有一个初始容量 & 负载因子 :

这两个参数对 HashMap & HashTable 性能影响较大

  • 初始容量是哈希表在创建时的容量, 提升初始容量可以减少hash表的冲突, 但是会增大空间开销
  • 加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度, 表明对现有空间的使用程度
    通常默认加载因子是0.75

这是在时间和空间成本上寻求一种折衷, 加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)

合理的设置这俩可以在一定程度上优化性能
但是一般很少注意这个玩意

HashMap & HashTable的比较:

img

TreeMap:

  • 底层使用R-B Tree储key, 与TreeSet相似

    所以在查找上的时间复杂度为o( log(n) )

  • 它存储类似于HashMap的键值对

  • 只允许不同的键, 但可以有相同的值

  • 不允许有null键, 但可以有多个null值。

  • 元素排列顺序与添加顺序不相同

    所以不允许使用下标(索引) 访问元素

  • 添加元素后会自动对其排序

    所以必须实现Comparator或Comparable接口

  • 线程不安全

构造器:
  1. **TreeMap() **
    使用其键的自然排序构造一个新的空树状图
  2. TreeMap(Comparator<? super K> comparator)
    构造一个新的,空的树图,按照给定的比较器排序
  3. **TreeMap(Map<? extends K,? extends V> m) **
    构造一个新的树状图,其中包含与给定地图相同的映射,根据其键的 自然顺序进行排序
  4. **TreeMap(SortedMap<K,? extends V> m) **
    构造一个包含相同映射并使用与指定排序映射相同顺序的新树映射
常用的方法:
  1. void clear():
    它从地图中删除所有键值对。
  2. void size():
    返回此映射中存在的键值对的数量。
  3. void isEmpty():
    如果此映射不包含键 - 值映射,则返回true。
  4. boolean containsKey(Object key):
    'true'如果地图中存在指定的键,则返回。
  5. 布尔的containsValue(对象键):
    它返回'true'如果一个指定的值被映射到地图上的至少一个键。
  6. Object get(Object key):
    它检索value指定的映射key,如果此映射不包含键的映射,则返回null。
  7. Object remove(Object key):
    它从地图中删除指定键的键值对(如果存在)。
  8. 比较器比较器():
    它返回用于对此映射中的键进行排序的比较器,如果此映射使用其键的自然顺序,则返回null。
  9. Object firstKey():
    它返回树映射中当前的第一个(最少)键。
  10. Object lastKey():
    它返回树映射中当前的最后一个(最大)键。
  11. Object ceilingKey(Object key):
    返回大于或等于给定键的最小键,如果没有这样的键则返回null。
  12. Object higherKey(Object key):
    返回严格大于指定键的最小键。
  13. NavigableMap descendingMap():
    它返回此映射中包含的映射的逆序视图

WeakHashMap:

特点:

WeakHashMap大体上与HashMap相同, 但是:

  • WeakHashMap是弱引用,而HashMap是强引用

即当JVM内存不足时,HashMap宁可抛出OutOfMemoryError异常也不会回收其相应的没有被引用的对象,而我们的WeakHashMap则会回收存储在其中但没有引用的对象

这里的没有被引用表示的是: WeakHashMap中储存的元素以键值对的形式存在, 当键仍然存在, 但已经没有映射的值时, 这个元素就有可能被销毁释放内存

LinkedHashMap:

继承自HashMap, 很多特征与方法都与HashMap重复

而其在设计上由于LinkedHashSet相似, 具有插入有序性, 所以这里不做记录了

IdentityHashMap:

这玩意与HashMap的区别就是, 其使用System.identityHashCode计算哈希散列码

此方法得出散列码的方式是通过对象的储存地址, 而不是对象的内容
所以, 其允许储存相同Key与相同value的元素, 其由于地址不同, 仍然被判定为不相等

并且, 在判定对象是否相等时, HashMap使用equals() , 而IdentityHashMap使用==

9.4 集合视图:

这玩意可以类比数据库中的概念:
类似于将集合类对象中的数据重新映射到一个数据集合中,但这个集合不是一个物理上存在的对象实体,而是对原集合类对象的再映射,数据物理地址未变,只是访问数据的接口变了

这玩意感觉用处不大, 先放着
下头仅仅是对书中的内容做一些摘录…

轻量级集合包装器:

List<String> strings = Arrays.asList("whz", "pyy");
List<String> whz = Collections.nCopies(100, "whz");

子视图:

//第一个索引包含在内,第二个索引则不包含在内。
List group2 = staff.subList(10,20);

SortedSet<E> subSet(E fromElement, E toElement);
SortedSet<E> tailSet(E fromElement);
SortedSet<E> headSet(E toElement);

不可修改的视图:

Collections还有几个方法,用于产生集合的不可修改视图

Collections.unmodifiableCollection();
Collections.unmodifiableList();
Collections.unmodifiableSet();
Collections.unmodifiableSortedSet();
Collections.unmodifiableNavigableSet();
Collections.unmodifiableMap();
Collections.unmodifiableSortedSet();
Collections.unmodifiableNavigableMap();

同步视图:

  • 如果多个线程访问集合,就必须确保集合不会被意外地破坏。
  • 类库的设计者使用视图机制来确保常规集合的线程安全,而不是实现线程安全的集合类。例如,Collections类的静态synchronizedMap方法可以将任何一个映射表转换成同步方法访问的Map。
Map<String, Employee> employeeMap = 
                Collections.synchronizedMap(new HashMap<String, Employee>());

受查视图:

  • 受查视图用来对泛型类型发生问题时提供调试支持

    例如:

    ArrayList<String> strings = new ArrayList<>();
    ArrayList rawList = strings;
    rawList.add(new Date());
    

    这个错误的add命令在运行时检测不到。相反,只有在稍后的另一部分代码中调用get方法,将结果转化为String时,这个类才会抛出异常。

  • 受查视图可以探测到这个类问题,下面定义了一个安全列表

    ArrayList<String> strings = new ArrayList<>();
    List<String> stringList = Collections.checkedList(strings, String.class);
    ArrayList rawList = (ArrayList) stringList;
    rawList.add(new Date());
    

    抛出java.lang.ClassCastException异常

9.5 算法:

本部分介绍Java中较为实用的方法

类似于C++ STL算法, Java中也有大量的实用算法

总之, 这部分暂时就是针对各个算法的特性进行分类, 并对使用方法上做一定的学习

排序算法:

本部分介绍Collections.sort()算法 (注意是Collections, 而不是Collection)

Collection是前头容器类实现的一个接口, 而现在的Collections是一个Class, 其中包含大量算法

//API中的方法声明:
public static <T extends Comparable<? super T>> void sort(List<T> list);

public static <T> void sort(List<T> list,
                            Comparator<? super T> c);

可以看到其只支持List系列的对象 (因为Set系列自动排序, 所以不需要这玩意)

也是有两个版本, 分别支持自然排序定制排序

特性:

sort()排序使用一个稍微优化的归并排序算法,它快速且稳定,

  • 使用优化的归并排序, 能够保证在o(nlog(n))范围内运行, 并在排序好的List上运行更快, 约o(n)
  • 排序是稳定的, 对相等的元素不进行排序
  • 被排序的List必须是可修改的, 但不能调整大小
  • 所有元素都必须是可比较的

其中, 自然排序专有特性:

  • 类必须支持Comparable接口
  • 按照升序排列List

二分查找:

本部分介绍Collections.binarySearch() 二分查找算法

//API:

public static <T> int binarySearch(List<? extends Comparable<? super T>> list,
                                   T key);

public static <T> int binarySearch(List<? extends T> list,
                                   T key,
                                   Comparator<? super T> c);

传入的对象也只是List, Set系列内部的红黑树可以快速查找, 不需要这玩意

特性:

  • 使用之前必须对List升序排序, 否则结果是未定义的

    无论是Comparable版本还是Comparator版本, binarySearch假定的情况都是List按照升序排列

  • 返回值:

    如果搜索到相应元素, 则返回其下标(索引), 其值>0, 便于判定

    否则, 返回的是一个负值, 但是这个值可以计算出一个位置, 用于将查找的元素插入List中而不会破坏其有序性, 其计算公式

    insertPoint=-returnValue-1;
    
    //通常使用的方法是:
    if(returnValue<0){
        llist.add(-returnValue-1, element);
    }
    
  • 如果传入的List对象不支持随机访问(即没有实现RandomAccess接口), 则二分查找的性能会缩水为线性查找
    即时间复杂度由o(log(n)) 变为 o(n)

简单算法:

本部分包括大量实现起来很简单的算法, 完全可以很快的手写出来, 但是使用API的话, 可以提升代码的可读性

常规数组操作:

  • reverse-反转列表中元素的顺序。
  • fill-覆盖列表中的每个元素,并具有指定的值。这个操作对于重新初始化一个列表非常有用。
  • copy-接受两个参数,一个目标列表和一个源列表,并将源的元素复制到目标中,覆盖其内容。目标列表必须至少和源代码一样长。如果它较长,则目标列表中的其余元素不会受到影响。
  • swap——在列表中的指定位置交换元素。
  • addAll—将所有指定元素添加到集合中。要添加的元素可以单独指定,也可以作为数组来指定。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

集合与数组的转换:

数组转化为集合:

可以使用Arrays.asList包装器:

//API:
@SafeVarargs
public static <T> List<T> asList(T... a);

集合转化为数组:

可以使用toArray方法, 这里以Set为例:

//API:
Object[] toArray();
<T> T[] toArray(T[] a);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值