java-集合框架
一 集合的由来
需求1:20个学员。用Student描述,创建Student对象。
既然对象很多先进行存储---数组。
Student[] stus = new Student[20];
需求2:又来来了一个新学员。
原来的容器用不了。数组的长度是固定的。
解决:
创建一个新数组,将原来数组中的元素复制到新数组中。
麻烦。Java中提供解决问题的对象---->集合。
当数据多了需要存储,需要容器,而数据的个数不确定,无法使用数组,这时可以使用Java中另一个容器--集合。
集合和数组的区别?
数组的长度是固定的。
集合的长度是可变的。
数组中存储的是同一类型的元素,可以存储基本数据类型值。
集合存储的都是对象。而且对象的类型可以不一致。
什么时候使用集合呢?
当对象多的时候,先进行存储。
这些容器怎么区分?
区分的方式:每一个容器的数据结构不一样。
数据存储到的一种方式。
不断的向上抽取过程中,出现体现,形成了集合框架。
最顶层:Collection接口。
学习体系:看顶层,用底层。
了解顶层Collection:
一个容器:添加,删除,获取等功能。
Collection:
|--List:有序的,带索引的,通过索引就可以精确的操作集合中的元素,元素是可以重复的。
List提供了增删改查动作
增加add(element) add(index,element) ;
删除remove(element) remove(index);
修改set(index,element);
查询get(index);
|--Set:
// 演示Collection中带all方法 。 public static void collectionDemo2(Collection c1,Collection c2){ //1,给两个集合添加元素。 c1.add("abc1"); c1.add("abc2"); c1.add("abc3"); c2.add("abc1"); c2.add("abc4"); c2.add("abc5"); //添加所有c2的元素到c1中。 // c1.addAll(c2); // boolean b = c1.containsAll(c2); // System.out.println("b="+b); //删除c1中所有和c2相同的元素。 // c1.removeAll(c2); //保留了c1中和c2相同的元素。 c1.retainAll(c2); //打印。 System.out.println("c1="+c1); } // 演示Collection中常见的一般方法。 public static void collectionDemo(Collection coll) { //1,往集合中添加对象元素。add(Object); coll.add("itcast1"); coll.add("itcast2"); coll.add("itcast3"); //2,删除。 // coll.remove("itcast2"); //3,判断是否包含。 System.out.println(coll.contains("itcast11")); //4,清除。 coll.clear(); //把集合打印一下。 System.out.println(coll);//[itcast1, itcast2, itcast3] }
1.1 集合框架图
图1
1.2 集合的细节@
public static void main(String[] args) { Collection coll = new ArrayList(); /* * 细节。 * 1,集合中存储其实都是对象的地址。 * 2,集合中可以存储基本数值吗?不行,但是jdk1.5以后可以这么写,但是存储的还是对象(基本数据类型包装类对象)。 * 3,存储时提升了Object。取出时要使用元素的特有内容,必须向下转型。 */ //coll存储元素。 // coll.add("abc1");//存储的是对象的引用。 // coll.add(3);//coll.add(Integer.valueOf(3));//自动装箱。 coll.add("itcast1");//Object obj = "itcast1";提升为了Object。 coll.add("hehe");// coll.add("nba"); for (Iterator it = coll.iterator(); it.hasNext();) { Object object = it.next();//取出来的都是Object。需要使用元素的特有方法时需要向下转型。 String str = (String)object; System.out.println(str.length()); } }
1.3 重复的概念equals
里面的重复是指的是引用是否重复,也就是equals方法来比较的,你也可以重写equals 方法。建立自己的比较方式
重复都是有判断依据的,都是通过方法来完成的。
public boolean equals(Object obj) { if(this == obj) return true; if(! (obj instanceof Student)) throw new RuntimeException("类型异常"); Student stu = (Student)obj; return (this.name.equals(stu.name) && (this.age == stu.age)); } public static void main(String[] args) { Collection coll = new ArrayList(); Student stu = new Student(21,"lishi"); coll.add(stu); coll.add(new Student(21,"lisi")); for (Iterator iterator = coll.iterator(); iterator.hasNext();) { Student student = (Student) iterator.next(); System.out.println(student.toString()); }
二 迭代@
也是集合的取出方式
public static void main(String[] args) { /* * 集合的取出方式。 * * 迭代器是Collection集合中通用的取出方式。 * */ //1,创建集合对象。 Collection coll = new ArrayList(); coll.add("abc1"); coll.add("abc2"); coll.add("abc3"); coll.add("abc4"); /*//2.获取容器的迭代器对象。通过iterator方法。 Iterator it = coll.iterator(); //3,使用具体的迭代器对象获取集合中的元素。参阅迭代器的方法 while(it.hasNext()){ System.out.println(it.next()); }*/ for (Iterator it = coll.iterator(); it.hasNext();) { System.out.println(it.next()); } // System.out.println(it.next()); // System.out.println(it.next()); // System.out.println(it.next()); // System.out.println(it.next()); // System.out.println(it.next());//java.util.NoSuchElementException没有这个元素异常。 } }
三 ListIterator
package cn.itcast.api.a.ListIterator; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class ListIteratorDemo { /** * @param args */ public static void main(String[] args) { List list = new ArrayList(); list.add("itcast1"); list.add("itcast2"); list.add("itcast3"); list.add("itcast4"); /* * 获取集合中的元素。 * 如果集合中有元素等于 itcast2.那么就插入一个新的元素,java。 * * 引发了java.util.ConcurrentModificationException * 在迭代过程中,使用了集合的方法对元素进行操作。导致迭代器并不知道集合中的变化,容易引发数据的不确定性。 * * 解决:在迭代时,不要使用集合的方法操作元素。 * 那么想要在迭代时对元素操作咋办?可以使用迭代器的方法操作。 * 可是很遗憾:迭代器的方式只有 hasNext() ,next(),remove(); * Iterator有一个子接口ListIterator可以完成该问题的解决。如何获取该子接口对象呢? * 通过List接口中的listIterator()就可以获取, * 记住:该列表迭代器只有List接口有。而且这个迭代器可以完成在迭代过程中的增删改查动作。 * * */ // Iterator it = list.iterator(); // 获取列表迭代其对象 ListIterator it = list.listIterator(); while(it.hasNext()){ Object obj = it.next(); if("itcast2".equals(obj)){ // list.add("java"); // it.add("java"); it.set("java"); } } System.out.println(list); } }
四 List
|--List:有序的,带索引的,通过索引就可以精确的操作集合中的元素,元素是可以重复的。
List提供了增删改查动作
增加add(element) add(index,element) ;
删除remove(element) remove(index);
修改set(index,element);
查询get(index);
|--Vector:可以增长的数组结构。同步的。效率非常低。已被ArrayList替代。
|--ArrayList:是数组结构,长度是可变的(原理是创建新数组+复制数组),查询速度很快,增删较慢,不同步的。
|--LinkedList:是链表结构,不同步的,增删速度很快,查询速度较慢。
可用于实现堆栈,队列。
堆栈:先进后出 First in Last Out FILO 手枪弹夹。
队列:先进先出 First in First Out FIFO 排队买票。
List可以存储重复元素的,如果需求中要求容器中的元素必须保证唯一性。
4.1 LinkedList
package cn.itcast.api.b.list.subclass; import java.util.LinkedList; public class LinkedListDemo { /** * @param args */ public static void main(String[] args) { /* * LinkedList做个了解。 * 特有:围绕头和尾展开定义的。First Last。 * addFirst(); * addLast(); * * getFirst();:获取头部元素。 * getLast(); * * removeFirst();获取头部元素,并删除头部元素。 * removeLast(); */ LinkedList link = new LinkedList(); link.addFirst("abc1"); link.addFirst("abc2"); link.addFirst("abc3"); // System.out.println(link.getFirst()); // System.out.println(link.getFirst()); // System.out.println(link.removeFirst()); // System.out.println(link.removeFirst()); // System.out.println(link.removeFirst()); // System.out.println(link.removeFirst()); while(!link.isEmpty()){ System.out.println(link.removeLast()); } } }
4.1.1 用LinkList模拟栈和队列
package cn.itcast.api.b.list.subclass; import java.util.LinkedList; public class LinkedListTest { /** * @param args */ public static void main(String[] args) { /* * 面试题:用LinkedList模拟一个堆栈或者队列数据结构。 * 创建一个堆栈或者队列数据结构对象,该对象中使用LinkedList来完成的。 * * 自定义堆栈结构。作业。 */ //创建一个队列对象。 Queue queue = new Queue(); //往队列中添加元素。 queue.myAdd("itcast1"); queue.myAdd("itcast2"); queue.myAdd("itcast3"); queue.myAdd("itcast4"); while(!queue.isNull()){ System.out.println(queue.myGet()); } } } /** * 定义一个队列数据结构。Queue */ class Queue{ //封装了一个链表数据结构。 private LinkedList link; /* * 队列初始化时,对链表对象初始化。 */ Queue(){ link = new LinkedList(); } /** * 队列的添加元素功能。 */ public void myAdd(Object obj){ //内部使用的就是链表的方法。 link.addFirst(obj); } /** * 队列的获取方法。 */ public Object myGet(){ return link.removeLast(); } /** * 判断队列中元素是否空,没有元素就为true。 */ public boolean isNull(){ return link.isEmpty(); } }
4.1.2 去重复元素
4.2 equals
package cn.itcast.api.b.list.subclass; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import cn.itcast.domain.Student; public class ArrayListTest { /** * @param args */ public static void main(String[] args) { List list = new ArrayList(); // list.add("abc1"); // list.add("abc2"); // list.add("abc1"); // list.add("abc2"); // list.add("abc1"); // list.add("abc2"); list.add(new Student("lisi1",21)); list.add(new Student("lisi2",22)); list.add(new Student("lisi1",21)); list.add(new Student("lisi2",22)); list.add(new Student("lisi1",21)); getSingleElement(list);//去除重复元素。 System.out.println(list); } /* * 案例:去除List集合中的重复元素。 * * 思路: * 1,先创建一个临时容器。用于存储唯一性的元素。 * 2,遍历原容器,将遍历到的元素到临时容器中去判断,是否存在。 * 3,如果存在,不存储到临时容器,如果不存在,存储到临时容器中。 * 4,遍历结束后,临时容器中存储的就是唯一性的元素。 * 5,如果需要将这些唯一性的元素保留到原容器中,只要将原容器清空,将临时容器中的元素添加到原容器中即可。 * */ public static void getSingleElement(List list){ //1,创建一个临时容器。 List temp = new ArrayList(); //2,遍历原容器。 for (Iterator it = list.iterator(); it.hasNext();) { Object obj = it.next(); //对遍历到的每一个元素都到临时容器中去判断是否包含。 if(!temp.contains(obj)){//如果不存在, temp.add(obj);//添加到临时容器。 } } //唯一性的元素已经被记录到临时容器中。 //清空原容器中的元素。 list.clear(); //把临时容器中的元素添加到原容器中。 list.addAll(temp); } }
/**
* 重写了equals方法,建立Student对象判断相同的依据。
*/
@Override
public boolean equals(Object obj) {
System.out.println("equals");
if (this == obj)
return true;
if (!(obj instanceof Student)) {
throw new ClassCastException();
}
Student stu = (Student) obj;
return this.name.equals(stu.name) && this.age == stu.age;
}
五 set
|--Set:不包含重复元素的集合,不保证顺序。而且方法和Collection一致。Set集合取出元素的方式只有一种:迭代器。
|--HashSet:哈希表结构,不同步,保证元素唯一性的方式依赖于:hashCode(),equals()方法。查询速度快。
|--TreeSet:可以对Set集合中的元素进行排序。使用的是二叉树结构。如何保证元素唯一性的呢?
使用的对象比较方法的结果是否为0,是0,视为相同元素不存。
元素的排序比较有两种方式:
1,元素自身具备自然排序,其实就是实现了Comparable接口重写了compareTo方法。
如果元素自身不具备自然排序,或者具备的自然排序不是所需要的,这时只能用第二种方式。
2,比较器排序,其实就是在创建TreeSet集合时,在构造函数中指定具体的比较方式。
需要定义一个类实现Comparator接口,重写compare方法。
5.1 HashSet
HashSet:哈希表结构,不同步,保证元素唯一性的方式依赖于:hashCode(),equals()方法。查询速度快。
注意:重点是哈希冲突是怎么解决的
5.2 重复元素去掉hashCode,equals
package cn.itcast.api.c.set; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import cn.itcast.domain.Student; public class SetDemo { /** * @param args */ public static void main(String[] args) { Set set = new HashSet(); /* // 去除了字符串中的重复元素。 set.add("nba"); set.add("java"); set.add("haha"); set.add("itcast"); set.add("haha"); set.add("java"); set.add("java"); set.add("java"); set.add("itcast");*/ /* * * 为什么学生对象没有保证唯一性呢? * 通过对哈希表的分析。 * 存储元素时,先调用了元素对象的hashCode()方法,而每个学生对象都是新建立的对象, * 所以hashCode值都不相同,也就不需要判断equals了。 * 想要按照需求同姓名同年龄来保证学生对象的唯一性咋办? * 不能使用Object中hashCode方法,需要重新定义hashCode的算法内容。 * 简单说:重写hashCode方法。 */ set.add(new Student("lisi1",21)); set.add(new Student("lisi2",22)); set.add(new Student("lisi1",21)); set.add(new Student("lisi2",22)); set.add(new Student("lisi1",21)); "abc1".hashCode(); for (Iterator it = set.iterator(); it.hasNext();) { System.out.println(it.next()); } } }
、
5.3 TreeSet
|--TreeSet:可以对Set集合中的元素进行排序。使用的是二叉树结构。如何保证元素唯一性的呢?
使用的对象比较方法的结果是否为0,是0,视为相同元素不存。
元素的排序比较有两种方式:
1,元素自身具备自然排序,其实就是实现了Comparable接口重写了compareTo方法。
如果元素自身不具备自然排序,或者具备的自然排序不是所需要的,这时只能用第二种方式。
2,比较器排序,其实就是在创建TreeSet集合时,在构造函数中指定具体的比较方式。
需要定义一个类实现Comparator接口,重写compare方法
Comparable和comparator 其实是一种策略模式
演示TreeSet
package cn.itcast.api.c.set; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import cn.itcast.domain.Student; public class TreeSetDemo { /** * @param args */ public static void main(String[] args) { //演示TreeSet, Set set = new TreeSet(); /* set.add("nba"); set.add("abc"); set.add("java"); set.add("aaa"); */ /* * TreeSet的add方法内部最终实现: * 需要将元素转成Comparable类型,为什么?因为这个类型具备排序的能力。 * 这个类型中有一个专门为排序提供了一个compareTo方法。 * 如果要让学生具备比较排序的功能,需要让学生扩展功能,实现Comparable接口。 */ set.add(new Student("lisi6",21)); set.add(new Student("lisi8",22)); set.add(new Student("lisi5",25)); set.add(new Student("lisi3",23)); set.add(new Student("lisi7",20)); for (Iterator it = set.iterator(); it.hasNext();) { System.out.println(it.next()); } } }
如果不重写comparable里面的方法报的异常
重写之后
/*
* 注意:在比较时,必须明确主次。主要条件相同,继续比较次要条件。
*/
int temp = this.age - stu.age;
return temp==0?this.name.compareTo(stu.name):temp;
TreeSet比较结构
这里好像二叉排序树
TreeSet comparator
package cn.itcast.api.c.set; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import cn.itcast.api.d.comparator.ComparatorByName; import cn.itcast.domain.Student; public class TreeSetDemo2 { /** * @param args */ public static void main(String[] args) { //在创建TreeSet集合对象时明确比较器。 Set set = new TreeSet(new ComparatorByName()); /* * 想要按照学生的姓名排序,说明学生中的自然排序不是所需要的。 * 这时只能使用比较器。ComparatorByName。 */ set.add(new Student("lisi6",21)); set.add(new Student("lisi8",22)); set.add(new Student("lisi5",25)); set.add(new Student("lisi3",23)); set.add(new Student("lisi7",20)); for (Iterator it = set.iterator(); it.hasNext();) { System.out.println(it.next()); } } }
总结:
到此为止:再往集合中存储对象时,通常该对象都需要覆盖hashCode,equals,
同时实现Comparale接口,建立对象的自然排序。通常还有一个方法也会复写toString();
5.4 练习
package cn.itcast.api.c.set; import java.util.Collections; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import cn.itcast.api.d.comparator.ComparatorByLength; public class TreeSetTest { /** * @param args */ public static void main(String[] args) { /* * 练习:要对字符串进行长度(由短到长)排序。 * 思路: * 1,字符串之所以可以排序,因为是已经实现Comparable接口重写compareTo方法。 * 建立了字符串的自然排序。 * 2,但是自然排序不是需求中所需要的。咋办? * 只能使用比较器。需要自定义一个比较器。 * */ Set set = new TreeSet(Collections.reverseOrder(new ComparatorByLength())); set.add("abc"); set.add("haha"); set.add("xixi"); set.add("z"); set.add("hiahia"); for (Iterator it = set.iterator(); it.hasNext();) { System.out.println(it.next()); } } }
5.5 LinkedHashSet
Hash查询快,但是查出来是没有顺序的。我想怎么存进去的,怎么取出来
这时要用到LinkedHashSet;
package cn.itcast.api.c.set; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; public class LinkedHashSetDemo { /** * @param args */ public static void main(String[] args) { /* * 提高唯一性元素的查询效率,还想有序,可使用HashSet的子类LinkedHashSet. */ Set set = new LinkedHashSet(); set.add("abcd"); set.add("hahahah"); set.add("java"); set.add("itcast"); for (Iterator it = set.iterator(); it.hasNext();) { System.out.println(it.next()); } } }
5.6 foreach循环
package cn.itcast.api.e.foreach; import java.util.ArrayList; import java.util.List; public class ForeachDemo { /** * @param args */ public static void main(String[] args) { /* * foreach:其实就是增强for循环。 * 格式: * for(元素的数据类型 变量 : Collection集合or数组){} * 用于遍历Collection和数组。通常只能遍历元素,不要在遍历的过程中做对集合元素的操作。 * * 和老式的for循环有什么区别? * 注意:新for循环必须有被遍历的目标。目标只能是Collection或者是数组。 * 建议:遍历数组时,如果仅为遍历,可以使用增强for如果要对数组的元素进行 操作,使用老式for循环可以通过角标操作。 * */ List list = new ArrayList(); list.add("itcast1"); list.add("itcast2"); list.add("itcast3"); list.add("itcast4"); // for (Iterator it = list.iterator(); it.hasNext();) { // Object obj = it.next(); // System.out.println(obj); // } for(Object obj : list){//简化。 System.out.println(obj); } } }
5.7 查阅的小技巧
看集合对象的小技巧:★★★★★★
集合分体系。List Set
子类对象的后缀名是所属体系,前缀名是数据结构名称。
List:新出的子类都是以List结尾的,通常都是非同步的。
|--ArrayList :看到array,就知道数组,查询速度快。
|--LinkedList:看到link,就知道链表,增删速度快。
Set:
|--HashSet:看到hash,就知道哈希表,查询速度更快,并想到元素唯一,通过hashCode(),equals方法保证唯一性。
|--TreeSet:看到tree,就知道二叉树,可以排序,排序想到Comparable-compareTo Comparator--compare方法。
作者:8亩田
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.
本文如对您有帮助,还请多帮 【推荐】 下此文。
如果喜欢我的文章,请关注我的公众号
如果有疑问,请下面留言