JAVA-集合框架
一、集合概念
集合,也就是容器,用于存储数据,也就是装东西的。
面向对象里包含很多容器,变量多了,用数组存起来。数组多了,用二维数组存起来。数据多了,用对象存起来。对象多了,用集合存起来。
但数组也能存对象,为什么要用集合呢?数组就像是一把格式,长度就20-30厘米固定好的,不能改变;而集合是一把卷尺,长度可长可短伸缩式的。
集合的由来:
对象用于封装特有数据,对象多了需要存储,如果对象不确定,就是用集合容器进行存储。
集合的特点:
1.用于存储对象的容器。
2.集合的长度是可变的。
3.集合中不可以存储基本数据类型。
二、集合体系-共性功能
集合容器因为内部的数据结构不同,有多种具体容器。不断向上抽取,就形成了集合框架。
框架的顶层collection接口:集合中的根接口,共性接口。
collection的常见方法(必要掌握):
1.添加:
boolean add(Object obj);
boolean addAll(Collection coll);
2.删除:
boolean remove(Object obj);
boolean removeAll(Collection coll);
void clear();
3.判断
boolean contains(Object obj);
boolean containsAll(Collection coll);
boolean isEmpty();判断集合中是否有元素。
4.获取
int size();获取长度
Iterator iterator();迭代器,取出元素的方式。
该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。所以该迭代器对象是在容器中进行内部实现的。对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可。也就是Iterator 。Iterator 接口就是对所有的Collection容器进行元素取出的公共接口。
5.其他
boolean retainAll(Collection coll);取交集。
Object toArray();将集合转换成数组。
三、方法演示
导包:
import java.util.Collection; import java.util.Arraylist;
演示:
public static void main(String [] args)
{
Collection coll = new Arraylist();
show(coll);
//输出结果为:[a1,a2,a3]
}
public static void show(Collection coll)
{
//1.添加元素。add
coll.add("a1");
coll.add("a2");
coll.add("a3");
System.out.println(coll);
//2.删除元素。remove
coll.remove("a2");//会改变集合的长度
System.out.println(coll);
//清空集合
coll.clear();
}
------------------------------------------分割线------------------------------------------
public static void main(String [] args) { Collection coll1 = new Arraylist(); Collection coll2 = new Arraylist(); show(coll1,coll2); } public static void show(Collection coll1,Collection coll2) { //演示addAll //1.coll1添加元素。 coll1.add("a1"); coll1.add("a2"); //2.coll2添加元素。 coll2.add("a2"); coll2.add("a3"); coll1.addAll(coll2);//将coll2的元素添加到coll1中 System.out.println(coll1); System.out.println(coll2); //输出结果为:[a1,a2,a2,a3] //演示removeAll coll1.remove(coll2);//将两个集合中的相同元素从调用removeAll的集合中删除。 System.out.println(coll1); //输出结果为:[a1,a3] //演示containsAll coll1.containsAll(coll2);//判断列表中是否包含指定collection的所有元素,必须全包含结果为true,否则为false //输出结果为:false,coll1里包含a1,a2,coll2里包含a2,a3。 //演示retainAll coll1.containsAll(coll2);//取交集,与removeAll相反。保留和指定集合中相同的元素在当前集合中,不相同的被干掉 //输出结果为:[a2]。 }
四、迭代器(取出集合中元素的方式)
导包:
import java.util.Iterator;
演示:
public static void main(String [] args) { Collection coll = new Arraylist(); coll.add("a1"); coll.add("a2"); coll.add("a3"); coll.add("a4");//添加元素 Iterator it = coll.iterator(); while(it.hasnext()) { System.out.println(it.next());//该写法导致it不能释放,占用内存空间。 } //开发一定要写这种 for(Iterator it = coll.iterator();it.hasNext();) { System.out.println(it.next());//而利用for循环写的,it在循环结束,即刻释放。 } } //Iterator迭代器所包含的方法: next(), 是返回当前元素, 并指向下一个元素。 hasNext(), 则是判断当前元素是否存在,并指向下一个元素(即所谓的索引)。 remove()将迭代器新返回的元素删除。
五、集合框架-list和set的特点
Collection
|--1.list:有序,(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。
|--2.set:元素不能重复,无序,有可能有序(看巧合)。
六、集合框架-List集合的常见方法
先说一点,list集合是集合中唯一具备增、删、改、查功能的集合,其他的包括collection都不完全具备修改功能。
List特有的常见方法:有一个共性的特点就是都可以操作下标。
1.添加
void add(index,element);
void add(index,collection);
2.删除
Object remove(index);
3.修改
Object set(index,element);
4.获取
Object get(index);
int indexOf(object);
int lastIndexOf(object);
List subList(from,to);
方法演示:
//注意导包-----import java.util.ArrayList;------import java.util.List;导包时要看清有重名的包,一定是util下的 public static void main(String [] args) { List list = new ArrayList(); show(list); } public void show(List list) { //添加 list.add("abc1"); list.add("abc2"); list.add("abc3"); System.out.println(list); //输出结果:[abc1,abc2,abc3] //插入 list.add(1,"abc9"); //输出结果:[abc1,abc9,abc2,abc3],可以看出,和操作数组下标是一样的 //删除 list.remove(2); //输出结果:[abc1,abc9,abc3],下标为2的被删除了 //修改 list.set(1,"abc8"); //输出结果:[abc1,abc8,abc3] //获取 list.get(0); //输出结果:[abc1] //获取子列表 list.subList(1,2);//包含1不包含2嘛 //输出结果:[abc8] } 方法就那些,一个一个尝试一下,就了解了。
七、集合框架-ListIterator接口
上边之前说了用iterator取出元素的方式,这里就不在重复了。
List有自己取出元素的方式。get(角标);这个方法上边也演示过了。但只取了一个元素,Iterator是把元素都取出来了。这还不是易如反掌?循环嘛!
for(int a = 0;a<list.size();a++) { System.out.println(list.get(a)); }
ListIterator演示:
public static void main(String [] args) { List list = new ArrayList(); list.add("abc1"); list.add("abc2"); list.add("abc3");//添加元素 //获取该容器的迭代器 Iterator it = list.iterator(); while(it.hasNext())//通过while循环的方式判断你是否有下个元素 { Object obj = it.next();//如果有就取出这个元素 if(obj.equals("abc2"))//而后判断这个元素是不是abc2 { list.add("abc9");//如果是,就往集合里添加一个新的元素abc9 }else{//如果不是,那就打印这个元素 System.out.println(obj); } } System.out.println(list);//最后把整个集合打印一遍 }
运行结果(异常),在开发中,一定要自己查询这些异常,jdk api文档中自己查询:
经过查询,找到了这个异常的解释:
分析原因:
所以,在迭代过程中,不要使用集合操作元素,容易出现异常。
解决办法:
使用集合的时候,我使用集合来解决。使用迭代器的时候,使用迭代器来解决,他俩不冲突就好了嘛。
回头看迭代,就3个方法,上面说过了。这种情况怎么解决,那就是迭代器本身有局限性,只有3个操作。
可以使用Iterator接口的子接口ListIterator接口来完成迭代中对元素进行更多的操作。
看一下它都包含啥:
这个子接口厉害了。那么怎么拿到这个子接口呢,List里还有个方法上面没说,那就是:
所以,改良代码:
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc1");
list.add("abc2");
list.add("abc3");//首先是添加了3个元素
ListIterator lit = list.listIterator();//获取列表迭代器对象
while(lit.hasNext()){
Object obj = lit.next();
if(obj.equals("abc2")){
lit.set("abc9");
}
}
System.out.println(list);
}
//输出结果:[abc1,abc9,abc3]
这就是ListIterator,它可以实现在迭代过程中完成元素的增删改查。
注意:只有list集合具有该迭代功能。
八、集合框架-List常用子类的特点
List:
|--1.vector:内部是数组数据结构,是同步的。增、删、查询都很慢。(工作原理自行查询)
|--2.ArrayList:内部是数组数据结构,是不同步的,替代了vector。查询的速度很快。(工作原理自行查询)
|--3.LinkedList:内部是链表数据结构,是不同步的。增、删元素的速度很快。(工作原理自行查询)
九、集合框架-HashSet集合
Set元素不可以重复,是无序。
Set接口中的方法和Collection一致。
Set接口下常用:Set集合取出只有一种方式,就是迭代器。
|--HashSet:内部数据结构是哈希表,是不同步的。
方法演示:
创建Students类存放学生信息:
public class Students { private int age; private String name; //构造方法 public Students(int age, String name) { super(); this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
测试类测试:
public class TestB { public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new Students(20,"abc0")); hs.add(new Students(21,"abc1")); hs.add(new Students(22,"abc2")); hs.add(new Students(23,"abc3")); hs.add(new Students(20,"abc4")); Iterator it = hs.iterator(); while(it.hasNext()){ Students st = (Students)it.next();//转类型拆箱 System.out.println(st.getName()+"---"+st.getAge()); } } } //输出结果:可以看出特点-----无序 //abc3---23 //abc0---20 //abc2---22 //abc1---21 //abc4---20
假设,存放相同的对象呢:
public class TestB { public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new Students(21,"abc1")); hs.add(new Students(21,"abc1")); hs.add(new Students(22,"abc2")); hs.add(new Students(22,"abc2")); hs.add(new Students(20,"abc4")); Iterator it = hs.iterator(); while(it.hasNext()){ Students st = (Students)it.next();//转类型拆箱 System.out.println(st.getName()+"---"+st.getAge()); } } } //输出结果:可以看出,HashSet集合数据结构是哈希表,所以存储元素的时候,使用的元素的hashcode()方法来确定位置,如果位置相同,在通过元素的equals来确定是否相同。 //abc2---22 //abc4---20 //abc1---21 //abc2---22 //abc1---21 //hashcode()哈西算法运算存储,计算地址,为每个对象计算了地址,这五个数据,虽然有重复的,但是在哈西里,给出了五个不同的地址,所以,哈西算法里认为这是五个不同的数据。
详细解决方案,Set本来是不能存放相同的数据,咋回事:
先看一下hashcode()哈西算法api文档的解释:
修改Students类,所有的类都继承于Object类,所以重写Object类方法:
直观代码修改Students类,其余代码不变,加入代码:
@Override//这个判断哈希值是否相同 public int hashCode() { System.out.println(this+"------hashcode");//为了直观,让当前对象调用 //字符串有hashcode return name.hashCode()+age; } @Override//这个判断内容是否相同,依赖于哈希值 public boolean equals(Object obj) { System.out.println(this+"------equals"+obj);//为了直观,让当前对象调用 //首先肯定强转 Students st = (Students)obj; return this.name.equals(st.name) && this.age == st.age; } @Override//加入一个tostring显示内容 public String toString() { return name+"---"+age; }
测试类不变:
public class TestB { public static void main(String[] args) { HashSet hs = new HashSet(); hs.add(new Students(21,"abc1")); hs.add(new Students(21,"abc1")); hs.add(new Students(22,"abc2")); hs.add(new Students(22,"abc2")); hs.add(new Students(20,"abc4")); Iterator it = hs.iterator(); while(it.hasNext()){ Students st = (Students)it.next();//转类型拆箱 System.out.println(st.getName()+"---"+st.getAge()); } } }
输出结果:
abc1---21---hashcode--------------第一次存入计算哈希值 abc1---21---hashcode--------------第二次存入计算哈希值 abc1---21---equals--abc1---21----第二次发现哈希值相同,与其比对数据内容是否相同,相同则抛弃,不相同则存入 abc2---22---hashcode--------------下方以此类推 abc2---22---hashcode abc2---22---equals--abc2---22 abc4---20---hashcode abc4---20---equals--abc2---22 abc1---21-----------------------------所以最后输出结果就是这样 abc4---20 abc2---22
到此为止。看一下linkedHashSet:
演示:
public class TestB { public static void main(String[] args) { HashSet hs = new LinkedHashSet(); hs.add("abc1"); hs.add("abc1"); hs.add("abc2"); hs.add("abc2"); hs.add("abc4"); Iterator it = hs.iterator(); while(it.hasNext()){ System.out.println(it.next()); } } } //输出结果: //abc1 //abc2 //abc4
由此可见,需要数据唯一,走Set集合,保证唯一,有序,用LinkedHashSet集合。
十、集合框架-TreeSet集合
API文档定义:
|--TreeSet:可以对Set集合中的元素进行排序。是不同步的。
判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。
TreeSet对元素进行排序的方式之一:
让元素自身具备比较功能,元素就需要实现Comparable接口。覆盖compareTo方法。
如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办?
可以使用TreeSet集合第二种排序方式:
让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。
将该类对象作为参数传递给TreeSet集合的构造函数。
还有个叫二叉树,以后用到在研究。
十一、集合框架-Map集合特点&常用方法
泛型:泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。不详细介绍。
Map<key,value>:
Map集合一次添加一对元素,Collection一次添加一个元素。
Map集合也称双列集合,Collection集合称为单列集合。
其实Map集合中存储的就是键值对。
Map集合中必须保证键的唯一性。
1.添加
void put(key,value):返回一个和key关联的值,如果没有返回null。
2.删除
void clear():清空map集合
value remove(key):根据指定的key删除这个键值对。
3.判断
boolean containsKey(key);
boolean containsValue(value);
boolean isEmpty();
4.获取
value get(key):通过键获取值,如果没有该键返回null。当然也可以通过返回null,来判断是否包含键。
int size():获取键值对的个数。
方法演示:
创建Dog类:
public class Dog { private String name; private int age; public Dog(){} public Dog(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "狗狗 [昵称=" + name + ", 年龄=" + age + "]"; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
创建测试类:
public class MyTest { public static void main(String[] args) { //<>尖括号泛型约束 Map<Dog, String> m = new HashMap<Dog, String>();//注意,key不能为基本数据类型 m.put(new Dog("旺财",5),"1号狗狗"); m.put(new Dog("萌萌",6),"2号狗狗"); m.put(new Dog("笨笨",7),"3号狗狗"); Set<Dog> keySet = m.keySet(); Iterator<Dog> it = keySet.iterator(); while(it.hasNext()){ Dog key = it.next(); String value = m.get(key); System.out.println(key.getName()+":"+key.getAge()+"-----"+value); } } }
输出结果:
萌萌:6-----2号狗狗 旺财:5-----1号狗狗 笨笨:7-----3号狗狗