Java基础第二遍-15-集合
集合
为什么出现集合类?
- 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式
数组和集合都是容器,有何不同
- 数组虽然也可以存储对象,但长度是固定的,集合长度是可变的,数组中可以存储基本数据类型,集合只能存储对象
- 集合不可以存储基本数据类型值
集合类的特点
- 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象
Collection接口
-
集合容器因为数据结构的不同,有多个具体的容器类型,对他们进行共性抽取,形成了集合框架Collection接口,他具备容器的基本操作
-
基本操作:
- add(Object obj);
- addAll(Collection c);
- remove(Object o);
- removeAll(Collection c);//将两集合的相同元素,从调用者的集合中删除
- clear();
- contains(Object o);
- containsAll(Collection c);
- size();
- Iterator iterator();//迭代器
- retainAll(Collection c);//取两集合的交集,调用者集合中保留两集合的交集,其他的删除
- toArray();//将集合转为数组
-
package com.bixiangdong.collection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Demo01 { public static void main(String[] args) { //Collection迭代器 Collection coll = new ArrayList(); for (int i = 0; i < 10; i++) { coll.add("test"+i); } System.out.println(coll); //使用迭代器 //Iterator it = coll.iterator(); //System.out.println(it.next());//返回第一个元素,连续使用默认指针向后移动 //循环,it会始终存在 // while (it.hasNext()){ // System.out.println(it.next()); // } //for循环it只在for中存在,循环结束it销毁 it:迭代器对象 for (Iterator it = coll.iterator(); it.hasNext();) { System.out.println(it.next()); } } }
-
集合框架-迭代器图解
每个具体的容器,都有自己的取出方式,Collection接口的迭代器也是一个接口,具体的容器需要对其进行实现,每个容器都有公共方法hasNext();next();,可以对容器进行操作,所以只需要获取Iterator对象就可以实现元素获取
list集合
-
有序:存入和取出的顺序一致(对存储的元素位置进行精确控制,索引下标)
-
允许出现重复元素
-
list特有的方法
- 都可以操作角标(索引)
- add(index,element);
- add(index,collection);
- remove(index);
- set(index,element);//根据角标,修改元素
- get(index);
- indexOf(object);//从第一个元素查找某个元素,并返回其下标
- lastIndexOf(object);//从最后一个元素开始查找某个元素 并返回其下标
- subList(from,to);//取出两角标间元素
-
list的特有迭代器(只有list集合具有):listIterator,可以对集合实现,增删改查(collection中的Iterator不足以满足对集合操作的需求)
-
list接口的常用集合类:ArrayList、LinkedList、Vector
Vector
- 内部是数组数据结构,数组长度可变(实现原理,创建一个新的数组将原来数组的值赋给新的数组),元素都具有索引
- 是同步的。线程安全
- 查询增删都慢
ArrayList
- 内部是数组数据结构,数组长度可变(实现原理,创建一个新的数组将原来数组的值赋给新的数组),元素都具有索引
- 不同步,线程不安全,代替了Vector
- 查询快(内存空间连续),增删慢
- contains:方法仅依赖equals()方法
LinkedList
-
内部是链表数据结构,增删元素的速度很快(只影响要操作位置的前后两个元素)数组结构的话牵一发而动全身(对某一个位置元素进行增删,其后的所有元素都需要改变位置)
-
不同步的,
-
增删速度快,查询慢(内存空间不连续)
-
特有方法:
- addFirst();
- addLast();
- getFirst();//获取但不移除元素,如果link为空,抛异常
- getLast();
- removeFirst();//获取并移除元素,如果link为空,抛异常
- removeLast();
- isEmpty();
- peekFirst();//获取但不移除元素,如果link为空,返回null
- pollFirst();//获取并移除元素,如果link为空,返回null
堆栈
- 栈:先进后出(像弹夹)
队列
- 先进先出(像管子)
模拟栈式结构
package com.bixiangdong.collection;
import java.util.LinkedList;
public class Demo03 {
public static void main(String[] args) {
Que que = new Que();
que.add("a1");
que.add("a2");
que.add("a3");
que.add("a4");
que.add("a5");
while (!que.isNull()){
//先进后出
System.out.println(que.get());
}
}
}
//模拟栈式存储 先进后出
class Que{
private LinkedList list;
public Que(){
list=new LinkedList();
}
//添加
public void add(Object o){
this.list.addFirst(o);
}
//获取
public Object get(){
return this.list.removeFirst();
}
//判断是否为空
public Boolean isNull(){
return this.list.isEmpty();
}
}
set集合
-
无序(存的顺序与取的顺序不一致)
-
元素不能重复(元素的存储位置是算出来的,所以不能重复)
HashSet
-
内部数据结构是Hash表
-
不同步
Hash表存储图解:
- Hash表确定元素是否相同
- 判断的是两个元素的哈希值是否相同
- 如果相同,再判断内容是否相同
- 判断Hash值是否相同,其实判断的是对象的hashCode方法,判断内容相同判断的是equals方法
- 注意:Hash值不同,不用判断内容是否相同
- 判断的是两个元素的哈希值是否相同
Hash冲突
- Hash地址相同,内容不同(会在同一个地址上进行多个内容存储,有另外一套存储逻辑)
自定义HashCode与equals
package com.bixiangdong.collection; import java.util.HashSet; import java.util.Iterator; public class Demo04 { public static void main(String[] args) { HashSet hashSet = new HashSet(); hashSet.add(new Person("a",12)); hashSet.add(new Person("b",12)); hashSet.add(new Person("c",13)); hashSet.add(new Person("a",12)); Iterator iterator = hashSet.iterator(); while (iterator.hasNext()){ Person next = (Person)iterator.next(); System.out.println(next.toString()); } /* 结果: * Person{name='a', age=12} Person{name='b', age=12} Person{name='c', age=13} * */ } } class Person{ private String name; private int age; @Override public int hashCode() { //因为每个类都继承了Object的hashCode方法 ,创建对象的时候,都具有唯一的内存地址 //但是在使用hashSet进行存储的时候,会出现存储同一个内容的不同对象的情况, //所以可以使用重写hashcode的方式,进行自定义的实现 //自定义hashCode 算出内存地址 return this.name.hashCode()+age*23; } @Override public boolean equals(Object obj) { //如果hashCode的值相同的话,会比对内容,所以也需要重写 Person p = (Person)obj; return this.name.equals(p.name)&&this.age==p.age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public Person(String name, int age) { this.name = name; this.age = 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; } }
- Hash表确定元素是否相同
-
操作元素判断:hashCode和equals
LinkedHashSet
- 有序的
- HashSet的子类
TreeSet
1. HashSet的子类
2. **可以对集合中的元素 进行自然排序(需要实现Comparable重写compareTo方法),返回为0默认对象相同,为负数则对象小于当前对象,为整数则对象大于当前对象** 不是使用hashCode和equals进行比较
3. 倘若不能在类中重写compareTo方法,获取类具有的compareTo方法不符合当前的情况,那么就让TreeSet具有排序功能(**给TreeSet创建一个比较器**)
4. **不同步的**
5. 类重写compareTo
6. ```java
package com.bixiangdong.collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;
// 类重写compareTo
public class Demo05 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(new Person1("afsd",12));
treeSet.add(new Person1("gfsd",13));
treeSet.add(new Person1("hfsd",14));
treeSet.add(new Person1("dfsd",15));
treeSet.add(new Person1("qfsd",13));
treeSet.add(new Person1("jfsd",19));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
Person1 p = (Person1) iterator.next();
System.out.println(p.toString());
}
}
}
class Person1 implements Comparable{
private String name;
private int age;
@Override
public String toString() {
return "Person1{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person1(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public int compareTo(Object o) {
//先按照年龄大小排序,如果年龄相同 则按照姓名排序
Person1 p = (Person1)o;
int temp = this.age-p.age;
return temp==0?this.name.hashCode()-p.name.hashCode():temp;
}
}
```
7. 给TreeSet创建比较器
8. ```java
package com.bixiangdong.collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
public class Demo06 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet(new myComparetor());
treeSet.add(new Person2("afsd",12));
treeSet.add(new Person2("gfsd",13));
treeSet.add(new Person2("hfsd",14));
treeSet.add(new Person2("dfsd",15));
treeSet.add(new Person2("qfsd",13));
treeSet.add(new Person2("jfsd",19));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
Person2 p = (Person2) iterator.next();
System.out.println(p.toString());
}
}
}
class Person2{
private String name;
private int age;
@Override
public String toString() {
return "Person2{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person2(String name, int age) {
this.name = name;
this.age = 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;
}
}
// 自定义比较器
class myComparetor implements Comparator{
@Override
public int compare(Object o1, Object o2) {
Person2 p1 = (Person2)o1;
Person2 p2 = (Person2)o2;
int temp = p1.getAge()- p2.getAge();
return temp==0?(p1.getName().hashCode()-p2.getName().hashCode()):temp;
}
}
```
9. 
相当于后面存储的 都默认比上一个大(或者小)。
Map接口
-
双列集合
-
键值对
-
键唯一
-
无序的(存储顺序与取出顺序不同)
-
常用方法
- put(key,value);//返回上一个和key关联的value,没有则返回null
- clear();//清空
- remove(key);//根据key删除
- containsKey(key);//是否包含key
- containsValue(value);//是否包含value
- isEmpty();//是否为空
- get(key);//根据key获取value 如果有返回value,没有则返回null
- size();//键值对的个数
- keySet();//获取所有的键,返回一个Set集合(key不能重复,所以是Set集合) 通过这个可以遍历Map
-
使用entrySet遍历Map
package com.bixiangdong.collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Demo07 { public static void main(String[] args) { Map map = new HashMap(); map.put(1,"fdsa"); map.put(2,"fdsa"); map.put(3,"fdsa"); map.put(4,"fdsa"); map.put(5,"fdsa"); Set set = map.entrySet(); Iterator iterator = set.iterator(); while (iterator.hasNext()){ Map.Entry next = (Map.Entry)iterator.next(); System.out.print(next.getKey()+"..."); System.out.println(next.getValue()); } } }
HashMap
- 内部数据结构是Hash表
- 不同步
- 允许null作为键,null作为值
Hashtable
- 内部数据结构是Hash表
- 是同步的
- 不允许null作为键,null作为值
TreeMap
- 内部数据结构二叉树
- 不同步
- 可以对Map集合中的键进行排序
- 也可以使用比较器设定排序
TreeMap map = new TreeMap(new MyComparetor());
泛型
-
约束(
ArrayList<String> list = new ArrayList<String>();
) -
安全,类型转换的时候类型一致,不会出现问题(避免强转的麻烦)
-
规范,数据类型一致,便于开发维护
-
简化书写,提高开发效率
-
将运行时期的问题
ClassCastException
转到了编译时期 -
什么时候使用 泛型:对引用的数据类型有需求的时候
-
泛型擦除:泛型用于编译时期,在运行时会将泛型去掉,生成class文件中是没有泛型的,
-
为什么擦除?为了兼容运行的类加载器
-
泛型的补偿机制:在运行时通过获取元素的类型进行转换动作(通过反射机制:getClass().getName()获取类型),不用使用者再进行强转了
-
自定义泛型类
//自定义泛型类 class Tool<T>{ private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } }
-
将泛型定义在方法上
class Tool<T>{ private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } public <W> void show(W w){ System.out.println(w.toString()+"将泛型定义在方法上"); } }
-
方法静态时,泛型只能定义在方法上(返回值类型前,修饰符后)
class Tool<T>{ private T t; public T getT() { return t; } public void setT(T t) { this.t = t; } public <W> void show(W w){ System.out.println(w.toString()+"将泛型定义在方法上"); } public static <W> void show2(W w){ System.out.println(w.toString()+"将泛型定义在方法上"); } }
-
实现类实现接口时,若接口有泛型,需要定义,如果不知道定义哪种类型,需要实现类的泛型与接口泛型保持一致
interface Tool2<T>{ void show(T t); } //T Q是形参 可以命名不一致 class T2<Q> implements Tool2<Q>{ @Override public void show(Q q) { System.out.println(q.toString()); } }
-
泛型的通配符:?
?表示未知类型
class Tool3{ //不明确返回值类型时可以使用? void show(Collection<?> c){ Iterator<?> iterator = c.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } //明确返回值类型时,需要使用字母 <X> X show2(Collection<X> c) { Iterator<?> iterator = c.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } return null; } }
-
泛型通配符的使用---指定某个类型的子类或父类(泛型的限定)
class Tool4{ //接收Person类或者其子类 <X> X show2(Collection<? extends Person> c) { Iterator<? extends Person> iterator = c.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } return null; } //接收Person类或者其父类 <X> X show3(Collection<? super Person> c) { Iterator<? super Person> iterator = c.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } return null; } }
-
一般存元素的时候都是使用上限(? extends E),因为这样取出都是按照上限类型来运算的,不会出现类型安全隐患
-
对集合中的元素进行取出操作时,可以使用下限(?super E)
集合的使用技巧
-
可以重复吗
-
能
- 使用List
- 需要频繁增删吗
- 需要
- LinkedList
- 不需要
- ArrayList
- 使用List
-
不能
- 使用set
- 需要制定顺序吗
- 需要
- TreeSet
- 不需要
- HashSet
- 需要
- 想要一个和存储一致的顺序:LinkedHashSet
-
看到array:想到数组,查询快,有角标
-
看到link:想到链表,增删快,头和尾first、last
-
看到hash:想到哈希表,唯一性,元素要覆盖hashCode和equals方法
-
看到tree:想到二叉树,可排序,比较接口Comparable、Comparator