集合框架

早在 Java 2 中之前,Java 就提供了特设类。比如:Dictionary, Vector, Stack, 和 Properties 这些类用来存储和操作对象组。

虽然这些类都非常有用,但是它们缺少一个核心的,统一的主题。由于这个原因,使用 Vector 类的方式和使用 Properties 类的方式有着很大不同。

集合框架被设计成要满足以下几个目标

  • 该框架必须是高性能的。基本集合(动`态数组,链表,树,哈希表)的实现也必须是高效的。
  • 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
  • 对一个集合的扩展和适应必须是简单的。

为此,整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedList**, **HashSet, 和 TreeSet 等,除此之外你也可以通过这些接口实现自己的集合。

Java集合框架图

从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合Collection,存储一个元素集合,另一种是图Map,存储键/值对映射。Collection 接口又有 3 种子类型,ListSetQueue,再下面是一些抽象类,最后是具体实现类,常用的有ArrayListLinkedListHashSetLinkedHashSetHashMapLinkedHashMap 等等。

集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:

  • 接口:是代表集合的抽象数据类型。例如 CollectionListSetMap 等。之所以定义多个接口,是为了以不同的方式操作集合对象
  • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayListLinkedListHashSetHashMap
  • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。

集合框架体系

接口

接口名称 接口描述
Collection 接口 Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如ListSet)。Collection 接口存储一组不唯一无序的对象。Collection接口才可以使用foreach循环。
List 接口 List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。List 接口存储一组不唯一有序(插入顺序)的对象。
Set 接口 Set 接口具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。Set 接口存储一组唯一无序的对象。
SortedSet 接口 继承于Set保存有序的集合。
Map接口 Map 接口存储一组键值对象,提供key(键)到value(值)的映射。
Map.Entry Map.Entry 描述在一个Map中的一个元素(键/值对)。是一个 Map 的内部接口
SortedMap 接口 SortedMap 接口继承于 Map,使 Key 保持在升序排列。
Enumeration 接口 Enumeration 接口是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。
  • SetList的区别

    1. Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可重复的元素。

    2. Set 检索效率低删除和插入效率高,插入和删除不会引起元素位置改变 (实现类有HashSetTreeSet)。

    3. List 和数组类似,可以动态增长,根据实际存储的数据的长度自动增长 List 的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变。 (实现类有ArrayListLinkedListVector

集合实现类(集合类)

类名称 类描述
AbstractCollection AbstractCollection 实现了大部分的集合接口。
AbstractList AbstractList 继承于AbstractCollection 并且实现了大部分List接口。
AbstractSequentialList AbstractSequentialList 继承于 AbstractList ,提供了对数据元素的链式访问而不是随机访问。
LinkedList LinkedList 该类实现了List接口,允许有null元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。例如:List list=Collections.synchronizedList(newLinkedList(...));``LinkedList 查找效率低。
ArrayList ArrayList 该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。
AbstractSet AbstractSet 继承于AbstractCollection 并且实现了大部分Set接口。
HashSet HashSet 该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。
LinkedHashSet LinkedHashSet 具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
TreeSet TreeSet 该类实现了Set接口,可以实现排序等功能。
AbstractMap AbstractMap 实现了大部分的Map接口。
HashMap HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。 该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。
TreeMap TreeMap 继承了AbstractMap,并且使用一颗树。
WeakHashMap WeakHashMap 继承AbstractMap类,使用弱密钥的哈希表。
LinkedHashMap LinkedHashMap 继承于HashMap,使用元素的自然顺序对元素进行排序.
IdentityHashMap IdentityHashMap 继承AbstractMap类,比较文档时使用引用相等。
Vector Vector 该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。
Stack Stack 栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
Dictionary Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。
Hashtable HashtableDictionary 类的子类,位于 java.util 包中。
Properties Properties 继承于 Hashtable,表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。
BitSet BitSet 一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。

迭代器

迭代器,可以通过循环来得到或删除集合的元素。

Iterable是一个定义集合遍历方式的接口,有方法 iterator() 获取迭代器对象,因此实现了Iterable接口的类才可以使用for-each循环遍历,例如实现了Collection接口的类。

Iterator 是一个定义了集合迭代器的借口,有方法 hasNext()next()remove() 来使用迭代器对集合进行遍历操作。

ListIterator 继承了 Iterator,以允许双向遍历列表和修改元素,List接口可以使用双向迭代器。

public class Test{
 public static void main(String[] args) {
     List<String> list = new ArrayList<String>();
     list.add("Hello");
     list.add("World");
     list.add("HAHAHAHA");
     // 第一种遍历方法使用 For-Each 遍历 List
     for (String str : list) { //也可以改写 for(int i=0;i<list.size();i++) 这种形式
        System.out.println(str);
     }
 
     // 第二种遍历,把链表变为数组相关的内容进行遍历
     String[] strArray = new String[list.size()];
     list.toArray(strArray);
     for(int i=0; i < strArray.length; i++) // 可以改写为  for(String str:strArray) 这种形式
     {
        System.out.println(strArray[i]);
     }
     
     // 第三种遍历 使用迭代器进行相关遍历
     
     Iterator<String> ite=list.iterator();
     while(ite.hasNext()) // 判断下一个元素之后有值
     {
         System.out.println(ite.next());
     }
 }
}

比较器

TreeSetTreeMap的按照排序顺序来存储元素。这是通过实现了比较器接口 Comparator 来精确定义按照什么样的排序顺序,这个接口可以以不同的方式来排序一个集合。

Comparable 是自然排序使用的接口,提供的方法compareTo(T o)实现,当一个类实现了Comparable接口,就意味着该类支持排序。

Comparator 是比较器接口,可以实现此接口自定义排序规则,用自定义的比较器入参,可以完成集合的自定义排序,例如 compare(E a, E b) 返回值为 int 类型。

:当自然排序与自定义比较器同时使用,自定义比较器优先级更高。

ComparableComparator比较

Comparable 简单, 只要实现 Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码;

Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去即可进行比较。

判断重复的标准

调用对象的compareTo方法进行升序排列(A.compareTo(B)),如果return 0 表示相等;返回值大于0,则认为A大于B;如果返回值小于0,则认为A小于B。

Collections工具类

用于操作Collection下的集合。

方法 描述
addAll(Collection c, Object... elements) 将所有指定的元素elements添加到指定的集合c中。
fill(List list, Object obj) 用指定的元素obj代替指定集合list中的所有元素。(批量初始化)
max(Collection coll) 根据自然顺序返回给定集合coll的最大元素。(注意:元素必须是同一种类型)
max(Collection coll, Comparator comp) 据比较器排序comp,返回给定集合coll中的最大元素。(注意:元素必须是同一种类型)
min(Collection coll) 根据自然顺序返回给定集合coll的最小元素。(注意:元素必须是同一种类型)
min(Collection coll, Comparator comp) 据比较器排序comp,返回给定集合coll中的最小元素。(注意:元素必须是同一种类型)
reverse(List list) 反转指定集合list中元素的顺序。
shuffle(List list) 将集合中list中元素的随机打乱顺序,常常用来洗牌。
sort(List list) 将list集合升序排序。(注意:元素必须是同一种类型)
sort(List list, Comparator c) 根据指定的比较器c对指定的集合list进行排序。(注意:元素必须是同一种类型)
swap(List list, int i, int j) 交换集合list中下标i和下标j的元素位置。
synchronizedList(List list) 将list对象转换为线程安全的。
synchronizedMap(Map<K,V> m) 将map对象转换为线程安全的。
synchronizedSet(Set s) 将set对象转换为线程安全的。

ArrayList

ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。

扩容底层是使用Arrays.copyOf,在元素个数大于数组长度的时候进行扩容,扩容每次扩大到原长度的1.5倍,

// 导包 与 初始化
import java.util.ArrayList; // 引入 ArrayList 类
// E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。
// objectName: 对象名。
ArrayList<E> objectName = new ArrayList<>(); // 初始化

常用方法

方法 描述
add() 将元素插入到指定位置的 arraylist 中
addAll() 添加集合中的所有元素到 arraylist 中
clear() 删除 arraylist 中的所有元素
clone() 复制一份 arraylist
contains() 判断元素是否在 arraylist
get() 通过索引值获取 arraylist 中的元素
indexOf() 返回 arraylist 中元素的索引值
removeAll() 删除存在于指定集合中的 arraylist 里的所有元素
remove() 删除 arraylist 里的单个元素
size() 返回 arraylist 里元素数量
isEmpty() 判断 arraylist 是否为空
subList() 截取部分 arraylist 的元素
set() 替换 arraylist 中指定索引的元素
sort() 对 arraylist 元素进行排序
toArray() 将 arraylist 转换为数组
toString() 将 arraylist 转换为字符串
ensureCapacity() 设置指定容量大小的 arraylist
lastIndexOf() 返回指定元素在 arraylist 中最后一次出现的位置
retainAll() 保留 arraylist 中在指定集合中也存在的那些元素
containsAll() 查看 arraylist 是否包含指定集合中的所有元素
trimToSize() 将 arraylist 中的容量调整为数组中的元素个数
removeRange() 删除 arraylist 中指定索引之间存在的元素
replaceAll() 将给定的操作内容替换掉数组中每一个元素
removeIf() 删除所有满足特定条件的 arraylist 元素
forEach() 遍历 arraylist 中每一个元素并执行特定操作

LinkedList

LinkedList是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。链表可分为单向链表和双向链表。一个单向链表包含两个值:当前节点的值和一个指向下一个节点的链接。

// 导包 与 初始化
import java.util.LinkedList; 

LinkedList<E> list = new LinkedList<E>();   // 普通创建方法
或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表

常用方法

方法名 描述
add(E e) 链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
add(int index, E element) 向指定位置插入元素。
addAll(Collection c) 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。
`addAll(int index, Collection c) 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。
addFirst(E e) 元素添加到头部。
addLast(E e) 元素添加到尾部。
offer(E e) 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。
offerFirst(E e) 头部插入元素,返回是否成功,成功为 true,失败为 false。
offerLast(E e) 尾部插入元素,返回是否成功,成功为 true,失败为 false。
clear() 清空链表。
removeFirst() 删除并返回第一个元素。
removeLast() 删除并返回最后一个元素。
remove(Object o) 删除某一元素,返回是否成功,成功为 true,失败为 false。
remove(int index) 删除指定位置的元素。
poll() 删除并返回第一个元素。
remove() 删除并返回第一个元素。
contains(Object o) 判断是否含有某一元素。
get(int index) 返回指定位置的元素。
getFirst() 返回第一个元素。
getLast() 返回最后一个元素。
indexOf(Object o) 查找指定元素从前往后第一次出现的索引。
lastIndexOf(Object o) 查找指定元素最后一次出现的索引。
peek() 返回第一个元素。
element() 返回第一个元素。
peekFirst() 返回头部元素。
peekLast() 返回尾部元素。
set(int index, E element) 设置指定位置的元素。
clone() 克隆该列表。
descendingIterator() 返回倒序迭代器。
size() 返回链表元素个数。
listIterator(int index) 返回从指定位置开始到末尾的迭代器。
toArray() 返回一个由链表元素组成的数组。
toArray(T[] a) 返回一个由链表元素转换类型而成的数组。

HashSet

HashSet 基于 HashMap 来实现的,实现了 Set 接口,是一个不允许有重复元素的集合。HashSet无序的,即不会记录插入的顺序,HashSet 允许有 null 值。

HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 必须在多线程访问时显式同步对 HashSet 的并发访问。

// 导包 与 初始化
import java.util.HashSet;

HashSet<String> sites = new HashSet<String>();

常用方法

方法 描述
add(E e) 如果指定的元素尚不存在,则将其添加到此集合中。
clear() 从该集中删除所有元素。
clone() 返回此 HashSet实例的浅表副本:未克隆元素本身。
contains(Object o) 如果此set包含指定的元素,则返回 true
isEmpty() 如果此集合不包含任何元素,则返回 true
iterator() 返回此set中元素的迭代器。
remove(Object o) 如果存在,则从该集合中移除指定的元素。
size() 返回此集合中的元素数(基数)。
spliterator() 在此集合中的元素上创建late-binding失败快速 Spliterator

LinkedHashSet

LinkedHashSetHashSet的子类,是有序的HashSet,通过链表完成。所以继承了HashSet所有的方法

TreeSet

TreeSet对元素进行排序,底层是基于TreeMap来实现的,TreeMap底层基于红黑树(特殊的二叉树,左边叉值小,右边叉值大,有自平衡机制),存储的元素类型必须一样且元素类型的类必须实现Comparable接口

TreeSet存在两种排序方式:

  1. 自然排序:元素类型必须实现了Comparable接口,且必须重写compareTo()方法。

  2. 定制排序: 在创建TreeSet对象的时候,传入一个Comparator接口的实现类,该实现类必须重写compare方法。

构造方法

  • TreeSet()创建一个有自然排序功能的TreeSet对象。 默认自然排序是升序。

  • TreeSet(Collection c) 构造一个包含指定集合中的元素的新树集,根据其元素的 自然排序进行排序 。 使用该构造方法,要求,集合参数c必须元素是同一种类型,且实现了Comparable接口的。

  • TreeSet(Comparator comparator) 创建一个有定制排序功能的TreeSet对象。可以使用Comparator的实现类对象,也可以使用Comparator的匿名内部类。

常用方法

方法 描述
add(E e) 如果指定的元素尚不存在,则将其添加到此集合中。
addAll(Collection<?extends E> c) 将指定集合中的所有元素添加到此集合中。
ceiling(E e) 返回此set中大于或等于给定元素的 null元素,如果没有这样的元素,则 null 。
clear() 从该集中删除所有元素。
clone() 返回此 TreeSet实例的浅表副本。
contains(Object o) 如果此set包含指定的元素,则返回 true 。
descendingIterator() 以降序返回此集合中元素的迭代器。
descendingSet() 返回此set中包含的元素的逆序视图。
first() 返回此集合中当前的第一个(最低)元素。
floor(E e) 返回此set中小于或等于给定元素的最大元素,如果没有这样的元素,则 null 。
headSet(E toElement) 返回此set的部分视图,其元素严格小于 toElement 。
headSet(E toElement, boolean inclusive) 返回此set的部分视图,其元素小于(或等于,如果 inclusive为true) toElement 。
higher(E e) 返回此集合中的最小元素严格大于给定元素,如果没有这样的元素,则 null 。
isEmpty() 如果此集合不包含任何元素,则返回 true 。
iterator() 以升序返回此集合中元素的迭代器。
last() 返回此集合中当前的最后一个(最高)元素。
lower(E e) 返回此集合中的最大元素严格小于给定元素,如果没有这样的元素,则 null 。
pollFirst() 检索并删除第一个(最低)元素,如果此组为空,则返回 null 。
pollLast() 检索并删除最后一个(最高)元素,如果此集合为空,则返回 null 。
remove(Object o) 如果存在,则从该集合中移除指定的元素。
size() 返回此集合中的元素数(基数)。
spliterator() 在此集合中的元素上创建late-binding和故障快速 Spliterator 。
subSet(E fromElement, E toElement) 返回此set的部分视图,其元素范围从 fromElement (含)到 toElement (独占)。
tailSet(E fromElement) 返回此set的部分视图,其元素大于或等于 fromElement 。

Map

Map也是一种容器的接口,里面存储的是一对一对的数据,每添加一次,添加的是两个(一对)数据,一个映射(一个Map对象)不能包含重复的键,与Collection并无关系;Collection体系,存储在都是单个单个的数据,数据之间没有关系。

Map的遍历方式

  1. 如果只是获取所有一个值,使用values()方法

  2. 如果只是获取所有的key,使用keySet()方法

  3. 如果获取所有的key-value,使用entrySet(),不要用先通过keySet()获取key,再用get()获取具体的value,这样会导致二次查询,效率低下

Map m = new HashMap();
m.put("小明", 90);
m.put("小马", 60);
m.put("小郭", 60);
Set entrys = m.entrySet();
for (Object en : entrys) {
	// 需要把en强制转成 Entry类型
	Entry e = (Entry)en;
	System.out.println(e.getKey()+"--"+e.getValue());
}

HashMap

基于hashCode(),底层是Entry键值对的数组,数组中装装的是键值对。从结构实现来讲,HashMap是数组+链表+红黑树实现。

HashMap会根据添加的keyhashCode值与当前数组的length(默认是16)取模,而得到数组的下标,进而保存键值对,故HashMap存储的数据是无序的,因为角标是计算得出而不是按顺序排列。

如果keylength后,得到的下标是相等的,就会发生哈希冲突,这时候再继续判断keyequals方法,如果equals判断为true,就会将原来的键值对覆盖,如果是false,就会在该下标处的原来键值对后面形成链表,如果链表长度达到8个且元素总量大于64时,就形成红黑树。当删除键值对后剩余的数量小于等于6的时候,就重新将红黑树退化为链表

构造方法

  • HashMap()构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。

    容量16:是键值对的数组长度16

    负载系数0.75:扩容机制是当容量达到75%的时候,开始扩容,就是创建新数组,复制元素。

  • HashMap(int initialCapacity)创建空的 HashMap具有指定的初始容量initialCapacity和默认负载因子(0.75)。

  • HashMap(int initialCapacity, float loadFactor) 构造一个空的 HashMap具有指定的初始容量和负载因子。

  • HashMap(Map m) 创建一个HashMap对象,并且传入另一个Map对象m,这样先创建对象就有了与m相同的数据

常用方法

方法 描述
putAll(Map m) 将传入的Map作为参数,创建一个新的HashMap对象
get(Object key) 根据key去获取value值,如果key不存在,则返回null
remove(Object key) 根据key删除当前map对象中的这一对键值对
remove(Object key, Object value) 只有key和value都存在的时候,才能删除
replace(K key, V value) 根据key替换为新的value的值
replace(K key, V oldValue, V newValue) 只有当key和value都存在的情况下,才能替换为新的 newValue值
containsKey(Object key) 判断当前map集合对象中是否包含这个key
containsValue(Object value) 判断当前map集合对象中是否包含这个value,包含则返回 true 。
clear() 清除所有的键值对对象
isEmpty() 判段当前map对象是否是空集合 。
size() 获取当前Map键值对个数。

HashTableHashMap的区别

  • HashMap可以接受null键值和值,而HashTable则不能。

  • HashMap去掉了Hashtablecontains方法,改成containsValuecontainsKey

  • Hashtable继承自Dictionary类,而HashMap是Java1.2引进的interface Map 的一个实现。

  • 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而**HashMap就必须为之提供外同步**。

  • HashtableHashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异

Properties

Properties 继承于 Hashtable。表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。Properties 定义的实例变量,这个变量持有一个 Properties 对象相关的默认属性列表。

使用场景

  1. 将内存中的数据写入到硬盘中;

  2. 将硬盘中的数据读取到内存中;

  3. 使用时需要使用 Properties 自己的方法,才能使keyvalue都是String类型。

常用方法

方法 描述
String getProperty(String key) 用指定的键在此属性列表中搜索属性。
String getProperty(String key, String defaultProperty) 用指定的键在属性列表中搜索属性,defaultProperty为不存在时的提示内容。
void list(PrintStream streamOut) 将属性列表输出到指定的输出流。
void list(PrintWriter streamOut) 将属性列表输出到指定的输出流。
void load(InputStream streamIn) throws IOException 从输入流中读取属性列表(键和元素对)。
Enumeration propertyNames( ) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
void store(OutputStream streamOut, String description) 以适合使用 load(InputStream)方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。
Properties pp = new Properties(); //获得一个Properties对象
		
pp.setProperty("XXXX", "OOOO"); //给Properties对象设置值
pp.setProperty("YYYY", "JJJJ");
		
System.out.println(pp.size());
System.out.println(pp);
		
System.out.println(pp.getProperty("XXXX")); //从Properties对象根据key获得值

PrintStream ps = new PrintStream("/qq.txt"); // 建立一个打印流与文件关联
pp.list(ps); // 将list方法的值持久化到文件中

FileInputStream fis = new FileInputStream("/easyexplore.link") // 建立一个文件输入流,与对应文件相关联
pp.load(fis); // load方法讲文件中的值加载到Properties对象中
System.out.println(pp);
		
String property = pp.getProperty("path"); // 获得Properties对象中的 “path” 值对应的属性
System.out.println(property);

泛型

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

规则限制

  1. 泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
  2. 同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
  3. 泛型的类型参数可以有多个。
  4. 泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。
  5. 泛型的参数类型还可以是通配符类型。例如<? extends Number> 表示最高可以传入的类型是Number及其子类类型

泛型类

一个类本质看成是一个通用的模板,在设计类的时候,其中的数据类型写一个符号,泛指所有的类型,但是不指具体的某种类型

当在使用模板来创建一个对象的时候可以指定自己的类型。

class Point<数据类型>{    // 表示在类声明了一个符号  数据类型,是一种泛指
   数据类型 x;
   数据类型 y;
}  

泛型方法

就是把泛型类型的声明放在某个方法上面,只需将泛型参数列表置于返回值前,只能够本方法上或者内部可以使用这个符号,在调用方法的时候传入的实际参数为真实的类型。

:在创建数组的时候,必须要知道具体类型的。

public static <T> T[] getArr(T[] arr){
	T[] copyOf = (T[])Arrays.copyOf(arr, arr.length, arr.getClass());
	Arrays.sort(copyOf);
	return copyOf;
}