集合类:
面向对象的语言对现实事物的体现都是以对象的形式,所以为了方便对象的操作,就得对对象进行存储,集合就是存储对象最常用的方式。
数组和集合的不同点:
l 数组虽然可以存储对象,但长度是固定的;集合长度是可变的。
l 数组可以存储基本类型数据;集合只能存储对象。
l 数组只能存储同一类型;集合可以存储不同类型。
java.util包下的集合类结构:
|
1) Collection。一个独立元素的序列,这些元素都股从一条或多条规则。
List必须按照插入的顺序保存元素。
Set不能有重复元素,里面的元素没顺序。
Queue按照排队规则确定对象产生的顺序(通常与插入顺序相同)。
2) Map。一组成对的“键值对”对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值,因此在某种意义上讲,它将数字与对象关联在了一起。映射表允许我们使用另一个对象来查找某个对象,它也被称为“字典”,因为你可以使用键对象来查找值对象,就像在字典中使用单词来定义一样。Map是强大的编程工具。
Collection接口中的方法:
![]() |
l ArrayList:它长于随即访问元素,但是在List的中间插入和移除元素时较慢。 List接口的两个实现类:
l LinkList:底层使用链表实现,在随机访问方面相对较慢,方便插入和删除操作。
l Vector:与ArrayList的功能相同,不同点:Vector是线程安全的。
迭代器Iterator:
迭代器(也是一种设计模式)的概念可以用于达成目的。迭代器是一个对象,它的工作就是便利并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。此外,迭代器通常被称为轻量级对象:创建它的代价小。
JAVA的Iterator只能单向移动,只能用来:
1. 使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
2. 使用next()获得序列中的下一个元素。
3. 使用hasNext()检查序列中是否还有元素。
4. 使用remove()将迭代器新返回的元素删除。
注意:如果只是向前遍历List,并不打算修改List对象本身,那么使用foreach()语法更加简洁。
Iterator还可以移除由next()产生的最后一个元素,所以在调用remove()之前必须先调用next()。
Iterator的并发错误:
import java.util.*;
class IteratorDemo { public static void main(String[] args) { List a = new ArrayList(); a.add(1); a.add(2); a.add(3); Iterator it = a.iterator(); while(it.hasNext()) { Object o = it.next(); a.add(4); //同时进行操作时,出现异常 System.out.println(o); }
} } |
ListIterator:
l ListIterator是一个更强大的Interator的子类型,它只能用于各种List类的访问。ListIterator可以进行双向移动。
l ListIterator可以产生当前位置的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的元素。
l 可以通过ListIterator()方法产生一个指向List开始处的ListIterator,ListIterator(n)方法创建一个一开始就指向列表索引为n的元素。
Stack:
“栈”通常是指“后进先出”的容器。
Linkedlist具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。
方法摘要:
|
Set:
Set元素无序存放,不保存重复的元素。查找是Set中最重要的操作,因此通常选择HashSet的实现,它专门对快速查找进行了优化。
1. TreeSet将元素存储在红-黑树数据结构中。
2. HashSet使用的是散列函数。
3. LinkedHashList使用了散列,还是使用了链表来维护元素的插入顺序。
HashSet如何保证元素的唯一性:
通过HashCode()和equals()方法进行判断。
如果两个元素的hashcode相同,则进行equals()方法判断。
如果两个元素的hashcode不同,则直接返回false。
(set集合中删除,包含操作时同样使用这两个方法。)
import java.util.*; class HashDemo { public static void main(String[] args) { Set s = new HashSet(); s.add(new Person("a1",2)); s.add(new Person("a2",4)); s.add(new Person("a4",3)); s.add(new Person("a1",2)); System.out.println(s); Iterator it = s.iterator(); while(it.hasNext()) { System.out.println(((Person)(it.next())).name); } } } class Person { String name; int age; Person(String n,int a) { name = n; age = a; } public int hashCode() { return name.hashCode()+age; } public boolean equals(Object obj) { if(!(obj instanceof Person)) { return false; }else { Person p = (Person)obj; return (this.name.equals(p.name))&&(this.age==p.age); } } } |
TreeSet:
此类保证排序后的 set 按照升序排列元素,根据使用的构造方法不同,可能会按照元素的自然顺序进行排序(参见 Comparable),或按照在创建 set 时所提供的比较器进行排序。
TreeSet在添加元素的时候会调用comparealbe接口里面的compareTo()方法,所以在添加自定义对象元素的时候需要实现compareable接口。
|
TreeSet构造方法添加比较器Comparator:
n 当Set内部的元素不具有比较性时,或者具备比较性不是所需的时候,这时候需要让容器自身具有比较性。
n 定义实现了Comparator接口的类,并将其对象作为参数传给TreeSet的构造方法。
n 覆盖compare()方法。
Map:
将对象映射到其他对象,Map里面存放的是键值对,不能包含相同的键,一个键只能映射一个值。
1. 增加:
V put(K key,V value) 将指定的值与此映射中的指定键相关联(可选操作)。如果此映射中以前包含一个该键的映射关系,则用指定值替换旧值(当且仅当 m.containsKey(k) 返回 true 时,才能说映射 m 包含键 k 的映射关系)。 返回: 以前与指定键相关联的值,如果没有该键的映射关系,则返回 null。如果该实现支持 null 值,则返回 null 也可表明此映射以前将 null 与指定键相关联。 |
void putAll(Map<? extends K,? extends V> t) 从指定映射中将所有映射关系复制到此映射中(可选操作)。对于指定映射中的每个键 k 到值 v 的映射关系,该调用的作用等效于在此映射上调用 put(k, v)。如果正在进行此操作的同时修改了指定的映射,则此操作的行为是未指定的。
|
2. 删除:
void clear() 从此映射中移除所有映射关系(可选操作)。 |
V remove(Object key) 如果存在此键的映射关系,则将其从映射中移除(可选操作)。更正式地说,如果此映射包含满足以下从键 k 到值 v 的映射关系:(key==null ? k==null :key.equals(k)),则移除该映射关系(该映射最多只能包含一个这样的映射关系)。 返回此映射中以前映射该键的值,如果此映射不包含该键的映射关系,则返回 null(如果该实现支持 null 值,则返回 null 也可表明此映射以前将 null 与指定键相关联)。一旦调用返回,则此映射不包含指定键的映射关系。 |
3. 判断:
boolean containsKey(Object key) |
boolean containsValue(Object value) |
boolean isEmpty() 如果此映射未包含键-值映射关系,则返回 true。 |
4. 获取:
V get(Object key) 返回此映射中映射到指定键的值。如果此映射中没有该键的映射关系,则返回 null。 |
int size() 返回此映射中的键-值映射关系数。如果该映射包含多个 Integer.MAX_VALUE 元素,则返回 Integer.MAX_VALUE。 |
Collection<V> values() |
Set<Map.Entry<K,V>> entrySet() |
Set<K> keySet() |
l Hashtable:底层是哈希表数据结构,不可以存入null键null值,该集合是线程同步的,jdk1.0效率低。
l HashMap:底层是哈希表数据结构,可以存入null键null值,该集和非线程同步,jdk1.2效率高。
l TreeMap:底层是二叉树数据结构,线程不同步。可以给键排序。
泛型:
泛型的主要目的之一就是用来指定容器要持有什么类型的对象。
简单泛型:
持有Object类型的对象:
class Holder { private Object a; Holder(Object a) {this.a = a;} } |
Holder可以存储任何类型的对象。通常我们只会使用容器来存储一种类型的对象。
使用泛型优化上面的代码:
class Holder<T> { private Object a; Holder(T a) {this.a = a;} public T get() { return a; } } |
泛型接口:
泛型可以应用于接口。例如生成器,是一种专门负责创建对象的类。
public interface Generator<T> { T next(); } |
泛型方法:
泛型方法原则:无论何时,只要能做到,尽量使用泛型方法。如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法。
public class GenericMethods { public <T> void f(T x) { System.out.println(x.getClass().getName()); } public static void main(String [] args) { GenericMethods gm = new GenericMethods(); gm.f(""); //输出java.lang.String } } |
f()方法的类型参数,是由该方法的返回类型前面动的类型参数列表表明的。
使用泛型类时,必须在创建对象的时候指定类型参数的值,而使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断。
l 当类指明了泛型类型时,方法上的参数类型以类的泛型为主。
l 静态方法的参数类型不受类的泛型类型的影响,因为静态域在对象创建之前便以加载。
泛型限定:
泛型的通配符:?
? extendsE : 可以接受E类型或E类型的子类。 上限
? superE : 可以接受E类型或E类型的父类。 下限
代码示例:
class GenericDemo { public static void main(String[] args) { ArrayList<? extends Person> a = new ArrayList<? extends Person>(); ArrayList<? super Person> a = new ArrayList<? super Person>(); } } |
Collecctions&Arrays:
在java.util包中的Arrays和Collections类中都有很多使用方法,可以在一个Collection中添加一组元素。
l Arrays.asList()方法接受一个数组或是一个用逗号分隔的可变参数列表,将其转换为一个List对象。
l Collections.addAll()方法接受一个Collection对象,以及一个数组或可变参数列表。
l Collection.addAll()成员方法只能接另一个Collection对象作为参数,因此它不如Arrays.asList()或Collecctions.addAll()灵活,这两个方法使用的都是可变参数列表。
l 易错点:
Arrays.asList()直接输出时,其底层是数组,不能调整尺寸。使用add()或deletet()方法时,将获得Unsuooorted Operation(不支持操作)错误。
使用Arrays.asList()的时候,如果数组中的元素都是对象,数组的元素就直接变成集合中的元素。
如果数组中的元素是基本数据类型,则集合中的元素为数组。
静态导入:
语法:importstatic 包.类(包.*);
类通过静态导入后,可以直接访问其中的静态方法及静态域,不必再写成类.方法或类.属性 的格式。