集合框架
概述
集合类的由来:对象用于封装特有数据,如果对象的个数不确定,就是用集合容器进行存储。
集合的特点:
1. 用于存储对象的容器。
2. 集合的长度是可变的。
3. 不能存储基本数据类型。
体系&共性功能
框架顶层Collection接口:
1. 添加
boolean add(E e)
boolean addAll(Collection<? extends E> c)
2. 删除
void clear()
boolean remove(Object o)
boolean removeAll(Collection<?> c)
default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given predicate
boolean retainAll(Collection<?> c)
Retains only the elements in this collection that are contained in the specified collection (optional operation).
3. 判断
boolean contains(Object o)
boolean containsAll(Collection<?> c)
boolean equals(Object o)
boolean isEmpty()
4. 获取
Iterator<E> iterator取得迭代器
int size()
5. 其他
Object[] toArray()
<T> T[] toArray(T[] a)
......
迭代器
迭代器的使用:
例子:
Collection coll ...;
Iteraotor it = coll.iterator();
两种遍历方式:
1. while循环,循环结束后it还有效。
while (it.hasNext()) {
System.out.println(it.next());
}
2. for循环。循环结束后it失效。把it的生命周期限定在for循环内。实际开发中请使用这种方式。
for (Iterator it = coll.iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
迭代器原理:
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
每一种容器都有一个实现Iterator接口的内部类
List和Set的特点
List有序(存入和取出的顺序一致)元素都有索引,元素可重复。
Set元素不能重复,无序。
List的常见方法
增删改查......
ListIterator listIterator()方法可以在遍历的时候做添加或删除元素。
List
|--Vector 内部是数组数据结构。是同步的(线程安全),效率低。几乎不用了。长度超出时,自动增加原来长度1倍。增删,查询都很慢。
|--ArrayList 内部是数组数据结构。是不同步的。替代了Vector。长度超出时,自动增加原长度50%。查询速度快。
|--LinkedList 内部是链表数据结构。是不同步的。增删元素的速度快。
数组和链表
ArrayList 查询快,增删慢。存储空间是连续的。
LinkedList 查询慢,增删快。
Vector集合
JDK1.0中的集合类。JDK1.2被添加到新增加的集合框架中。
Vector有自己特有的遗留方法。
如:addElement(E e) elements()等。
Enumeration接口:Vector的elements方法返回值,用于遍历。
boolean hasMoreElements()
E nextElement()
应该用Iterator来代替Enumeration
LindedList集合
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
有所实现的接口的所有方法。
void addFirst(E e)
void addLast(E e)
removeFirst()
removeLast()
E getFirst() 获取但不移除。如果链表为空,则抛NoSuchElementException
E getLast()
......
面试题:使用LinkedList来模拟一个堆栈或队列数据结构。
堆栈,先进后出 FILO
队列,先进先出 FIFO
ArrayList集合
练习:去除一个ArrayList中的重复元素。
Set的常见方法
Set 元素不重复,无序。
Set接口中的方法和Collection一致。
Set
|--HashSet
|--TreeSet
HashSet集合
存储的元素为自定义对象的时候,如果元素需要按照自定义的规则来确定位置。那元素就得覆盖hashCode方法和equals方法
哈希表
HashSet集合数据结构是哈希表。存储元素的时候使用元素的hashCode方法来确定位置,如果位置相同(即哈希值相同)再使用equals()方法判断元素的内容是否相同
LinkedHashSet集合
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。
TreeSet集合
可以对Set集合中的元素进行排序,是不同步的。
判断元素唯一性的方式:根据比较方法的返回结果是否是0,是0表示是相同元素,不存。
TreeSet对元素进行排序的方式一:
让元素自身具备比较功能,元素需要实现Comparable接口,覆盖CompareTo方法。
如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然排序。怎么办?
可以使用TreeSet集合的第二种排序方式:
让集合自身具备比较功能。定义一个类实现Comparator接口,覆盖compare方法。将该类作为参数传递给TreeSet集合的构造函数。
我自己的话说就是:
TreeSet中的元素是有顺序的,而顺序是通过一定的比较规则来确定的。有两种方式来确定比较规则:
1. 元素自身实现Comparable接口,覆盖ComparaTo方法。在CompareTo方法中定义比较规则。
2. 构造带比较器的TreeSet,即TreeSet(Comparator<? super E> comparator)。自定义一个类实现Comparator接口,覆盖的compate方法里定义比较规则,再把这个类的对象传给TreeSet构造器。
注:
1. 构造带比较器的TreeSet时,存到里面的元素可以不用实现Comparable接口。
2. 当两种方式都实现了的时候,以第二种方式为准。即当元素自身实现了Comparable接口,TreeSet又是带比较器构造的。则元素的顺序以比较器的规则为准。
3. 一般地,需要进行排序的对象都会实现Comparable接口。比如Java定义的类String,Integer,Long等。
4. 实际开发中,一般使用比较器。因为它可以自定义规则,更加灵活。
二叉树(红黑树)
Map集合
接口Map<K, V>。一次添加一对元素,Collection一次添加一个元素。
Map称为双列集合,Collection称为单列集合。
Map集合中存储的就是键值对。必须保证键的唯一性。
常用方法:
1. 添加。
将指定的值与此映射中的指定键关联(可选操作)。如果此映射以前包含一个该键的映射关系,则 用指定值替换旧值(当且仅当 m.containsKey(k) 返回 true 时,才能说映射 m 包含键 k 的映射关 系)。
2. 删除。
void clear()
从此映射中移除所有映射关系(可选操作)。此调用返回后,该映射将为空。
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。更确切地讲,如果此映射包含 从满足 (key==null ? k==null :key.equals(k)) 的键 k 到值 v 的映射关系,则移除该映射关系。(该 映射最多只能包含一个这样的映射关系。)
返回此映射中以前关联该键的值,如果此映射不包含该键的映射关系,则返回 null。
如果此映射允许 null 值,则返回 null 值并不一定 表示该映射不包含该键的映射关系;也可能该 映射将该键显示地映射到 null。
调用返回后,此映射将不再包含指定键的映射关系。
3. 判断。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
boolean equals(Object o)
比较指定的对象与此映射是否相等。
4. 获取。
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
int size()
返回此映射中的键-值映射关系数。如果该映射包含的元素大于 Integer.MAX_VALUE,则返回 Integer.MAX_VALUE。
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。该 set 受映射支持,所以对映射的更改可在此 set 中反映出来,反之亦然。
Set<K> keySet()
返回此映射中包含的键的 Set 视图。
Collection<V> values()
返回此映射中包含的值的 Collection 视图。
Map常用的子类
|--Hashtable 内部结构是哈希表,是同步的。不允许null作为键或值。
|--Properties 用来存储键值对型的配置文件信息。可以和IO技术相结合。
|--HashMap 内部结构是哈希表,不是同步的。允许null作为键或值。
|--TreeMap 内部结构是二叉树,不是同步的。可以对Map集合中键进行排序。
泛型
概述
泛型:JDK1.5出现的安全机制。
好处:1. 将运行时期的问题ClassCastException转移到变异时期。
2. 避免了强制转换的麻烦。
<>:什么时候用?当操作的引用数据类型不确定的时候。就是用<>。将要操作的引用数据类型传入即可。其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型。
泛型技术是给编译器使用的技术,用于编译时期,确保对了类型的安全。
擦除&补偿
泛型的擦除:运行时,会将泛型去掉,生成的class文件是不带泛型的,这个称为泛型的擦除。
为什么擦除呢?为了兼容运行的类加载器。
泛型的补偿:在运行时,通过获取元素的类型进行转换动作。不用使用者再强制转换了。
在集合中的应用
泛型类
class Tool<T> {
}
在JDK1.5后,使用泛型来接受类中要操作的引用数据类型。
泛型类。什么时候用?当类中操作的引用数据类型不确定的时候,就使用泛型来表示。
泛型方法
public <T> void show(T t) {
// ...
}
注意:
当方法是静态的,不能访问类上定义的泛型。如果静态方法要使用泛型,只能将泛型定义在方法上。
泛型接口
public class GenericDemo5 {
public static void main(String[] args) {
InterImpl in = new InterImpl();
in.show("abc");
InterImpl2<Integer> in2 = new InterImpl2<Integer>();
in2.show(3223);
}
}
/**
* 将泛型定义在接口上。
*/
interface Inter<T> {
public void show(T t);
}
class InterImpl implements Inter<String> {
public void show(String s) {
System.out.println(s);
}
}
class InterImpl2<Q> implements Inter<Q> {
public void show(Q q) {
System.out.println(q);
}
}
泛型限定(上限 下限 通配符)
通配符?:当不知道类型且不需要对该类型进行操作的时候,可以使用通配符。
上限 <? extends Person> 接收Person类型或者Person的子类型对象。
下限 <? super Student>接收Student类型或者Student的父类型。
一般在定义存元素的方法的时候用上限,因为这样都是按照上限类型来运算的,不会出现类型安全隐患。
通常对集合中的元素进行取出操作时,可以使用下限。
集合的一些技巧
需要唯一吗?
需要:Set
需要指定顺序吗?
需要:TreeSet
不需要:HashSet
但是想要一个和存储顺序一致的顺序(有序):LinkedList
不需要:List
需要频繁增删吗?
需要:LinkedList
不需要:ArrayList
如何记住每一个容器的结构和所属的体系呢?
List
|--ArrayList
|--LinkedList
Set
|--HashSet
|--TreeSet
前缀名就是该集合的数据结构。
看到array,就要想到数组,就要想到查询快,有角标。
看到link就要想到链表,就要想到增删快,就要想到add get remove + first last的方法。
看到hash就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashCode方法和equals方法。
看到tree就要想到二叉树,就要想到排序,就要想到两个接口Comparable,Comparator。
而且这些常用的集合容器都是不同步的。
集合框架工具类
Collections
集合框架的工具类。里面的方法都是静态的。
Arrays
如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行集合存储;
如果数组中的元素是基本类型数值,那么会将该数组作为集合中的元素进行存储。
重点:List asList(数组)将数组转成集合。
好处:其实可以使用集合的方法操作数组中的元素。
注意:数组的长度是固定的,所以对于集合的增删方法是不可以使用的
否则会发生UnsupportedOperationException
集合转数组的toArray()方法使用
集合转成数组呢?
使用的就是Collection接口中的toArray方法。
集合转成数组:可以对集合中的元素操作的方法进行限定,但不允许对其进行增删。
toArray方法需要传入一个指定类型的数组。
长度该如何定义呢?
如果长度小于集合的size,那么该方法会创建一个同类型并和集合相同size的数组。
如果长度大于集合的size,那么该方法就会使用指定的数组,存储集合中的元素,其他位置默认为
null。
所以建议,最后长度就指定为,集合的size。
---------------------- ASP.Net+Unity开发、 .Net培训、期待与您交流! ----------------------