集合:集合是存储对象数据的集合容器。
---------------------------------------------------------------------------------
数组: 存储同一种数据类型的集合容器.
数组的特点:
- 只能存储同一种数据类型的数据。
- 一旦初始化,长度固定。
- 数组中的元素与元素之间的内存地址是连续的。
注意: Object类型的数组可以存储任意类型的数据。
------------------------------------------------------------------------------------------------------------
集合比数组的优势:
- 集合可以存储任意类型的对象数据,数组只能存储同一种数据类型的数据。
- 集合的长度是会发生变化的,数组的长度是固定的。
注意:数组中可以存储基本数据类型,集合只能存储对象。集合和数组中存放的都是对象的引用而非对象本身
- |——Collection 单例集合的根接口。
- |——List 具备的特点: 有序,可重复。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变
- |——ArrayList (JDK1.2)Object数组实现的,特点: 查询速度快,增删慢。
- |——LinkedList 链表数据结构实现的, 特点:查询速度慢,增删快。
- |——Vector (JDK1.0)Object数组实现的,线程安全,操作效率低
- |——Set 具备特点: 无序,不可重复。检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变
- |——HashSet 哈希表来支持的,特点:存取速度快
- |——TreeSet 红黑树(二叉树)数据结构实现的,默认对元素进行自然排序
- |——LinkedHashSet 会保存插入的顺序
- |——List 具备的特点: 有序,可重复。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变
有序: 集合的有序不是指自然顺序,而是指添加进去的顺序与元素出来的顺序是一致的。
无序:添加元素的顺序与元素出来的顺序是不一致的。
Collection接口
方法:
增加
add(E e) 添加成功返回true,添加失败返回false.
addAll(Collection c) 把一个集合的元素添加到另外一个集合中去。删除
clear() 清空集合中的元素
remove(Object o) 指定集合中的元素删除,删除成功返回true,删除失败返回false
removeAll(Collection c) c.removeAll(c2),删除c集合中与c2的交集元素。
retainAll(Collection c) c.retainAll(c2),保留c集合与c2的交集元素,其他的元素一并删除查看
size() 查看元素个数*判断*
isEmpty() 是否为空
contains(Object o) 判断集合中是否存在指定的元素 ( contains方法内部是依赖于equals方法进行比较的)
containsAll(Collection<?> c) 判断此集合有包含指定集合中的所有元素迭代
toArray() 把集合中的元素全部存储到一个Object类型的数组中返回
iterator() 返回一个迭代器(实际上返回的是iterator接口的实现类对象); 获取迭代器时,迭代器中有一个指针指向集合中第一个元素。
List接口
List接口中特有的方法具备的特点: 操作的方法都存在索引值。
只有List接口下面的集合类才具备索引值。其他接口下面的集合类都没有索引值。
特有方法:
添加
add(int index, E element) 把元素添加到集合中的指定索引值位置上
addAll(int index, Collection<? extends E> c) 把参数list的元素添加到对象list集合指定索引值的位置上
获取
get(int index) 根据索引值获取集合中的元素
indexOf(Object o) 找出指定元素第一次出现在集合中的索引值
lastIndexOf(Object o) 找指定的元素最后一次出现在集合中的索引值
subList(int fromIndex, int toIndex) 指定开始与结束的索引值截取集合中的元素
修改
set(int index, E element) 使用指定的元素替换指定索引值位置的元素
list特有的迭代器
listIterator() 返回的是一个List接口中特有的迭代器
ListIterator特有的方法:
hasPrevious() 判断是否存在上一个元素。
previous() 当前指针先向上移动一个单位,然后再取出当前指针指向的元素。
add(E e) 把当前有元素插入到当前指针指向的位置上。set(E e) 替换迭代器最后一次返回的元素。
ArrayList:
ArrayList底层是维护了一个Object数组实现的。 特点: 查询速度快,增删慢。(数组内存地址连续。超过容量时新建数组拷贝数据)
使用无参构造函数时,Object数组默认的容量是10,当长度不够时,自动增长0.5倍。
如果目前的数据是查询比较多,增删比较少的时候,那么就使用ArrayList存储这批数据。 比如 :高校的图书馆
ArrayList 特有的方法:
ensureCapacity(int minCapacity) 指定增加数组容量(一般用ArrayList构造方法指定容量)
trimToSize() 将数组容量调整为列表当前大小
LinkedList:
LinkedList底层是使用了链表数据结构实现的。 特点:查询速度慢,增删快。
Linkedlist特有的方法:
addFirst(E e) 把元素添加到集合的首位置上
addLast(E e) 把元素添加到集合的末尾处
getFirst() 获取集合中首位置的元素
getLast() 获取集合中末尾的元素
removeFirst() 删除集合中的首位置元素并返回
removeLast() 删除集合中的末尾素并返回
descendingIterator() 返回逆序的迭代器对象
数据结构
栈 (1.6) : 主要是为了可以使用LinkedList模拟堆栈数据结构的存储方式。 先进后出
push() 将该元素插入此集合的开头处 (将元素推入此列表所表示的堆栈)
pop() 移除并返回集合中的第一个元素 (从此列表所表示的堆栈处弹出一个元素)
队列(双端队列1.5): 主要是为了可以使用LinkedList模拟队列数据结构的存储方式。 先进先出
offer() 把元素添加到集合的末尾处
poll() 移除并返回集合中的第一个元素
Vector:
底层也是维护了一个Object的数组实现的,实现与ArrayList是一样的,但是Vector是线程安全的,操作效率低
当长度不够时,自动增长1倍。
Set接口
HashSet的实现原理 :
哈希表来支持实现的。
往Haset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,
然后通过元素的哈希值经过移位等运算,就可以算出该元素在哈希表中的存储位置。
算出位置后分两种情况:
情况1: 如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。
情况2: 如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素允许添加(哈希表桶式结构,一个位置能存储多个元素)。
TreeSet,比较器:
红黑树(二叉树)数据结构实现的,默认对元素进行自然排序。红黑树是一种特定类型的二叉树:左小右大
如果二叉树3个节点还没构成叉结构就会自动调整节点,以缩短后续比较过程
注意事项:
- 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
- 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素的比较规则定义在compareTo(T o)方法上。
- 如果比较元素的时候,compareTo方法返回 的是0,那么该元素就被视为重复元素,不允许添加。(注意:TreeSet与HashCode、equals方法是没有任何关系。)
- 往TreeSet添加元素的时候, 如果元素本身没有具备自然顺序 的特性,而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个比较器。
- 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了Comparable接口, 在创建TreeSet对象的时候也传入了比较器那么是以比较器的比较规则优先使用。
compareTo(Emp o) 根据此对象是小于、等于还是大于指定对象,返回负整数、零或正整数。
自定义定义比较器:自定义一个类实现Comparator接口,把元素与元素之间的比较规则定义在compare方法内即可。
自定义比较器的格式 :
class 类名 implements Comparator{
public int compare(Emp o1, Emp o2) {
return o1.id-o2.id;
}
}
推荐使用:比较器(Comparator)。提高复用性(Comparable只能在类内使用)
TreeSet是可以对字符串进行排 的, 因为字符串已经实现了Comparable接口。
字符串的比较规则:
情况一: 对应位置有不同的字符出现, 就比较的就是对应位置不同的字符。
情况 二:对应位置上的字符都一样,比较的就是字符串的长度。
Iterator迭代器
迭代器的作用:用于抓取集合中的元素。
Iterator是对集合进行迭代的迭代器。代替了 Java Collections Framework 中的 Enumeration(Enumeration是JDK1.0时推出的 )
Iterable接口:是Collection的父接口(Jdk1.5)。实现了Iterable的类就是可迭代的!并且支持增强for循环。
迭代器的方法:
- boolean hasNext() 当前指针是否有指向元素。如果有返回true,否则返回false 。
- Object next() 获取当前指针指向的元素并返回,然后指针向下移动一个单位。
- void remove() 移除迭代器最后一次返回的元素。
- void forEachRemaining(Consumer action),这是Java 8为Iterator新增的默认方法,该方法可使用Lambda表达式来遍历集合元素。
例:ArrayList录入字符串,迭代器+for增强遍历
ArrayList list = new ArrayList();
list.add······
for (Iterator it = list.iterator(); it.hasNext();) {
//迭代器的next方法返回值类型是Object,所以要记得类型转换。
String next = (String) it.next();
System.out.println(next);
}
在遍历元素的时候要注意事项:
1. NoSuchElementException :没有元素的异常。 出现的原因: 没有元素可以被迭代了
2. 在迭代器迭代元素的过程中,不允许使用集合对象改变集合中的元素个数,如果需要添加或者删除只能使用迭代器的方法进行操作。如果使用过了集合对象改变集合中元素个数那么就会出现ConcurrentModificationException异常。
迭代元素的过程:迭代器创建到使用结束的时间。
泛型
在jdk5.0后出现了泛型,通过给容器加限定的形式规定容器只能存储一种类型的对象
泛型的好处:
- 在编译时期进行类型检查,将运行时的异常提前至了编译时。
- 避免了频繁的无谓的强制类型转换 。
创建泛型对象格式:
集合类<类类型> 变量名 = new 集合类<类类型>()
注意:
- 泛型没有多态的概念,左右两边的数据类型必须要一致,或者只写一边的泛型类型(兼顾5.0之前版本)。推荐两边都写泛型
- 在泛型中不能使用基本数据类型,如果需要使用基本数据类型,那么就使用基本数据类型对应的包装类型。
byte ————> Byte
short ————> Short
int ————> Integer
long ————> Long
double ————> Double
float ————> Float
boolean ————> Boolean
char ————> Character
泛型擦除:泛型只在编译时期有效,编译后的字节码文件中不存在有泛型信息
自定义泛型:自定义泛型就是一个数据类型的占位符或者是一个数据类型的变量。
+ + + + + + + + + + + + + + + + + + + + + + +
方法上自定义泛型格式:
修饰符 <声明自定义的泛型>返回值类型 函数名(泛型 变量名){
}
泛型方法注意事项:
- 在方法上自定义泛型,这个自定义泛型的具体数据类型是在调用该方法的时候传入实参时确定具体的数据类型的。
- 自定义泛型只要符合标识符 的命名规则即可,但是自定义泛型一般都习惯使用一个大写字母表示。 T(Type) E(Element)
+ + + + + + + + + + + + + + + + + + + + + + +
类上自定义泛型格式:
修饰符 class 类名<声明自定义泛型>{
}
泛型类注意事项:
- 在类上自定义泛型的具体数据类型是在使用该类的时候创建对象时候确定的。
- 如果一个类在类上已经声明了自定义泛型,如果使用该类创建对象的时候没有指定泛型的具体数据类型,那么默认为Object类型
- 在类上自定义泛型不能作用于静态的方法,如果静态的方法需要使用自定义泛型,那么需要在方法上自己声明使用。
+ + + + + + + + + + + + + + + + + + + + + + +
接口上自定义泛型定义格式:
interface 接口名<声明自定义泛型>{
}
泛型接口注意事项:
- 接口上自定义的泛型的具体数据类型是在实现一个接口的时候指定的。
- 在接口上自定义的泛型如果在实现接口的时候没有指定具体的数据类型,那么默认为Object类型。
- 如果需要在创建接口实现类对象的时候才指定接口上自定义泛型,那么需要以下格式:修饰符 class<T> 类名 implements 接口<T>{ }
泛型通配符 ?
(?)只是接收值;(extends )元素的类型必须继承自指定的类;(super)元素的类型必须是指定的类的父类
? super Integer :泛型的下限,只能存储Integer或者是Integer父类元素。
? extends Number :泛型的上限,只能存储Number或者是Number类型的子类数据。