集合
1. 概念:
为了对多个对象进行操作,Java提供了集合类。
1.1 数组和集合的区别:
长度不同:数组是固定长度,集合是不固定长度。
内容不同:数组存储的数据类型相同,集合可以存储不同数据类型。
类型不同:数组可以存储基本数据类型和引用数据类型,集合只能存储引用数据类型。
1.2 集合的特点:
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
1.3 集合继承体系:
Collection:List,Set
List:ArrayList,Vector,LinkedList
Set:HashSet,TreeSet
2. collection
2.1 概述:
collection是集合层次中的根接口。集合表示一组对象的容器,这些对象有重复的,有不重复的,有有序的,有无无序的。
2.2 collection中的方法:
1. 添加方法:
public boolean add(Object o):给集合添加元素
public boolean addAll(Collection c):将一个集合添加到另一个集合当中
2. 删除出方法:
public void clear():从该集合中删除所有元素
public boolean remove(Object o):从该集合中删除指定元素
public boolean removeAll(collection<?> c):从该集合中删除指定集合的元素
3. 判断方法:
public boolean contains(Object o):判断该集合是否含有指定元素
public boolean containsAll(Collection<?> c):判断该集合是否含有指定集合所有元素
public boolean isEmpty():判断集合是否为空
4. 获取元素方法:
public Iterator<E> iterator():通过迭代器来获取元素
public Object[] toArray():返回包含此集合所有元素的数组
public boolean retainAll(Collection c):求两个集合交集
5. 获取长度方法:
int size():获取集合长度
/*
普通方法使用
*/
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList();
//添加元素
c.add("Hello");
c.add("乐乐");
c.add("java");
System.out.println("c:"+c);//说明重写了toString()方法
//删除元素
//c.clear();//删除所有元素
c.remove("Hello");//删除指定元素
System.out.println("remove:"+c.remove("乐乐"));//删除存在的元素返回true
System.out.println("remove:"+c.remove("啦啦啦"));//删不除存在的元素返回false
System.out.println("c:"+c);
//判断
System.out.println("contains:"+c.contains("java"));//集合中存在该元素返回true
System.out.println("contains:"+c.contains("乐乐"));//集合中不存在该元素返回false
System.out.println("isEmpty:"+c.isEmpty());//集合为空返回true
//获取集合长度
System.out.println("size:"+c.size());
}
/*
集合与集合之间方法的使用
*/
public static void main(String[] args) {
//创建集合 多态
Collection c1 = new ArrayList();
Collection c2 = new ArrayList();
//添加元素
c1.add("a1");
c1.add("a2");
c1.add("a3");
c2.add("b1");
c2.add("b2");
c2.add("b3");
c2.add("a1");
System.out.println("c1:"+c1);
System.out.println("c2:"+c2);
//addAll(Collection c):将指定集合中所有元素添加到该集合中
c1.addAll(c2);
System.out.println("c1:"+c1);
//removeAll(Collection c):删除该集合中指定集合的元素
c1.removeAll(c2);
System.out.println("c1:"+c1);
//containsALL(Collection c):判断该集合是否含有指定集合的所有元素
System.out.println(c1.containsAll(c2));//全包含指定集合返回true
//c1.addAll(c2);
//System.out.println(c1.containsAll(c2));
//retainAll(Collection c):求交集
//如果调用者集合元素
c1.add("b1");
System.out.println(c1.retainAll(c2));//该集合发生改变返回true
System.out.println("c1:"+c1);
}
3.迭代器
Iterator iterator():专门遍历集合的迭代器
3.1 Iterator接口中的方法:
public Object next();返回集合中的下一个元素
public Object hasNext():如果next()返回一个元素而不是一个异常,则返回true
注意:NoSuchElementException:不存在这个元素异常
3.2 集合使用步骤
- 创建集合对象
- 创建集合元素
- 将元素添加到集合中
- 遍历集合
/*
遍历学生对象集合
*/
public static void main(String[] args) {
Collection c = new ArrayList();
Student s1 = new Student("王宝强",25);
Student s2 = new Student("张卓帆",19);
Student s3 = new Student("张三",22);
c.add(s1);
c.add(s2);
c.add(s3);
//声明一个迭代器
Iterator i = c.iterator();
//判断有没有下一个元素,并接收它
while(i.hasNext()){
Object o = i.next();
Student s = (Student)o;//Student s = (Student)i.next();
System.out.println(s.getName()+"----"+s.getAge());
}
}
4. List
4.1 概述:
有序集合:插入元素顺序和取出元素顺序一致
允许有重复元素
4.2 List中的特有方法
public void add(int index,Object element):向指定位置添加元素
public Object remove(int index):移除指定位置元素并返回该元素
public Object get(int index):获取指定位置元素
public Object set(int index,Object element):替换指定位置元素,并返回原来的元素
public static void main(String[] args) {
List l = new ArrayList();
l.add("hello");
l.add("love");
l.add("away");
l.add("hello");
System.out.println(l);
//添加元素
l.add(4,"fall");//四个元素:0-4,否则数组下标越界
//l.add(5,"fall") 数组下标越界
System.out.println(l);
//删除元素
l.remove(2);
//System.out.println("remove:"+l.remove(2));删除哪个元素,并返回该元素
System.out.println(l);
//获取元素
System.out.println("get:"+l.get(2));
//替换元素
System.out.println("set:"+l.set(1,"you"));
System.out.println(l);
}
List特有的迭代器:
public ListIterator ListIterator():迭代器
public Object previous():返回集合中上一个元素,并且光标前移
public boolean hasPrevious():返回true如果遍历反向别表,列表迭代器有多个元素
注意:当我们光标没有正向遍历的时候,它就在其实位置,这时候如果你要逆向输出,则不会有输出结果
public static void main(String[] args) {
List l = new ArrayList();
l.add("hello");
l.add("love");
l.add("getAway");
// for (int i = 0;i<l.size();i++){
// System.out.println(l.get(i));
// }
ListIterator li = l.listIterator();
while(li.hasNext()){
String s = (String)li.next();
System.out.println(s);
}
System.out.println("===============");
//逆序输出
while(li.hasPrevious()){
String s = (String)li.previous();
System.out.println(s);
}
}
练习:想要在集合hello,world,java中判断如果world存在就添加i love
/**
* 注意:ConcurrentModificationException:并发修改异常
* 当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常
* 为什么会出现这种异常?
* 首先我们知道,迭代器是依赖集合存在的,我们的迭代器已经在刚开始就固定了
* 这时候,我们通过迭代器来遍历集合,而集合中突然添加了一个元素,我们的迭代器并不知道
* 所以就会发生并发修改异常
* 如何解决这种问题:
* 1. 迭代器迭代,迭代器修改
* 元素插入我们想要插入的位置
* 2. 集合遍历,集合修改
* 元素插入到集合的最后
*
*/
public static void main(String[] args) {
List l = new ArrayList();
l.add("hello");
l.add("world");
l.add("java");
//通过迭代器
ListIterator li = l.listIterator();
while (li.hasNext()){
String s = (String)li.next();
if (s.equals("world")){
// l.add(2,"i love");并发修改异常 ConcurrentModificationException
li.add("love");
}
}
//通过集合,使用for循环
for(int i = 0;i<l.size();i++){
String s = (String) l.get(i);
if (s.equals("world")){
l.add("hate");
}
System.out.println(s);
}
System.out.println(l);
}
}
5. List的子类
ArrayList:
底层数据结构是数组,所以查询快,增删慢。
又因为线程不同步,所以效率高。
Vector:
底层数据结构是数组,所以查询快,增删慢。
又因为线程同步,所以效率低。
LinkedList:
底层数据结构是链表,所以查询慢,增删快。
又因为线程不同步,所以效率高。
我们到怎么样使用这些集合呢?
-
首先考虑线程是否安全:
如果需要线程安全,选择Vector
-
看业务需求:
如果对查询有要求,选择ArrayList
如果对增删有要求,选择LinkedList
-
如果我们啥都不知道,就选择ArrayList
5.1 Vector中特有方法:
public void addElement(Object o):添加元素
public Object elementAt(int index):按照索引获取元素
public Eumeration elements():其实是一个迭代器
public boolean hasMoreElements():判断是否有下一个元素
public Object nextElement();返回下一个元素
vector练习:
public static void main(String[] args) {
Vector v = new Vector();
//添加元素
v.addElement("hello");
v.addElement("world");
v.addElement("java");
//获取元素
for(int i = 0;i<v.size();i++){
String s = (String)v.elementAt(i);
System.out.println(s);
}
System.out.println("==================");
//迭代器
Enumeration e = v.elements();
while(e.hasMoreElements()){
String s = (String)e.nextElement();
System.out.println(s);
}
}
5.2 LinkedList的特有方法:
public void addFirst(Object o) 及 addLast(Object o)
public Object getFirst() 及 getLast()
public Object removeFirst() 及 removeLast()
练习:
public static void main(String[] args) {
LinkedList l = new LinkedList();
l.add("hello");
l.add("world");
l.add("java");
//添加元素
l.addFirst("love");
l.addLast("YOU");
System.out.println(l);
//删除元素
l.removeFirst();
l.removeLast();
System.out.println(l);
//获取元素
String s1 = (String)l.getFirst();
String s2 = (String)l.getLast();
System.out.println(s1);
System.out.println(s2);
}
6.泛型
6.1 泛型的概述
由于添加元素的类型不确定,我们可以添加任意类型的元素,那么在遍历的时候可能就会出现问题。
我们之前学习过数组,就可以声明一种类型,使得在添加元素时,数据类型不匹配的情况就会出现异常。
那么集合中有没有这样的方法呢?从而引出了:泛型
-
泛型的概念:泛型是一种特殊的类型,可以理解为参数化类型,也就是可以把类型像参数一样传递。
-
泛型的用途:一般多用于集合。
-
泛型的格式:<泛型类型>
-
泛型的好处:1.提高了程序的安全性,将原本运行时期的错误提前在编译阶
段就暴露出来
2.省去了类型的强制转换的麻烦
import java.util.ArrayList;
import java.util.Iterator;
public class TestArrayListDemo03 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("dll");
arrayList.add("love");
arrayList.add("yx");
//arrayList.add(5);
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()){
//String s = (String)iterator.next();
String s = iterator.next();
System.out.println(s);
}
}
}
6.2 泛型的应用
-
泛型类
把泛型定义在类上 public class 类名 <泛型类型1,……> 泛型类型必须是引用类型
/** * 泛型类: * 概述:把泛型定义在类上 * 格式:public class 类名 <泛型类型1,……> 这里泛型类型可以有多个 * 注意:泛型类型必须是引用数据类型,而且要符合我们的命名规范 */ public class TestFanxingDemo05 { public static void main(String[] args) { MyFanxing<String> myFanxing = new MyFanxing<String>(); myFanxing.setObj("金城武"); String s1 = myFanxing.getObj(); myFanxing.setObj("21"); String s2 = myFanxing.getObj(); System.out.println(s1+s2); MyFanxing<Integer> myFanxing1 = new MyFanxing<Integer>(); myFanxing1.setObj(15); Integer obj = myFanxing1.getObj(); System.out.println(obj); } } class MyFanxing<T>{ private T obj; public T getObj(){ return obj; } public void setObj(T obj){ this.obj = obj; } }
-
泛型方法
把泛型定义在方法上 public <泛型类型> 返回值类型 方法名(泛型类型)
/** * 泛型方法:将泛型定义在方法上 * 格式:public <泛型类型> 返回值类型 方法名(泛型类型) */ public class TestFanxingDemo07 { public static void main(String[] args) { MyFanxing3 myFanxing3 = new MyFanxing3(); myFanxing3.show("dll"); myFanxing3.show(21); myFanxing3.show(true); } } class MyFanxing3{ public <T> void show(T t){ System.out.println(t); } }
-
泛型接口
把泛型定义在接口上 public interface 接口名 <泛型类型>
/** * 泛型接口 * 概述:将泛型定义在接口上 * 格式:public interface 接口名 <泛型类型> */ public class TestFanxingDemo08 { public static void main(String[] args) { //方法1: MyFanxing4<String> m1 = new RealizeMyFanxing4(); m1.show("dll"); //方法2 MyFanxing4 <String> m2 = new RealizeMyFanxing5<String>(); m2.show("yx"); MyFanxing4<Integer> m3 = new RealizeMyFanxing5<Integer>(); m3.show(21); MyFanxing4<Boolean> m4 = new RealizeMyFanxing5<Boolean>(); m4.show(true); } } interface MyFanxing4<T>{ public abstract void show(T t); } //在使用泛型接口时,需要知道到底什么明确使用泛型类型 //1.在实现类的时候就已经明确了泛型类型 //2.在使用的时候才知道泛型类型到底是什么 class RealizeMyFanxing4 implements MyFanxing4 <String>{ @Override public void show(String s) { System.out.println(s); } } class RealizeMyFanxing5 <T> implements MyFanxing4<T>{ @Override public void show(T t) { System.out.println(t); } }
6.3 泛型的高级应用
泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意类型的Java类
<? extends E>
向上限定,只能是E及其子类
<? super E>
向下限定,只能是E及其父类
7. 增强for
-
概述:简化数组和collection的遍历
-
格式:
for(元素数据类型 变量名:数组或者Collection集合){ 方法体 }
import java.util.ArrayList; /** * 增强for: * 概述:简化数组或者集合的遍历 * 格式:for(元素数据类型 变量名:集合或者数组对象){ * 通过变量名来使用元素 * } * 好处:简化了遍历 * 弊端:我们在使用它之前,需要判断遍历的数组或者集合是否为空,因为它会发生空指针异常 * 注意:我们的增强for其实就是用来代替迭代器的 */ public class TestForDemo01 { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; for (int x:arr){ System.out.println(x); } System.out.println("==========="); ArrayList<String> arrayList = new ArrayList<String>(); arrayList.add("hello"); arrayList.add("world"); arrayList.add("java"); for (String s:arrayList){ System.out.println(s); } //需要先判断需要遍历的数组是否为空 arrayList = null; if (arrayList!=null){ for (String s:arrayList){ System.out.println(s); } } } }
8. Set
8.1 set集合
import java.util.HashSet;
import java.util.Set;
/**
* Set接口:
* 概述:不包含重复元素的集合,它是无序的(存入顺序和取出顺序不一致),相对于List集合来说的。
* 并且List集合是有序的(指存入数据顺序和取出数据顺序一致)
*
*/
public class TestSetDemo01 {
public static void main(String[] args) {
Set<String> s = new HashSet<>();
s.add("dll");
s.add("love");
s.add("yx");
s.add("yx");
System.out.println(s);
}
}
8.2 HashSet集合
import java.util.HashSet;
/**
* HashSet:
* 概述:一个实现set接口的类,它不能保证迭代顺序,特别是它不保证在一段时间内的迭代顺序。
*
* HashSet是如何保证元素的唯一性:
* 1.底层数据结构是哈希表
* 2.哈希表底层依赖了两个方法
* 首先会判断hashCode()是否相等,
* 如果不相等就说明两元素不一样,直接添加;
* 如果相等,就会继续判断equals()方法,
* 如果equals方法返回false,则添加元素。
*
*/
public class TestHashSetDemo02 {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
hs.add("hello");
hs.add("dll");
hs.add("lyy");
hs.add("lyy");
for (String s:hs){
System.out.println(s);
}
//如果元素相等,则hashCode值相等,保证元素的唯一性!
System.out.println("lyy".hashCode());
System.out.println("lyy".hashCode());
}
}
8.3 LinkedHashSet
import java.util.LinkedHashSet;
/**
* LinkedHashSet:
* 概述:它是一个有序的切元素唯一的集合
* 它的底层是链表和哈希表
* 唯一性由哈希表保证
* 有序性由链表保证
*/
public class TestLinkedHashSetDemo04 {
public static void main(String[] args) {
LinkedHashSet<String> l = new LinkedHashSet<String>();
l.add("dll");
l.add("wrh");
l.add("wrh");
l.add("yx");
for (String s:l){
System.out.println(s);
}
}
}
8.4 TreeSet
import java.util.TreeSet;
/**
* TreeSet:
* 概述:基于TreeMap的实现,使用元素的自然顺序或者比较器排序。这取决于我们选取的构造方法
* 特点: 元素唯一,且根据选取的构造方法来给元素排序
* 通过刚才查看源码,发现它十几场是实现了自然排序结构Comparable,并且调用Comparable
*
* 红黑树存取原理:
* 第一个元素作为红黑树的节点,后面的元素依次和之前的元素比较,如果小了往左放,如果大了往右放,如果相等就不放
*
*/
public class TestTreeSetDemo05 {
public static void main(String[] args) {
//因为我们使用的是无参构造,所以这里是按照自然顺序排序
TreeSet<Integer> t = new TreeSet<Integer>();
t.add(20);
t.add(18);
t.add(23);
t.add(22);
t.add(17);
t.add(24);
t.add(19);
t.add(18);
t.add(24);
for (Integer i:t){
System.out.println(i);
}
}
}
9. 总结
Collection:
-
List:有序切可以重复
-
ArrayList
底层数据结构是数组,查询快,增删慢,线程不安全,效率高
-
Vector
底层数据结构是数组,查询快,增删慢,线程安全,效率低
-
LinkedList
底层数据结构是链表,查询慢,增删快,线程不安全,效率高
-
-
Set
-
HashSet:无序切唯一
底层数据结构是哈希表,它的底层依赖hashCode和equals方法来实现元素唯一
-
LinkedHashSet:有序切唯一
底层数据结构是哈希表和链表,哈希表保证元素唯一性,链表保证元素有序
-
-
TreeSet:有序切唯一
底层数据结构是红黑树,它的排序由构造方法决定,一种是自然排序,一种是比较器排序
-