一 集合的由来
需求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方法。
来源:oschina
链接:https://my.oschina.net/u/4382780/blog/4135389