-
集合(容器)
技能点列表
一、集合和数组的区别
A:长度区别
数组固定
集合可变
B:内容区别
数组可以是基本类型,也可以是引用类型
集合只能是引用类型
C:元素内容
数组只能存储同一种类型
集合可以存储不同类型 (其实集合一般存储的也是同一种类型 )
二、集合架构
A:Collection 接口存储一组不唯一, 无序的对象
B:List 接口存储一组不唯一, 有序(索引顺序) 的对象
C:Set 接口存储一组唯一, 无序的对象
D:Map接口存储一组键值对象, 提供key到value的映射
a:Key 唯一 无序
b:value 不唯一 无序
Collection
|–List
|–ArrayList
|–Vector(基本不使用,被ArrayList代替)
|–LinkedList
|–Set
|–HashSet
|–TreeSet
|–LinkedHashSet
Map
|–HashMap
|–Hashtable(基本不使用,被HashMap代替)
|–TreeMap
|–LinkedHashMap -
Collection
一、Collection 的功能
A:添加功能
add(Object obj)
B:删除功能
remove(Object obj)
C:判断功能
contains(Object obj)
D:获取功能
Iterator iterator()
E:长度功能
size()
F:把集合转数组
toArray()
二、Collection 集合的遍历
A:把集合转数组 (了解)
B:迭代器 (集合专用方式 )
C:Collection 集合的案例 (遍历方式 迭代器 )
集合的操作步骤:
a:创建集合对象
b:创建元素对象
c:把元素添加到集合
d:遍历集合
1. 存储字符串并遍历
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;
public class CollectionDemo {
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList();
//创建并添加元素
c.add("hello");
c.add("world");
c.add("java");
//遍历集合
Iterator it = c.iterator();
while(it.hasNext()) {
String s =(String) it.next();
System.out.println(s);
}
}
}
2. 存储自定义对象并遍历
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name,int age) {
this.name = name;
this.age = age;
}
//getXxx()/setXxx()
}
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;
public class StudentDemo {
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList();
//创建学生对象
Student s1 = new Student(" 林青霞 ",27);
Student s2 = new Student(" 风清扬 ",30);
Student s3 = new Student(" 刘意 ",30);
Student s4 = new Student(" 武鑫 ",25);
Student s5 = new Student(" 刘晓曲 ",16);
//添加元素
c.add(s1);
c.add(s2);
c.add(s3);
c.add(s4);
c.add(s5);
//遍历集合
Iterator it = c.iterator();
while(it.hasNext()) {
Student s = (Student)it.next();
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
- List
1)特点: 有序,可重复。
2)List 的特有功能
A:添加功能
add(int index,Object obj)
B:删除功能
remove(int index)
C:获取功能
get(int index)
D:迭代器功能
ListIterator listIterator()
E:修改功能
set(int index,Object obj)
3)List 集合的特有遍历功能
A:由 size()和 get()结合。
B:代码演示
//创建集合对象
List list = new ArrayList();
//创建并添加元素
list.add("hello");
list.add("world");
list.add("java");
//遍历集合
Iterator it = list.iterator();
while(it.hasNext()) {
String s =(String) it.next();
System.out.println(s);
}
System.out.println("----------");
for(int x=0; x<list.size(); x++) {
String s =(String) list.get(x);
System.out.println(s);
}
- ArrayList
ArrayList 线性表中的顺序表
a:在内存中分配连续的空间, 实现了长度可变的数组
b:优点: 遍历元素和随机访问元素的效率比较高
c:缺点: 添加和删除需大量移动元素效率低, 按照内容查询效率低,线程不安全。 - LinkedList
1)LinkedList 线性表中双向链表
a:采用双向链表存储方式。
b:缺点: 遍历和随机访问元素效率低下
c:优点: 插入、 删除元素效率比较高(但是前提也是必须先低效率查询才可。 如果插入删除发生在头尾可以减少查询次数),线程不安全。
2)LinkedList的特有功能
A:添加
addFirst()
addLast()
B:删除
removeFirst()
removeLast()
C:获取
getFirst()
getLast() - Set
1)特点: 无序 唯一(不重复)。
2)Set常用方法
Set相对Collection没有增加任何方法
3)Set的遍历方法
a:for-each
b:Iterator迭代器
c:无法使用for进行遍历(因为无序, 所以没有get(i)) - HashSet
A:底层数据结构是哈希表 (是一个元素为链表的数组 )
B:哈希表底层依赖两个方法: hashCode()和 equals()
执行顺序:
首先比较哈希值是否相同
相同:继续执行 equals()方法
返回 true:元素重复了,不添加
返回 false:直接把元素添加到集合
不同:就直接把元素添加到集合
优点: 添加速度快 查询速度快 删除速度快
缺点: 无序 - LinkedHashSet
A:采用哈希表存储结构, 同时使用链表维护次序
B:有序(添加顺序) - TreeSet
A:采用二叉树(红黑树) 的存储结构
优点: 有序 查询速度比List快(按照内容查询)
缺点: 查询速度没有HashSet快 - Map
1)特点:key-value映射
2)Map 和 Collection 的区别
A:Map 存储的是键值对形式的元素,键唯一,值可以重复。
B:Collection 存储的是单独出现的元素,子接口 Set 元素唯一,子接口List 元素可重复。
3)Map 接口功能概述 (自己补齐 )
A:添加功能
put(K key, V value)
B:删除功能
remove(Object key)
remove(Object key, Object value)
C:判断功能
containsKey(Object key)
containsValue(Object value)
D:获取功能
get(Object key)
E:长度功能
size()
4)Map 集合的遍历
A:键找值
a:获取所有键的集合
b:遍历键的集合 ,得到每一个键
c:根据键到集合中去找值
B:键值对对象找键和值
a:获取所有的键值对对象的集合
b:遍历键值对对象的集合,获取每一个键值对对象
c:根据键值对对象去获取键和值
代码体现:
Map<String,String> hm = new HashMap<String,String>();
hm.put("it002","hello");
hm.put("it003","world");
hm.put("it001","java");
//方式 1 键找值
Set<String> set = hm.keySet();
for(String key : set) {
String value = hm.get(key);
System.out.println(key+"---"+value);
}
//方式 2 键值对对象找键和值
Set<Map.Entry<String,String>> set2 = hm.entrySet();
for(Map.Entry<String,String> me : set2) {
String key = me.getKey();
String value = me.getValue();
System.out.println(key+"---"+value);
}
-
HashMap
1)Key无序 唯一 (Set)
2)Value 无序 不唯一 (Collection) -
LinkedHashMap
有序的HashMap 速度快 -
TreeMap
有序 速度没有hash快 -
Iterator
1)所有集合类均未提供相应的遍历方法, 而是把把遍历交给迭代器完成。 迭代器为集
合而生, 专门实现集合遍历。
2)Iterator方法
a:boolean hasNext(): 判断是否存在另一个可访问的元素
b:Object next(): 返回要访问的下一个元素
c:void remove(): 删除上次访问返回的对象。
3)ListIterator和Iterator的关系
1.public interface ListIterator extends Iterator
2.都可以遍历List
4)ListIterator和Iterator的区别
1.使用范围不同
2.Iterator可以应用于更多的集合, Set、 List和这些集合的子类型。
3.而ListIterator只能用于List及其子类型。
4.遍历顺序不同
a:Iterator只能顺序向后遍历; ListIterator还可以逆序向前遍历
b:Iterator可以在遍历的过程中remove();ListIterator可以在遍历的过程中remove()、 add()、set()。
c:ListIterator可以定位当前的索引位置, nextIndex()和previousIndex()可以实现。 Iterator没有此功能。 -
For-each循环
1)增强的for循环, 遍历array 或 Collection的时候相当简便
2)无需获得集合和数组长度, 无需使用索引访问元素, 无需循环条件
3)遍历集合时底层调用Iterator完成操作
4)For-each缺陷:
A:数组:
1.不能方便的访问下标值
2.不要在for-each中尝试对变量赋值, 只是一个临时变量
B:集合:
1.与使用Iterator相比, 不能方便的删除集合中的内容
2.For-each总结:
3.除了简单遍历并读出其中的内容外, 不建议使用增强for -
泛型
1)起因:
a:JDK1.4以前类型不明确:
b:装入集合的类型都被当作Object对待, 从而失去自己的实际类型。
c:从集合中取出时往往需要转型, 效率低, 容易产生错误。
2)解决办法:
泛型, 在定义集合的时候同时定义集合中对象的类型
3)好处:
增强程序的可读性和安全性
4)格式:
<数据类型 >
注意:该数据类型只能是引用类型。
5)泛型的由来
Object 类型作为任意类型的时候,在向下转型的时候,会隐含一个转型问题。
泛型一般是在集合中使用。 -
1.泛型类
泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。泛型类的最基本写法:
class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
private 泛型标识 /*(成员变量类型)*/ 变量名;
.....
}
}
定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,
则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类
型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
- 2.泛型方法
在java中,泛型类的定义非常简单,但是泛型方法就比较复杂了。尤其是我们见到的大多数泛型类中的成员方法也都使用了泛型,有的甚至泛型类中也包含着泛型方法,这样在初学者中非常容易将泛型方法理解错了。泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
/**
* 泛型方法的基本介绍
* @param tClass 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常
* 用于表示泛型。
*/
public <T> T genericMethod(Class<T> tClass)throws InstantiationException,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
光看上面的例子有的同学可能依然会非常迷糊,我们再通过一个例子,把我泛型方法再总结一下。
public class GenericTest {
//这个类是个泛型类,在上面已经介绍过
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
//我想说的其实是这个,虽然在方法中使用了泛型,但是这并不是一个泛型方法。
//这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
//所以在这个方法中才可以继续使用 T 这个泛型。
public T getKey(){
return key;
}
/**
* 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"
* 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。
public E setKey(E key){
this.key = keu
}
*/
}
/**
* 这才是一个真正的泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//当然这个例子举的不太合适,只是为了说明泛型方法的特性。
T test = container.getKey();
return test;
}
//这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
//这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
public void showKeyValue2(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}
/**
* 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
* 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
* 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
public <T> T showKeyName(Generic<E> container){
...
}
*/
/**
* 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
* 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
* 所以这也不是一个正确的泛型方法声明。
public void showkey(T genericObj){
}
*/
public static void main(String[] args) {
}
}
- 3.类中的泛型方法
当然这并不是泛型方法的全部,泛型方法可以出现杂任何地方和任何场景中使用。但是有一种情况是非常特殊的,当泛型方法出现在泛型类中时,我们再通过一个例子看一下
public class GenericFruit {
class Fruit{
@Override
public String toString() {
return "fruit";
}
}
class Apple extends Fruit{
@Override
public String toString() {
return "apple";
}
}
class Person{
@Override
public String toString() {
return "Person";
}
}
class GenerateTest<T>{
public void show_1(T t){
System.out.println(t.toString());
}
//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
public <E> void show_3(E t){
System.out.println(t.toString());
}
//在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
public <T> void show_2(T t){
System.out.println(t.toString());
}
}
public static void main(String[] args) {
Apple apple = new Apple();
Person person = new Person();
GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
//apple是Fruit的子类,所以这里可以
generateTest.show_1(apple);
//编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person
//generateTest.show_1(person);
//使用这两个方法都可以成功
generateTest.show_2(apple);
generateTest.show_2(person);
//使用这两个方法也都可以成功
generateTest.show_3(apple);
generateTest.show_3(person);
}
}
- 4.泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
- 5.泛型高级通配符
在java泛型中,引入了 ?(通配符)符号来支持协变和逆变.通配符表示一种未知类型,并且对这种未知类型存在约束关系.
A:
? extends T(上边界通配符upper bounded wildcard) 对应协变关系,表示 ? 是继承自 T的任意子类型.也表示一种约束关系,只能提供数据,不能接收数据.
B:
? 的默认实现是 ? extends Object, 表示 ? 是继承自Object的任意类型.
C:
? super T(下边界通配符lower bounded wildcard) 对应逆变关系,表示 ? 是 T的任意父类型.也表示一种约束关系,只能接收数据,不能提供你数据. - Collections工具类
1)专门用来操作集合的工具类
a:构造方法私有, 禁止创建对象
b:提供一系列静态方法实现对各种集合的操作
c:具体操作: 搜索、 复制、 排序、 线程安全化等
2)常用方法
a:添加功能
Collections.addAll(list, “aaa”,“bbb”,“ccc”,“ccc”);
b:查找功能
int index = Collections.binarySearch(list, “ccc”);
c:复制功能
Collections.copy(list2, list);
d:填充功能
Collections.fill(list3, “888”);
e:寻找最大值
String max = Collections.max(list4);
f:寻找最小值
String min = Collections.min(list4);
g:转置功能
Collections.reverse(list4);
h:将集合转成线程安全(有重载方法)
List list5 = Collections.synchronizedList(list4); - 旧的集合类
1)Vector
A:实现原理和ArrayList相同, 功能相同, 都是长度可变的数组结构, 很多情况下可以互用
B:两者的主要区别如下
a:Vector是早期JDK接口, ArrayList是替代Vector的新接口
b:Vector线程安全, 效率低下; ArrayList重速度轻安全, 线程非安全
c:长度需增长时, Vector默认增长一倍, ArrayList增长50%
2)Hashtable
A:实现原理和HashMap相同, 功能相同, 底层都是哈希表结构, 查询速度快, 很多情况下可互用。
B:两者的主要区别如下
a:Hashtable是早期JDK提供的接口, HashMap是新版JDK提供的接口
b:Hashtable继承Dictionary类, HashMap实现Map接口
c:Hashtable线程安全, HashMap线程非安全
d:Hashtable不允许null值, HashMap允许null值 - 集合总结
1.ArrayList和LinkedList 的联系和区别
1) 联系:
都实现了List接口
有序 不唯一(可重复)
2)ArrayList
在内存中分配连续的空间, 实现了长度可变的数组
优点: 遍历元素和随机访问元素的效率比较高
缺点: 添加和删除需大量移动元素效率低, 按照内容查询效率低,
3)LinkedList
采用链表存储方式。
缺点: 遍历和随机访问元素效率低下
优点: 插入、 删除元素效率比较高(但是前提也是必须先低效率查询才可。 如果插入删除发生在头尾可以减少查询次数)
2.Vector和ArrayList的联系和区别
1)实现原理相同, 功能相同, 都是长度可变的数组结构, 很多情况下可以互用
2)两者的主要区别如下
a:Vector是早期JDK接口, ArrayList是替代Vector的新接口
b:Vector线程安全, 效率低下; ArrayList重速度轻安全, 线程非安全
c:长度需增长时, Vector默认增长一倍, ArrayList增长50%
3.HashMap和Hashtable的联系和区别
1)实现原理相同, 功能相同, 底层都是哈希表结构, 查询速度快, 在很多情况下可以互用
2)两者的主要区别如下
a:Hashtable是早期JDK提供的接口, HashMap是新版JDK提供的接口
b:Hashtable继承Dictionary类, HashMap实现Map接口
c:Hashtable线程安全, HashMap线程非安全
d:Hashtable不允许null值, HashMap允许null值
4.Collection和Collections的区别
a:Collection是Java提供的集合接口, 存储一组不唯一, 无序的对象。 它有两个子接口List和Set。
b:Java中有一个Collections类, 专门用来操作集合类 , 它提供一系列静态方法实现对各种集合的搜索、 排序、 线程安全化等操作。
5.集合之间比较