Day14
集合Collection
集合是一种可以存放多个数据的容器
- List:可重复且有序集(元素是否重复取决于元素的equals()比较的结果)
- Set:不可重复集
Stream流
采用IO流式想法对集合进行操作,将集合看作一个流进行操作
常用方法
向集合中添加元素
使用add方法向集合中添加元素,不需要下标位置来指定存入位置,只需要调用add方法,从首位0开始插入
不需要管理容量,需要多少会自动完成扩容
注:可以存任意数据类型,但基本数据类型无法放入,存放基本数据类型时会自动装箱
方法格式
boolean add();//添加成功返回true,添加失败返回false
示例:
Collection list = new ArrayList();
list.add("one");
list.add("two");
list.add("three");
list.add(1);//自动装箱
System.out.println(list);//打印输出时也不需要用Arrays类转换,直接打印即可
获取集合元素个数
通过size方法返回集合的元素个数
失真现象:If this collection contains more than Integer.MAX_VALUE elements, returns Integer.MAX_VALUE.
size不等价于数组中的length,数组一创建就会固定长度并赋予初始值,所以length一开始就为开始创建的长度
方法格式
int size(Collection collection);
判空
通过isEmpty方法判断集合是否为空,null都没有
方法格式
boolean isEmpty();//无参方法,直接点在集合之后进行判断
清空集合
使用clear方法快速清空集合中所有元素
方法格式
void clear();//无返回值无参方法,直接点在集合后使用
包含
使用contains方法判断集合中是否包含指定元素,包含则返回true
底层判断依据为equals的结果,所以元素重写了equals方法则比较的是元素内部的值,没有重写则比较的是元素对象的地址
方法格式
boolean contains(Object object);
示例:Point没有重写equals方法,所以内部元素相同无法正确判断
HashSet<Object> set = new HashSet<>();
set.add(new Point(1, 2));
set.add(new Point(3, 4));
set.add(new Point(5, 6));
boolean contains = set.contains(new Point(1, 2));
System.out.println("是否包含Point(1, 2): " + contains);//是否包含Point(1, 2): false
并集
使用addAll方法添加集合中所有元素到另一个集合中
注:向set中添加元素时,重复元素无法放入
方法格式
boolean addAll(Collection c);
示例:
Collection languageList = new ArrayList();
languageList.add("java");
languageList.add("c");
languageList.add("c++");
System.out.println("languageList = " + languageList);
ArrayList languageList2 = new ArrayList();
languageList2.add("html");
languageList2.add("css");
languageList.addAll(languageList2);
System.out.println("languageList = " + languageList);
输出
languageList = [java, c, c++]
languageList = [java, c, c++, html, css]
交集
使用retainAll方法将目标集合的内容变为两个集合共有元素的集合
方法格式
boolean retainAll(Collection<?> c);
示例:
Collection languageList = new ArrayList();
languageList.add("java");
languageList.add("c");
languageList.add("c++");
System.out.println("languageList = " + languageList);
Collection languageList3 = new ArrayList();
languageList3.add("java");
languageList3.add("php");
System.out.println("languageList3 = " + languageList3);
languageList.retainAll(languageList3);
System.out.println("languageList = " + languageList);
输出
languageList = [java, c, c++]
languageList3 = [java, php]
languageList = [java]
差集
使用removeAll方法将目标集合变为去除两个集合共有元素的集合
注:区别removeAll与remove的不同
方法格式
boolean removeAll(Collection<?> c);
示例
Collection languageList = new ArrayList();
languageList.add("java");
languageList.add("c");
languageList.add("c++");
System.out.println("languageList = " + languageList);
Collection languageList3 = new ArrayList();
languageList3.add("java");
languageList3.add("php");
System.out.println("languageList3 = " + languageList3);
languageList.removeAll(languageList3);
System.out.println("languageList = " + languageList);
输出
languageList = [java, c, c++]
languageList3 = [java, php]
languageList = [c, c++]
删除指定元素
使用remove方法删除目标集合中的指定元素
注:remove方法用于删除指定元素,若传参时将整个集合传入,则会在目标集合中将参数集合当作一个对象来匹配元素,若目标集合中没有元素为参数集合对象,则目标集合不做修改
方法格式
boolean remove(Object o);
示例:
languageList.remove(languageList3);//将集合作为参数传入,会在languageList中匹配是否有元素是languageList3对象
System.out.println("languageList = " + languageList);//languageList对象的元素中没有languageList3对象,所以不做操作
languageList.remove("java");
System.out.println("languageList = " + languageList);
输出
languageList = [java, c, c++]
languageList = [c, c++]
完全包含
使用containAll方法判断当前集合是否包含给定集合中的所有元素,只要有一个元素不包含,就返回false
方法格式
boolean containsAll(Collection<?> c);
示例:
Collection languageList = new ArrayList();
languageList.add("java");
languageList.add("c");
languageList.add("c++");
System.out.println("languageList = " + languageList);
Collection languageList4 = new ArrayList();
languageList4.add("java");
languageList4.add("c");
System.out.println("languageList4 = " + languageList4);
boolean containsAll = languageList.containsAll(languageList4);
System.out.println("containsAll = " + containsAll);
输出
languageList = [java, c, c++]
languageList4 = [java, c]
containsAll = true
集合元素对象指向问题
[!IMPORTANT]
集合中存放对象时,存放的也是对象的地址,所以对对象进行操作时,集合中的元素会随之改变
如下图,集合c中的对象和p对象本身指向的是同一个Point
示例
Collection<Point> c = new ArrayList<>();
Point p = new Point(1, 2);
c.add(p);
p.setX(2);
System.out.println(p);//Point{x=2, y=2}
System.out.println(c);//[Point{x=2, y=2}]
集合重写toString()方法的格式
集合的toString方法会直接调用各个元素自身的toString方法,若元素自身没有重写该方法,则还是会直接调用Object类中的默认方法,输出地址
[元素1.toString() , 元素2.toString() , ...]
对象存入问题
在Set集合中元素是不允许重复的,但是判断的依据仍然是equals方法的判断结果,所以若存入的对象没有重写equals方法,则默认调用Object类中的方法,比较的是对象的地址,所以即使对象中的内容完全相同,也可以在Set集合中并存
示例:Point没有重写equals方法,可以将重复元素存入set
HashSet<Object> set = new HashSet<>();
set.add(new Point(1, 2));
set.add(new Point(3, 4));
set.add(new Point(5, 6));
set.add(new Point(1, 2));//重复存入,Point没有重写equals方法
System.out.println(set);//[Point{x=1, y=2}, Point{x=3, y=4}, Point{x=5, y=6}, Point{x=1, y=2}]
遍历集合元素
迭代器模式
迭代器规定,迭代器迭代过程中,不可以使用集合的方法进行元素的操作,否则报并发修改异常ConcurrentModificationException
-
集合提供了统一的遍历元素方式:迭代器模式
-
集合提供的方法:Iterator iterator()
java.util.Iterator接口
该方法会返回一个用于遍历当前集合的迭代器
-
迭代器接口规定了几个方法,用于遍历集合元素.不同的集合都实现了一个迭代器用于遍历自身元素,我们不需要记住那些实现类的名字。
-
迭代器遍历集合的遵循的步骤:问->取->删 (删除不是必须操作)
方法格式
boolean hasNext();//校验集合中是否还有下一个元素 -> 问
E next();//从集合中取出下一个元素 -> 取
示例:
// Declare an ArrayList to store strings
ArrayList<String> list = new ArrayList<>();
// Add strings to the ArrayList
list.add("one");
list.add("two");
list.add("three");
list.add("four");
// Create an Iterator object for the ArrayList
Iterator<String> iterator = list.iterator();
// Loop through the ArrayList using the Iterator
while (iterator.hasNext()) {
// Get the next element from the Iterator
String element = iterator.next();
// Print the element
System.out.println(element);
}
增强for循环
注:集合中使用增强for循环,底层会使用Iterator迭代器实现,所以在增强for循环中也无法使用集合本身的方法
而在数组中使用增强for循环,则会等效为普通for循环
语法结构
for(<?> s : Collection c){
...
}
示例:
Collection<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
for (String s : list) {
System.out.println(s);
}
List集合
LinkedList和ArrayList
内存中的结构
- ArrayList底层用顺序表实现,LinkedList底层用链表实现
- ArrayList查询性能非常高O(1),但插入删除效率差
- LinkedList在一定阈值内插入删除效率高,查询性能较差
常用方法
获取指定下标位置上的元素
使用get方法获取List中的指定位置元素
示例
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list.get(2));//three
将给定元素存入指定位置
使用set方法将给定的元素存入指定位置,并将原位置的元素返回
方法格式
E set(int index, E element);
示例
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list.set(2, "six"));//three
案例——将List中的元素反转
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
String temp = "";
for (int i = 0; i < list.size() / 2; i++) {
temp = list.get(i);
list.set(i, list.get(list.size() - 1 - i));
list.set(list.size() - 1 - i, temp);
}
System.out.println(list);//[five, four, three, two, one]
插入、移除
List中重写了add和remove方法,用于在指定位置插入给定元素及移除指定位置的元素
方法格式
void add(int index, E element);
E remove(int index);
示例:
- 使用add方法时,从index指向的位置开始,其后所有元素均向后移,然后再将element插入index位置
- 使用remove方法时,删除index位置的元素后,从index指向的位置开始,其后所有元素均向前移,并返回删除的元素
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add(2,"2.5");
System.out.println(list);//[one, two, 2.5, three, four, five]
list.remove(1);
System.out.println(list);//[one, 2.5, three, four, five]
获取List中的子集
List中使用subList方法获取子集
subList方法获取的子集与原List占有相同的存储空间,对子集的操作就是对原集合的操作
方法格式
List<E> subList(int fromIndex, int toIndex);//含左不含右
示例
List<Integer> list = new ArrayList<>();
for (Integer i = 0; i < 10; i++) {
list.add(i);
}
System.out.println(list);
List<Integer> subbedList = list.subList(3, 7);
System.out.println("list.subList(3,7) = " + subbedList);
//将子集subbedList中所有元素扩大十倍
for (int i = 0; i < subbedList.size(); i++) {
Integer element = subbedList.get(i);
element *= 10;
subbedList.set(i, element);
}
System.out.println("扩大后的list = " + list);//修改子集,原集合随之改变
List转数组
方法结构
规则:若传入数组的长度大于等于List的长度,则直接使用该数组,
长度不足则新建一个数组返回
- 参数:自定义的一个数组(含长度)
- 返回值:与传入数组同类型的数组
<T> T[] toArray(T[] a)
示例
ArrayList<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
String[] array = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(array));//[one, two, three, four, five]
数组转List
调用Arrays类中的asList方法对给定数组进行转换
[!IMPORTANT]
- 数组转出的List与原数组的绑定的,修改List就会对原有数组进行修改,反之亦然
- 因为数组长度固定,所以不允许对转换出的List进行add操作和remove操作,否则报不支持操作异常UnsupportedOperationException
- 若想对集合进行增删操作,需要对原数组进行“扩容”(copyOf)
方法格式
static <T> List<T> asList(T... a)
示例
String[] arrays = {"one","two", "three","four","five"};
List<String> list = Arrays.asList(arrays);
System.out.println("List: "+ list);//List: [one, two, three, four, five]
//对转换出的List进行修改
list.set(2,"3");
System.out.println("Modified List: "+ list);
System.out.println("Array: "+ Arrays.toString(arrays));
//对Array进行修改
arrays[0] = "1";
System.out.println("Modified Array: "+ Arrays.toString(arrays));
System.out.println("List: "+ list);
结果
Modified List: [one, two, 3, four, five]
Array: [one, two, 3, four, five]
Modified Array: [1, two, 3, four, five]
List: [1, two, 3, four, five]
构造器
所有集合都支持一个参数为Collection的构造器,用于在创建集合时将给定集合的所有元素导入
ArrayList<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);//[one, two, three, four, five]
ArrayList<String> list1 = new ArrayList<>(list);
System.out.println(list1);//[one, two, three, four, five]
List排序
集合工具类java.util.Collections提供了一个静态方法sort可以对List集合进行排序
Collections.sort(List list)方法在排序集合时,要求集合元素必须实现一个接口:Comparable
因为该接口中定义了一个用于比较两个对象之间大小关系的方法:compareTo()
sort方法就是利用这个方法确定元素之间大小关系从而进行排序的
方法构造
static <T extends Comparable<? super T>> void sort(List<T> list)
示例
ArrayList<Integer> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
list.add(random.nextInt(100));
}
System.out.println("排序前List:"+list);
Collections.sort(list);
System.out.println("排序后List:"+list);
结果
排序前List:[33, 2, 9, 19, 37, 47, 26, 0, 94, 34]
排序后List:[0, 2, 9, 19, 26, 33, 34, 37, 47, 94]
自定义比较规则
实际开发中对于集合中元素是自己定义的类时,不建议使用原生sort方法,因为要比较需要自定义类时需要手动让目标类实现Comparable接口,而这样操作会导致程序中出现侵入性
自定义比较规则,若通过实现Comparator接口重写compare方法,自定义比较规则
针对元素为对象的集合,排序时可以使用sort方法的重载方法
方法构造
参数:
- list:排序目标List
- Comparator c:比较规则,会调用该规则进行比较
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
示例:
ArrayList<Point> list = new ArrayList<>();
list.add(new Point(1, 2));
list.add(new Point(30, 4));
list.add(new Point(15, 6));
list.add(new Point(5, 7));
list.add(new Point(25, 3));
System.out.println(list);
/**
* 使用Collections.sort(list,Comparator)方法对list进行排序
* Comparator只需要使用一次,所以使用匿名内部类来实现
* Comparator接口的compare方法需要返回一个int值,表示排序的顺序
* 返回正数表示o1在o2之前,返回负数表示o1在o2之后,返回0表示相等
* 这里按照距离原点的距离进行排序
* 这里使用匿名内部类来实现Comparator接口,也可以使用Lambda表达式
* 注意:Lambda表达式只能使用一次,所以这里使用匿名内部类
* 匿名内部类可以省略参数类型,如果只有一个参数,还可以省略()
* 匿名内部类可以省略{},如果{}中的代码只有一句,还可以省略{}和return
*/
Comparator<Point> MyComparator = new Comparator<Point>(){
@Override
public int compare(Point o1, Point o2) {
int len1 = o1.getX()*o1.getX() + o1.getY()* o1.getY();
int len2 = o2.getX()*o2.getX() + o2.getY()* o2.getY();
if (len1 == len2){
return 0;
}
return len1 > len2 ? 1 : -1;
}
};
Collections.sort(list, MyComparator);
System.out.println(list);