1、集合和数组的区别
- 数组作为一种容器,有一些不方便之处,在进行增、删、改操作的时候很不方便,并且数组只能存储同一数据类型,可以是基本数据类型,也可以是引用数据类型。Java提出集合的概念,集合也是一种容器,它只能存储引用,集合不是某一个类,而是一个集合框架,有很多种集合构成。 以下是Collection集合的继承体系:
2、Collection集合
- Collection 是层次结构中的根接口,Collection 表示一组对象,这些对象也称为 collection 的元素。该集合在java.util包下。
Collection的功能概述(通过API查看即可得到)——
添加功能:
1、boolean add(Object obj):添加一个元素
2、boolean addAll(Collection c):添加一个集合的元素 (给一个集合添加进另一个集合中的所有元素)
删除功能:
3、void clear():移除所有元素
4、boolean remove(Object o):移除一个元素
5、boolean removeAll(Collection c):移除一个集合的元素(移除一个以上返回的就是true) 删除的元素是两个集合的交集元素 ,如果没有交集元素 则删除失败 返回false
判断功能:
6、boolean contains(Object o):判断集合中是否包含指定的元素
7、boolean containsAll(Collection c):判断集合中是否包含指定的集合元素(这个集合包含另一个集合中所有的元素才算包含 才返回true)【比如:1,2,3 containsAll 12=true 1,2,3 containsAll 2,3,4=false】
8、boolean isEmpty():判断集合是否为空
获取功能:
9、Iterator<E> iterator()(重点),迭代器可以认为是指针
长度功能:
10、int size():元素的个数
交集功能:
【例如:A集合对B集合取交集,获取到的交集元素在A集合中。返回的布尔值表示的是A集合是否发生变化】
11、boolean retainAll(Collection c):获取两个集合的交集元素(交集:两个集合都有的元素)
把集合转换为数组:
12、Object[] toArray()
package org.westos.demo3;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
public class Collection集合
{
public static void main(String[] args)
{
Collection c=new ArrayList();//需要一个接口类型的变量,使用实现接口的子类,多态的形式
boolean b = c.add(12);
boolean b1 = c.add(true);
boolean b2 = c.add('c');
boolean b3 = c.add(new Student());
boolean b4 = c.add(new int[2]);//所有的返回值都代表是否添加元素成功
System.out.println(c);//打印集合,发现输出的是集合里面的元素,所以,该集合一定是重写了toString()
boolean b5 = c.remove(12);//根据元素移除集合里面的元素,返回结果为是否移除成功
Collection c1=new ArrayList();
c1.add(12);
c1.add(true);
System.out.println(c.removeAll(c1));//true
System.out.println(c);
//移除一个集合的元素(移除一个以上返回的就是true),删除的元素是两个集合的交集元素
//如果没有交集元素 则删除失败 返回false
c.add(1);
c.add(2);
c.add(3);
c.add(4);
c.add(5);
c1.add(1);
c1.add(2);
c1.add(3);
c1.add(4);
c1.add(5);
System.out.println(c.contains(1));//判断是否包含该元素,true
System.out.println(c.containsAll(c1));//判断集合c包含c1,false
c.clear();//移除所有元素
System.out.println(c.isEmpty());//判断集合是否为空,true
System.out.println(c1.size());//获取集合的元素个数,7
c.add(1);
System.out.println(c.retainAll(c1));//获取两个集合的交集元素(交集:两个集合都有的元素),false
Object[] obj = c1.toArray();//集合转为数组
System.out.println(Arrays.toString(obj));//[12, true, 1, 2, 3, 4, 5]
}
}
- 关于迭代器:
迭代器是一种设计模式,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构(迭代器把访问逻辑从不同类型的集合类里面抽取出来,从而避免向外部暴露集合的内部结构)。迭代器通常被认为是“轻量级”对象,因为创建它的代价小。 - 为什么要使用迭代器?
若不使用迭代器,访问代码和集合本身是紧密耦合的,因此无法将访问逻辑从集合类和客户端代码中分离出来。而由于不同的集合会对应不同的遍历方法,所以客户端代码无法复用。而对Iterator来说,它总是用同一种逻辑来遍历集合,使得客户端自身不需要维护集合的内部结构,所有的内部状态都由Iterator来维护。 - 实现原理:
下面基于ArrayList的Iterator的实现分析Iterator的原理(基于JDK1.8):在ArrayList类中有一个方法 iterator() ,此方法将返回一个iterator的实现,这里可以看出实现类交叫Itr,通过它的源码可知,这是ArrayList的一个内部类。为什么要将迭代器设置为内部类,因为内部类(无论是成员内部类还是局部内部类)可以访问外部类里面的所有成员,包括私有的成员。
package org.westos.demo3;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class MyTest1
{
public static void main(String[] args)
{
Collection c=new ArrayList();
c.add(1);
c.add(2);
c.add(3);
c.add(4);
c.add(5);
Iterator iterator = c.iterator();//使用该方法获取迭代器,Iterator接口对Collection集合进行遍历的迭代器
while(iterator.hasNext())
{//迭代器可以认为是指针
System.out.println(iterator.next());//判断是否存在下一个元素,如果存在就获取并输出
}
System.out.println("迭代器输出为:"+iterator);//java.util.ArrayList$Itr@1540e19d
//打印这个迭代器会发现里面有一个美元符号,说明这个迭代器类是内部类
}
}
3、List子接口
- 元素序列有序(存取顺序一致),并且每一个元素都有索引,集合中允许元素重复。
List集合的特有功能概述:
void add(int index,E element): 在指定索引处添加元素
E remove(int index):移除指定索引处的元素 返回的是移除的元素
E get(int index):获取指定索引处的元素
E set(int index,E element):更改指定索引处的元素 返回的而是被替换的元素
package org.westos.demo3;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MyTest2
{
public static void main(String[] args)
{
List list=new ArrayList();//多态
list.add("三国演义");
list.add("水浒传");
list.add("西游记");
list.add(0,"红楼梦");//List集合中特有的添加元素的方法,根据索引添加元素
list.remove("红楼梦");
list.remove(0);//List集合中特有的删除元素的方法,根据索引删除元素
list.add(0);
list.add(1);
list.add(2);
list.add(3);
list.remove(1);//这里的1只能代表索引,不是元素1,如果区分不出来元素还是索引,将元素包装
list.remove(Integer.valueOf(1));
System.out.println("集合中第一个元素为:"+list.get(0));//list中的特有的方法,根据索引获取元素,注意索引越界
list.set(0,"儒林外史");//根据索引替换元素
System.out.println(list);//[儒林外史, 0, 2, 3]
}
}
- 遍历List集合的几种方式:
package org.westos.demo3;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class MyTest3
{
public static void main(String[] args)
{
List list = new ArrayList();
list.add("碎琉璃");
list.add("穆斯林的葬礼");
list.add("鲁迅");
list.add("冰心");
list.add("巴金");
//方式1;
Iterator i=list.iterator();
while(i.hasNext())
{
System.out.println(i.next());
}
//方式2:
for (int j = 0; j < list.size(); j++)
{
System.out.println(list.get(j));
}
//方式3:List集合自己有一个迭代器
ListIterator listIterator = list.listIterator();
//这个迭代器的父接口是Iterator,它里面的方法比父接口中的方法丰富,可以反向迭代
while(listIterator.hasPrevious())
{
System.out.println(listIterator.previous());
}
//注意:反向迭代的时候,必须先进行正向迭代,因为迭代就相当于一个指针,而且注意正向反向迭代的必须是一个迭代器
}
}
4、并发修改异常
- 首先看一段代码:
package org.westos.demo3;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class MyTest4
{
public static void main(String[] args)
{
List list=new ArrayList();
list.add("C/C++");
list.add("Java");
list.add("PHP");
list.add("JavaScript");
list.add("Python");
list.add("Matlab");
ListIterator iterator=list.listIterator();
while(iterator.hasNext())
{
String str= (String) iterator.next();
if(str.equals("PHP"))
list.add("C#");
//ConcurrentModificationException,此处程序会报并发修改异常
}
}
}
- 为什么会出现这样的错误?
当我们使用迭代器遍历集合的时候,我们的迭代器已经事先知道集合元素的个数,突然在迭代途中增加或者删除元素,就会打乱原来集合的顺序,迭代器也就无所适从,就会抛出并发修改异常。解决方式有两种:1、使用迭代器自己的增加删除集合中元素的方法 2、使用for循环遍历,就可以使用集合自己的增加和删除方式,修改后的代码如下:
package org.westos.demo3;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class MyTest4
{
public static void main(String[] args)
{
List list=new ArrayList();
list.add("C/C++");
list.add("Java");
list.add("PHP");
list.add("JavaScript");
list.add("Python");
list.add("Matlab");
ListIterator iterator=list.listIterator();
while(iterator.hasNext())
{
String str= (String) iterator.next();
if(str.equals("PHP"))
iterator.add("C#");//解决方式 1
}
System.out.println(list);//[C/C++, Java, PHP, C#, JavaScript, Python, Matlab]
for (int i = 0; i < list.size(); i++)
{
String str= (String) list.get(i);
if(str.equals("PHP"))
list.add("C#");//解决方式 2
}
System.out.println(list);//[C/C++, Java, PHP, C#, JavaScript, Python, Matlab, C#]
}
}
5、不同集合的特点
- List:数组元素有序,允许元素重复,选择哪种集合,根据自己的需求而定
6、ArrayList集合
- 实现了Collection接口和List接口,在 java.util 包下,常用的方法如下:
1、boolean add(E e) :将指定的元素添加到此列表的尾部
2、void add(int index, E element):将指定的元素插入此列表中的指定位置
3、void clear() :移除此列表中的所有元素
4、Object clone() :返回此 ArrayList 实例的浅表副本
5、boolean contains(Object o):如果此列表中包含指定的元素,则返回 true
6、 E get(int index):返回此列表中指定位置上的元素
6、 int indexOf(Object o) :返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1
7、boolean isEmpty() :如果此列表中没有元素,则返回 true
8、int lastIndexOf(Object o):返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1
9、E remove(int index):移除此列表中指定位置上的元素
10、boolean remove(Object o) :移除此列表中首次出现的指定元素(如果存在)
11、protected void removeRange(int fromIndex, int toIndex):移除列表中索引在 fromIndex(包括)和 toIndex(不包括)之间的所有元素
12、E set(int index, E element):用指定的元素替代此列表中指定位置上的元素
13、int size() :返回此列表中的元素数
14、Object[] toArray():按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组
package org.westos.demo3;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
public class MyTest5
{
public static void main(String[] args)
{
ArrayList a = new ArrayList();
a.add(100);
a.add(200);
a.add(300);
a.add(100);
a.add(267);
a.add(383);
a.add(157);
int i = a.indexOf(100);
System.out.println("元素100在集合a中的索引为:"+i);//0
System.out.println("元素100在集合a中最后一次出现的索引为:"+a.lastIndexOf(100));//3
a.sort(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer a= (Integer) o1;
Integer b= (Integer) o2;
return a-b;
}
});//排序集合中的元素
System.out.println("集合中的元素为:"+a);//[100, 100, 157, 200, 267, 300, 383]
List list = a.subList(0, 2);
System.out.println("根据索引截取出来的集合为:"+list);//[100, 100]
Object o = a.clone();
System.out.println("浅克隆出来的元素为:"+o);//[100, 100, 157, 200, 267, 300, 383]
//JDK1.8给ArrayList集合提供了一个内部迭代
a.forEach(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
});
}
}
【案例】去除集合中的重复元素:要求是不能创建新的容器,就在原来的集合当中,去掉重复元素
package org.westos.demo4;
import java.util.ArrayList;
import java.util.ListIterator;
public class MyTest1
{
public static void main(String[] args)
{
ArrayList list=new ArrayList();
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(200);
list.add(300);
list.add(400);
list.add(100);
list.add(200);
list.add(400);
list.add(500);
list.add(100);
list.add(200);
list.add(300);
list.add(400);
//方式1-----原理:类似于选择排序
deleteEle(list);
//方式2------递归
ArrayList list1=removeEle(list);
System.out.println(list1);//[500, 100, 200, 300, 400]
}
private static ArrayList removeEle(ArrayList list)
{
for (int i = 0; i < list.size(); i++)
{
if(list.indexOf(list.get(i))!=list.lastIndexOf(list.get(i)))
{
list.remove(list.get(i));
removeEle(list);
}
}
return list;
}
private static void deleteEle(ArrayList list)
{
for (int i = 0; i < list.size()-1; i++)
{
for (int j = i+1; j < list.size(); j++)
{
if (list.get(i).equals(list.get(j)))
{
list.remove(j);
j--;
}
}
}
System.out.println(list);//[100, 200, 300, 400, 500]
}
}
7、Vector集合
- Vector集合底层数据结构仍然是数组,因此具有查询快,增删慢的特点,线程安全,效率低,因此一般不常用。以下是它的一些常用的方法:
1、boolean add(E e):将指定元素添加到此向量的末尾
2、void add(int index, E element) :在此向量的指定位置插入指定的元素
3、void addElement(E obj):将指定的组件添加到此向量的末尾,将其大小增加 1
4、E elementAt(int index) :返回指定索引处的组件
5、E firstElement():返回此向量的第一个组件(位于索引 0) 处的项)
6、void removeElementAt(int index):删除指定索引处的组件
7、List<E> subList(int fromIndex, int toIndex):返回此 List 的部分视图,元素范围为从 fromIndex(包括)到 toIndex(不包括)
8、Object[] toArray():返回一个数组,包含此向量中以恰当顺序存放的所有元素
package org.westos.demo4;
import java.util.Enumeration;
import java.util.Vector;
public class Vector类
{
public static void main(String[] args)
{
Vector vector = new Vector();
vector.add("aaa");
vector.addElement("bbb");
vector.add("ccc");
vector.add("ddd");
vector.add("eee");
vector.add("fff");
System.out.println(vector.get(0));//aaa
System.out.println(vector.elementAt(0));//一个作用--aaa
System.out.println(vector.firstElement());//aaa
System.out.println(vector.lastElement());//fff
//vector.removeAllElements();//清空所有元素
//自带的迭代器
Enumeration elements = vector.elements();
while(elements.hasMoreElements())
{
System.out.println(elements.nextElement());
}
}
}
8、linkedList集合
- 底层是链表,查询慢,增删快,线程不安全,效率高
LinkedList类特有功能:
public void addFirst(E e):添加元素至集合最前面
addLast(E e):添加元素至集合最后面
public E getFirst():获取第一个元素
getLast();获取第二个元素
public E removeFirst():移除第一个元素
public E removeLast():移除第二个元素
【案例】 请用LinkedList模拟栈数据结构的集合,并测试
//测试类:
package org.westos.demo4;
public class MyTest2
{
public static void main(String[] args)
{
//用LinkedList集合模拟栈数据结构,先进后出
MyList myList=new MyList();
myList.addEle("abc1");
myList.addEle("abc2");
myList.addEle("abc3");
myList.addEle("abc4");
myList.addEle("abc5");
Object obj=myList.getEle();
System.out.println(obj);
Object obj1 = myList.getEle();
System.out.println(obj1);
System.out.println(myList.toString());//[abc3, abc2, abc1, abc5, abc4]
}
}
//工具类:
package org.westos.demo4;
import java.util.LinkedList;
public class MyList
{
LinkedList list=null;
public MyList(){
list=new LinkedList();
}
public void addEle(Object obj)
{
list.addFirst(obj);
//list.push(obj);---往链头添加元素
}
public Object getEle()
{
Object o = list.pop();
//list.addFirst(o);---自己的错误做法
list.addLast(o);//正确的做法
return o;
}
@Override
public String toString() {
return list.toString();
}
}
【案例】ArrayList去除集合中字符串的重复值(字符串的内容相同)
//已有类去重:
package org.westos.demo4;
import java.util.ArrayList;
public class MyTest3
{
public static void main(String[] args)
{
ArrayList list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("aaa");
list.add("ccc");
list.add("bbb");
list.add("ddd");
list.add("bbb");
list.add("aaa");
list.add("ccc");
list.add("bbb");
list.add("ddd");
//去除集合中的重复元素
//采用创建新集合的方式来做
ArrayList newList = new ArrayList();
for (int i = 0; i < list.size(); i++)
{
Object o = list.get(i);//去重装到新集合
if(!newList.contains(o))
{
newList.add(o);
}
}
System.out.println(newList);//[aaa, bbb, ccc, ddd]
}
}
//自定义类去重————
//测试类:
package org.westos.demo4;
import java.util.ArrayList;
public class MyTest4
{
public static void main(String[] args)
{
ArrayList list=new ArrayList();
list.add(new Student("张三",19));
list.add(new Student("李四",20));
list.add(new Student("王五",23));
list.add(new Student("赵六",18));
list.add(new Student("张三",19));
list.add(new Student("王五",23));
ArrayList newList = new ArrayList();
for (int i = 0; i < list.size(); i++)
{
Student stu = (Student) list.get(i);
if(!newList.contains(stu)){
//要想判断是否存在这个Student对象,如果直接这样写的话一定是错的
//每次存储的时候都是new新对象,contains()方法在判断的时候调用equals(),比较的是地址值
//但是我们重写Student类的equlas()和hashCode(),contains()方法调用重写过后的equals()方法,比较的是字段值
newList.add(stu);
}
}
System.out.println(newList);
}
}
//自定义的Student类:
package org.westos.demo4;
import java.util.Objects;
public class Student
{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(){}
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}