8.1 集合框架的概念
8.1.1集合框架概况
(1)集合框架是java.util包中提供的一系列工具,它为程序处理对象组提供了标准的方式,诞生自1.2版本。
1.2版本之前,处理对象的方法如下:
- 数组 缺点:长度固定、数组中只能存放一种类型的数据
优点:速度快
- Vector、stack、properties 缺点:缺乏统一的操作方式,不易扩展
1.2版本之后,诞生了集合框架
优点: ①性能提高
②操作统一 Collection、List、Set
③提高了扩展性
④集合实现了Iterator接口
⑤迭代器:提供了访问元素的统一方法
(2)集合学习的三大部分:
①集合的继承关系
②各集合的特点
③属性各集合性能,安全性
8.1.2常用接口
Collection是所有集合的父接口,不包括Map
List实现自Collection,以列表形式存储的集合
Set实现自Collection,不允许出现相同元素
它们之间的关系是继承关系。(它们都是接口)
Collection
--List:将以特定次序存储元素。所以取出来的顺序可能和放入顺序不同。
--ArrayList / LinkedList / Vector
--Set :不能含有重复的元素
--HashSet /TreeSet
Map
--HashMap
--HashTable
--TreeMap
集合特性:
(1)集合只能存放对象,虽然能够装载整型数,但它是包装类Integer的对象
(2)集合没有限制具体装载数据类型,不建议同一个集合装载不同的数据类型,会发生转型错误,因为它存储的数据全部是Object类型,所以你无法确认在具体某位置存放是什么,在使用该数据时,无法按特定类型来转型。
(3)集合输出时,因其重写了重写toString()方法,所以直接打印输出整个集合,这一点比数组方便。 // collection : [ andy, 123, 5003 ]
什么时候用到集合框架:
如果并不知道程序运行时会需要多少对象(集合只能存放对象),或者需要更复杂方式存储对象——可以使用Java集合框架
集合框架的内容:
Collection接口 存储一组不唯一,无序的对象
List 接口 存储一组不唯一,有序(插入顺序)的对象
Set 接口 存储一组唯一,无序的对象
Map接口 存储一组键值对象,提供key到value的映
8.2 Collection
Iterator接口
//创建一个集合对象, 集合只能装载对象
//创建一个没有元素的集合
Collection c = new ArrayList();
c.add("123");
c.add("456");
c.add("andy");
c.add("ken");
System.out.println("c:"+c);
//如果想遍历这个集合
//迭代: 遍历 ---> 迭代器
Iterator it =c.iterator();
//it.next();
//it.hasNext();
//it.remove();
//通过迭代器迭代集合中的元素
while(it.hasNext()){
// 迭代器后移, 并获取该位置上的元素
String s = (String)it.next();
System.out.println("s:"+ s);
}
//如果, 我想要迭代的时候, 添加, 删除, 修改 集合中的元素
//切记不要在遍历时, 变更集合中的元素
while(it.hasNext()){
//迭代器后移, 并获取该位置上的元素
String s = (String)it.next();
System.out.println("s:"+ s);
//删除不会报错, 迭代的结果会有偏差
if(s.equals("andy")){
c.remove("andy");
}
//当你在迭代时, 添加一个元素到集合中, 运行时报错
//java.util.ConcurrentModificationException (并发修改异常)
//这句话如何理解 ?
//在遍历元素的同时, 另一边又在修改集合, 可能会引起迭代器无法确认
//遍历的边界
c.add("candy");
}
8.2List分类和用法
- ArrayList:
[1]实现了长度可变的数组,在内存中分配连续的空间。遍历元素和随机访问元素的效率比较高。
- LinkedList:
[1]采用链表存储方式。插入、删除元素时效率比较高。
[2]以链表的形式来实现list功能,查询可用get( index ),效率比ArrayList差,但插入、删除元素都比ArrayList优,一般插入、删除用的不多,所以一般选用ArrayList
[3]迭代顺序与插入时顺序一致
[4]双端队列,可以两头出入队
[5]单端队列,如果不用尾部功能,它就是单端队列
[6]还可以当成Stack来使用,与Stack区别:LinkedList非线程安全,性能上比Stack要快
3、Vector
[1]基于数组的存储形式来实现的
[2]添加、删除方法中用了同步锁保证线程安全
[3]性能比ArrayList差
[4]还存在一些BUG
[5]Stack是Vector的子类,它也是线程安全的
4、迭代器ListIterator
List 专门的迭代接口ListIterator,其在Iterator接口基础上增加了如下方法:
Boolean hasPrevious() : 返回迭代器关联的集合是否还有上一个元素
Object previous() :返回该迭代器的上一个元素
Void add() :在指定位置插入一个元素
8.2.2ArrayList 储存有序,可重复
①存储任意类型数据
//创建集合数组list
ArrayList list=new ArrayList();
//往list数组添加数据
list.add(2);
//往list数组里添加数据nihao
list.add(“nihao”);
//往数组里添加类Molly
list.add(new Molly());
//往数组添加方法
list.add(new Molly().fangfa());
//往数组第一个位置添加数据55
list.add(0,55);
//往集合中添加整个集合
list.addAll(集合);
//删除数据nihao
list.remove(“nihao”);
//删,往数组添加字符串除数组第三个数据
list.remove(2);
//获取并输出第一个
System.out.println(list.get(0));
//获取输出数组全部数据
System.out.println(list);
②存储指定类型的数据
//创建数组
ArrayList<String> list=new ArrayList<String>();
//往数组添加字符串
list.add(“您好”);
list.add(“nihao”);
③ArrayList与数组的交互
String[] s=new String[list.size()];
//括号里的s参数只代表泛型的类型,不加要强转
s=list.toArray(s);
④构造ArrayList
//无泛型无参数
ArrayList list=new ArrayList();
//有泛型无参数
ArrayList<T> list2=new ArrayList<>();
//有泛型有传值
ArrayList<T> list3=new ArrayList<>(list);
//有泛型指定长度
ArrayList<T> list4=new ArrayList<>(3);
8.2.3 LinkedList 链表
①构造LinkedList
//无泛型无参数
LinkedList list=new LinkedList();
//有泛型无参数
LinkedList<T> list2=new LinkedList<>();
//有泛型有参数
LinkedList<T> list3=new LinkedList<>(list);
②与数组的交互 同ArrayList
③常用方法:
//往集合list中顺序添加元素
List.add(“a”);
//往list的第二个位置添加b
List.add(1,”b”);
//往list的尾部添加元素last
List.addLast(“last”);
//往list的头部添加元素first
list.addFirst(“first”);
//获取并移除列表的头(第一个元素)
List.poll();
//获取但不移除列表的头(第一个元素)
List.peek();
//从此列表所表示的堆栈处弹出一个元素。
List.pop();
//获取并移除此列表的头(第一个元素)。
List.remove();
8.3Set分类和用法
- HashSet:是一个无序无重复的集,它能够快速的查找指定的对象,这是因为它采用了能够适应这个需求的散列码(hash code)。
- LinkedHashSet:有序无重复,因其还使用了一个链表结构来记录插入时候的顺序,当用Iterator迭代时,它会使用这个顺序来迭代。
- TreeSet:是一个有序无重复的集。我们可以用任何的顺序向TreeSet中加入对象。
Set 集合的特点:
[1] Set 不能出现重复的元素
[2] Set 是没有顺序, 无法通过下标访问
[3] 遍历时的顺序, 未必与插入时一致
[4] 线程非安全
add():
①HashSet 根据hashCode判定对象应该存放的位置
②如果位置(即hashCode)一样, 它会调用元素的 equals 方法来判定两个元素是否为同一元素;hashCode一样且equals方法返回值一样,判断为同一元素。
remove():
首先去找该对象的所在位置, 再去执行 equals 判断你指定的 Object 和 它找到的元素是否为同一个元素, 如果是 --> 删除
8.3.1 HashSet 储存无序,不可重复
①构造HashSet 同ArrayList
②概念:HashSet是通过散列的机制来进行信息存储到哈希表,它不能保证集合的顺序, 但是存储的速度非常快,Set里元素不能重复,是唯一的。
③与数组的交互 同ArrayList
④常用方法:
set.add(“a”);
//从此set中移除所有元素
set.clear();
//如果set包含此元素,则返回true
set.contains(Object o);
//如果此set为空,在返回true
set.isEmpty();
//返回对此set中元素进行迭代的迭代器
set.iterator();
//如果指定元素存在此set中,则将其移除
set.remove(Object o);
HashSet的底层其实也是使用了HashMap作为储存元素的容器
HashSet只能插入一个null
8.3.2TreeSet
迭代时,它是以元素的排序规则来进行输出的,即已经排序好再输出
- 自然排序 --->
[1]元素的类型, 必须实现 Comparable, 通过 compareTo() 方法 来判断两元素大小
[2]如果你要插一个元素到 TreeSet 中, 这种元素类型必须实现 Comparable 接口,为了让 TreeSet 可遵循你所指定的顺序标准来确认元素的存放位置
[3]Comparable 接口 的作用: 用作两个同类对象的比较, 只要你复写它的 compareTo() 方法它就能算出两个对象的差值, 通过这个差值来判断谁大谁小
总结:TreeSet自然排序:调用集合元素的compareTo( Object obj )方法(需要实现comparable接口)比较元素间大小关系(0相等,非0不相等),再升序。
例子:
/**
* TreeSet自然排序
*/
public class TreeSetTest {
public static void main(String[] args) {
//自然排序
Set treeset1 = new TreeSet();
//加入Phone实例作为TreeSet集合数据
treeset1.add( new Phone(2));
treeset1.add( new Phone(22) );
//迭代器,循环遍历
Iterator it = treeset1.iterator();
while( it.hasNext() ){
System.out.println( "treeset1: " + it.next() );
}
}
}
//注意实现的接口要有泛型规定,不然都是可以自动转换为Object类型
//这样就可以兼容另一个类型同时加入TreeSet了
/**
* Phone类是TreeSet需要需要传入的数据类型,所以要实现Comparable接口
* 从而实现该接口的compareTo方法,判断新加入的值是否与已加入的值相同,
* 返回0相同,其他不相同
*/
class Phone implements Comparable<Phone>{
int num;
public Phone( int num ){
this.num = num;
}
public String toString(){
return ("Phone num:" + String.valueOf( num ));
}
@Override
public int compareTo( Phone p ) {
if( p == null ){
//不能为null,不能加入
return 0;
}
if( p.getClass() == Phone.class ){
//Phone p1 = (Phone)p;
//同一个类返回两值差
return p.num-this.num;
}
//不为同一类型返回0,不能加入
return 0;
}
}
(2)定制排序 -->
[1] 通过调用以下构造器来实现 TreeSet 定制排序
public TreeSet( Comparator<? super E> comparator ) {
this(new TreeMap<>(comparator));
}
[2] 创建一个类实现 Comparator(接口): 比较器
[3] 覆盖 Comparator 的 compare 方法
例:
/**
* TreeSet定制排序
*/
public class TreeSetTest2 {
public static void main(String[] args) {
//定制排序
//-1为逆序
Set treeset = new TreeSet( new PhoneComparator(-1) );
treeset.add( new Phone1(111) );
treeset.add( new Phone1(1111) );
//迭代器,用于遍历
Iterator it = treeset.iterator();
while( it.hasNext() ){
System.out.println( "treeset1: " + it.next() );
}
}
}
/**
* TreeSet集合实例化时候需要传入的一个实现Comparator接口的类
*/
class PhoneComparator implements Comparator{
int flag;
//传入值非-1的话,返回1。
public PhoneComparator( int flag ){
if( flag!=-1 ){
this.flag = 1;
}else{
this.flag = -1;
}
}
/**
* 比较o1和o2这两个值
*/
@Override
public int compare(Object o1, Object o2) {
//不能有null,有则返回0
if( o1 ==null || o2 ==null ){
return 0;
}
//两个都必须是Phone1类,否则返回0
if( o1.getClass()!= Phone1.class|| o2.getClass()!= Phone1.class){
return 0;
}
//两个都是Phone1,所以转为Phone1类型
Phone1 p1 = (Phone1)o1;
Phone1 p2 = (Phone1)o2;
//返回两个值得差值
return flag*(p1.num- p2.num);
}
}
/**
* Phone1类,其实例用于加入到TreeSet集合
*/
class Phone1 {
int num;
public Phone1( int num ){
this.num = num;
}
public String toString(){
return ("Phone num:" + String.valueOf( num ));
}
}
8.3.3 LinkedHashSet
(1)有序,不可重复
因LinkedHashSet 另外还使用一个链表结构来维护(记录), 插入时的顺序, 当你在用 iterator 迭代时, 它就以这个链表的顺序来进行迭代
Set set = new LinkedHashSet();
8.4Map的分类和使用
Map接口专门处理键值映射数据的存储,可以根据键实现对值的操作
最常用的实现类是HashMap
- HashMap和TreeMap是继承Map,并且Map是以key --value形式显示
- key值不可以重复,但是value值可以重复
若 新key-value 与 旧key-value 相同,旧key不会被覆盖,而旧value会被新value覆盖。
HashMap和HashTable之间的区别:
1.HashTable是一个线程安全的Map实现,HashMap的线程不安全
2.HashTable不容许使用null作为key和value,如果放入将会发生空指针异常,但是HashMap当中的key能够重复,所以最多只有一个key值为空。
8.4.1 HashMap 储存数据一一对应
①存储任意类型数据
如:HashMap map=new HashMap();//创建HashMap集合
map.put(1,”神农”);// 1的值为”神农”
map.put(“二”,99);//二的值为99
map.put(3.3,new Molly());//3.3值为类Molly
map.remove(1);//删除名字为1的对应的数据
map.put(“二”,”伏羲”);//替换二中的值为“伏羲”
System.out.println(map);//输出map的所有数据
System.out.println(map.get(3.3));//输出3.3中的数据
②存储指定类型的数据
如:HashMap<String,Integer> map=new HashMap<String,Integer>();
map.put(“我”,123); //我指定的数值为123
③HashMap的实例有两个参数影响其性能:初始化容量16个单位 和 加载因子0.75
④其中key、value都可以为null
⑤用了一个数组来存放Entry对象(一个内部类),Entry对象的存放位置是通过散列算法来计算的,同一个位置若有多个Entry对象存在,它们是以链表的形式来连结的
⑥LinkedHashMap:用了一个两向链表来维护key的顺序,它保证了插入的顺序与迭代的顺序一致,性能上略低于HashMap
⑦WeakHashMap:存放在WeakHashMap的key一旦垃圾回收器执行回收时,key-value都会被回收,前提是key没有被强引用
8.4.2 SortedMap ---- TreeMap:
①存入map的key必须实现comparable接口,还要复写compareTo方法,如果key没有实现comparable接口,必须使用定制排序来创建TreeMap对象
②TreeMap使用了红黑树对key进行了排序,记录了位置,在迭代的时候,使用红黑树排序顺序进行迭代
③TreeMap的性能比LinkedHashMap还要差
8.4.3 Hashtable
①与HashMap内部实现原理基本相同
②线程安全,主要用于并发操作时,因它线程安全(synchronized)
③初始化容量:11 个单位 加载因子:0.75
④key、value都不能为null
⑤properies用于操作属性文件
8.5 Queue集合
8.6迭代器