一(集合介绍):
在前面的章节有所提到,数组可以保存多个对象,但是在某些情况下无法确定具体保存多少个对象,此时数组不再适用,因为在创建数组时,必须制定数组长度。
数组的特点:
1.数组初始化时,长度就确定了!(如果要存放的内容超出长度,需要使用copyOf来扩容)
2.数组声明的类型,就决定了每个元素初始化时的类型
数组的缺点:
1.数组初始化后,长度不可变,不便于扩展
2.数组中提供的属性和方法太少,不便于进行添加 删除 插入等操作,且效率不高
为了保存这些数目不确定的对象,Java中提供了一系列特殊的类,统称为集合,集合可以存储任意类型的对象,并且长度可变。
Java中的集合就像一个容器,专门用来存储Java对象(实际上是对象的引用,但习惯称为对象),这些对象可以是任意类型的对象,并且长度可变。其中,这些集合类都位于java.util包中,使用时一定注意导包,否则会出现异常。
集合按照储存结构分为2大类:单列集合Collection+双列集合Map。这两种集合的特点具体如下:
(1)Collection:单列集合的根接口,用于存储一系列符合某种规则的元素。Collection集合有2个重要的子接口:List+Set。其中List集合的特点是元素有序,可重复;Set集合的特点是元素无序,不可重复。List接口的主要实现类有ArrayList和LinkedList和Vector;Set接口的主要实现类有HsahSet和TreeSet。
(2)Map:双列集合的根接口,用于存储具有键(Key)、值(Value)映射关系的元素。Map集合中每个元素都包含一对键值,并且Key是唯一的,在使用Map集合中可以通过指定的Key找到对应的Value。就比如根据学生的学号可以找到学生的姓名。Map接口的主要实现类有HashMap和TreeMap.
接下来就通过一张图来买描述整个集合的核心继承体系:
二(Collection接口):
Collection是所有单列集合的根接口,因此在Collection中定义了单列结合(List和Set)的一些通用方法,可用于操作所有的单列集合。
boolean add(Object o ) | 添加一个元素到集合中 |
boolean addAll(Collection c) | 将指定集合c中的所有元素添加到该集合中 |
void clear( ) | 删除该集合中的所有元素 |
boolean remove(Object o) | 删除该集合中的指定元素 |
boolean removeAll(Collection c) | 删除该集合中包含指定集合c的所有元素 |
boolean isEmpty() | 判断该集合是否为空 |
boolean contains(Object o) | 判断该集合中是否包含某个元素 |
boolean containAll(Collection c) | 判断该集合中是否包含指定集合c中的所有元素 |
int size() | 获取该集合的元素个数 |
注意:在使用contains(Object o)方法时,会调用o对象所在类的equals方法,若不重写该类的equals方法,则调用Object类的equals方法。
没有重写Student类的equals方法:
package 集合;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
public class CollectionDemo1 {
public static void main(String[] args) {
Collection c=new ArrayList();
c.add(new Student("杰杰",18));
boolean b=c.contains(new Student("杰杰",18));
System.out.println("c集合中是否包含杰杰:"+b);
}
}
class Student{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// @Override
// public boolean equals(Object o) {
// if (this == o) return true;
// if (o == null || getClass() != o.getClass()) return false;
// Student student = (Student) o;
// return age == student.age && Objects.equals(name, student.name);
// }
}
因为new Student("杰杰",18) 2次,也就是两个对象,他们在栈里面的内存地址是不同的,所以不相等。
重写Student类的equals方法后:
数组和集合的相互转换:
1.集合——>数组: 通过Collection中toArray()将指定集合转为数组。
2.数组——>集合: 通过数组工具类Arrays中的asList方法
集合——>数组:
Collection c=new ArrayList();
c.add(111);
c.add(222);
c.add(333);
System.out.println("集合"+c);
//集合转数组
Object[] o=c.toArray();
System.out.println("集合转数组"+Arrays.toString(o));
数组——>集合:
int[] a={1,2,3,4,5,6};
List list=Arrays.asList(Arrays.toString(a));
System.out.println("数组转集合:"+list);
三(List接口):
List接口继承自Collection接口,是单列集合的一个重要分支,习惯性的将实现了List接口的对象称为List集合。在List集合中允许出现重复的元素,所有的元素是以一种现线性方式进行存储的,在程序中可以通过索引(类似于数组中的元素角标)来访问集合中的指定元素。另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
List作为Collection集合的子接口,不但继承了Collection接口的全部方法,而且还增加了一些操作集合的特有方法,如下表所示:
void add(int index,Object element) | 将元素element插入在List集合的指定索引位置 |
boolean addAll(int index,Collection c) | 将集合c包含的所有元素插入到List集合的指定索引位置 |
Object get(int index) | 返回集合索引index出的元素 |
Object remove(int index) | 删除index索引处的元素 |
Object set(int index,Object element) | 将索引index处元素替换成element元素,并将替换后的元素值返回 |
int indexOf(Object o) | 返回对象o在List集合中首次出现的位置索引 |
int lastIndexOf(Object o) | 返回对象o在List集合中最后一次出现的位置索引 |
List subList(int feonIndex,int toIndex) | 返回从索引fromIndex(包括)到toIndex(不包括)处所有元素集合组成的子集合 |
Object[ ] toArray( ) | 将集合转换为数组 |
List接口有3个实现类:ArrayList+LinkedList+Vector
ArrayList:作为List接口主要实现类,线程不安全,效率高;底层使用Object类型的数组
LinkedList:对于频繁的插入,删除操作,使用该类效率比ArrayList高,底层使用的时双向链表
Vector:是一个古老的集合类,JDK1.0就有了,大多数操作与ArrayList相同,区别在于Vector线程安全的,效率低,
四(ArrayList集合):
ArrayList是List接口的一个实现类,它是程序中最常见的一种集合。在ArraysList内部封装了一个长度可变的数组对象,当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来存储这些元素,因此可以将ArraysList集合看作一个长度可变的数组。正是由于ArraysList内部的数据存储结构是数组形式,在增加或删除指定位置的元素时,会创建新的数组,效率较低,不适合做大量的增删操作。适合遍历和查找元素,非常高效。
ArrayList底层分析:
JDK7版本的情况下底层实现:
ArrayList al=new ArrayList();
//底层创建长度为10的Object类型的数组名为elementData
//...当添加到11个元素时,
//如果添加的元素的时候,导致底层elementData容量不够了,则自动扩容
//默认情况下,扩容为原来的1.5倍,同时把原数组中的内容赋值到新数组中去
JDK8的情况下底层实现:
ArrayList arrayList=new ArrayList();//底层创建Object类型的数组:Object[ ] elementDate={ }
当调用一次add,底层数组扩容一个长度,然后将要添加的元素放在数组的末尾
底层数组扩容一个长度,然后将要添加的元素放在数组的末尾
五(LinkedList集合):
ArrayList集合在查询元素时速度很快,但在增删元素时效率较低,为了克服这种局限性,可以使用List接口的另外一个实现类LinkedList。该集合内部包含2个Node类型的first和last属性维护一个双向循环链表,链表中的每一个元素都是使用引用的方式来记住它的前一个元素和后一个元素,从而可以将所有的元素彼此连接起来。当插入一个新元素时,只需要修改元素之间的引用关系即可,删除一个节点也是如此。正是因为这样的存储结构,所以LinkedList集合对于元素的增删操作表现出很高的效率。
LinkedList底层分析:
双向链表,内部没有声明数组,而是定义Node类型first和last,用来记录首末元素 。
在上图中,元素1是first,元素3是last。LinkedList集合添加元素和删除元素就是改变元素之间的指向。若想要删除元素2,只需要让元素1和元素3成为前后关系即可。若想要新增元素4到元素2和元素3之间,只需要让元素2的后面指向元素4,让元素3的前面指向元素4即可。
void add(int index,E element) | 在此列表中指定的位置插入指定的元素 |
void addFirst() | 在集合开头插入元素 |
void addLast() | 在集合末尾插入元素 |
Object getFirst() | 获取集合的第一个元素 |
Object getLast() | 获取集合的最后一个元素 |
Object removeFirst() | 移除并返回集合的第一个元素 |
Object removeLast() | 移除并返回集合的最后一个元素 |
Vector底层分析:
Vector v=new Vector();底层创建长度为10的Object类型的数组名为elementData,当添加到11个元素时,底层elementData容量不够了,则自动扩容。默认情况下,扩容为原来的2倍,同时把原数组中的内容赋值到新数组中去。
总结:ArrayList/LinkedList/Vector的异同点:
1.线程安全问题
ArrayList和LinkedList都是线程不安全的,Vector线程安全的;线程不安全效率高,线程安全效率低;
2.底层实现
ArrayList和Vector是实现类基于动态数组的数据结构;LinkedList底层是双向链表的数据结构
3.功能性
对于随机访问get和set;ArrayList优于LinkedList,因为LinkedList要移动指针
对于新增add(特定的插入)和删除,LinkedList优于ArrayList,因为ArrayList要移动数组中的元素
六(foreach遍历集合) :
从JDK5开始,提供了foreach循环,它是一种更加简洁的for循环,也称“增强for循环” 。foreach循环用于遍历集合或数组中的元素,遍历集合的底层调用迭代器来实现的。语法格式如下:
for(容器中元素类型 临时变量 :容器变量){
//执行语句
}
package 集合;
import java.util.ArrayList;
public class foreachDemo {
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add("data-1");
list.add("data-2");
list.add("data-3");
for (Object o:list) {
System.out.println(o);
}
}
}
注意:当使用foreach循环集合或数组时,只能访问元素,不能修改元素。
package 集合;
import java.util.Arrays;
public class foreachDemo1 {
static String[] str={"aaa","bbb","ccc"};
public static void main(String[] args) {
System.out.println("原数组:"+ Arrays.toString(str));
//1.使用foreach遍历该数组
for (String s:str) {
s="ddd";
}
System.out.println("foreach循环修改后的数组:"+str[0]+","+str[1]+","+str[2]);
//使用for循环遍历数组
for(int i=0;i<str.length;i++){
str[i]="ddd";
}
System.out.println("普通for循环修改后的数组:"+str[0]+","+str[1]+","+str[2]);
}
}
从运行结果可看出,foreach循环并不能修改数组的元素,原因是foreach循环中“ddd”只是将临时变量s指向了一个新的字符串,这和数组中的元素没有一点关系。
七(Collection集合的工具类:Collections)
Collections是一个操作set和List集合的工具类,提供了一系列的静态方法对集合元素进行排序/查询/修改等。
排序:
1.sort(List):根据元素的自然顺序对指定的List集合按照升序排序
2.sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序操作
3.shuffle(List):对List集合元素进行随机排序
4.reverse(List):反转List集合元素的顺序
package 集合;
import java.util.ArrayList;
import java.util.Collections;
public class CollectionsDemo1 {
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add(11);
list.add(22);
list.add(333);
list.add(44);
System.out.println("原始list储存顺序:"+list);
Collections.sort(list);
System.out.println("按照元素的自然顺序对list按照升序排序:"+list);
Collections.shuffle(list);
System.out.println("对list集合元素进行随机排序:"+list);
Collections.reverse(list);
System.out.println("对list集合元素进行反转:"+list);
}
}
八(Iterator遍历集合)
Iterator接口是Java集合框架的一员,主要用于迭代访问(即遍历)Collection集合中的元素,因此Iterator对象也被称为“迭代器”。
Iterator iterator() | 获得迭代器的对象 |
Boolean hasNext() | 判断集合中是否还有下一个元素 |
Object next() | 将指针下移,并返回指针下移后指向的元素 |
void remove() | 可以在遍历集合时删除元素 |
注意:
1.在调用next()方法获取元素时,必须保证获取的元素存在,否则会抛出NoSuchElementException异常。
2.如果还没有调用next()或者调用next()之后,又调用两次remove会报错(因为第一次调用remove()时就删除了某些元素,若再次调用remove(),之前被删除元素的位置上为空,没有元素可以再被删除)。
3.遍历集合结束后,指针已经指在最后一个元素上,如果需要再次遍历集合,则需要重新获取迭代器对象。
通过Iterator迭代器对象来遍历集合:
package 集合;
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorDemo2 {
public static void main(String[] args) {
ArrayList list=new ArrayList();
list.add(12);
list.add("张飞");
list.add("true");
list.add("6.6");
//1.获取迭代器对象
Iterator iterator=list.iterator();
//2.判断集合中是否有下一个元素
while (iterator.hasNext()){
//3.将指针下移并将指针指向的元素返回
Object o=iterator.next();
System.out.println("List集合中的元素为:"+o);
}
}
}
ListIterator迭代器用来遍历List集合,用法和遍历Collection集合的Iterator迭代器一致。
package 集合;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorDemo {
public static void main(String[] args) {
List list=new ArrayList();
list.add(111);
list.add("哈哈");
list.add("false");
ListIterator lt=list.listIterator();
while(lt.hasNext()){
Object o=lt.next();
System.out.println("list集合中有:"+o);
}
}
}