java 集合 Collection(List、Set)

集合

集合框架

1、Collection

java.util.Collection 接口

集合是用来保存一组元素的,不同的实现类,实现了不同的数据结构

Collection是所有集合的顶级接口,规定了所有集合都必须具备的功能

集合与数组一样,保存一组元素,但是操作元素的方法集合提供了

Collection下面有两个常见的子接口(分类)

2、List(可重复集合)

java.util.List:线性表,特点是可以存放重复元素,并且有序

3、Set(不可重复集合)

java.util.Set:不可以重复集合,大部分实现类是无序的

是否为重复元素是根据元素自身equals比较的结果判定的

集合持有对象的引用

集合中存储的都是引用类型元素
并且集合只保存每个元素对象的引用,而并非将元素对象本身存入集合

1、集合只能放引用类型元素,不能放基础类型

		Collection c = new ArrayList();
		c.add(1);
		System.out.println(c);

若放的是基础类型,在jdk5之后或触发其自动拆装箱特性,将基本类型转换为对应的引用类型

2、集合只保存每个元素对象的引用(元素地址,非元素对象)

public class CollectionDemo2 {
	public static void main(String[] args) {
		Point p = new Point(1,2);
		Collection c = new ArrayList();
		//把p对象添加到集合中(添加的是p对象的地址)
		c.add(p);
		System.out.println("p:"+p);//(1,2)
		System.out.println("c:"+c);//[(1,2)]
		//修改p对象的值,发现集合中的元素也发生改变
		p.setX(2);
		System.out.println("p:"+p);//(2,2)
		System.out.println("c:"+c);//[(2,2)]
	}
}

[外链图片转存失败(img-zVGXD4sM-1566307380947)(C:\Users\Tedu\AppData\Roaming\Typora\typora-user-images\1565081738076.png)]

面试考题

问输出结果(考点,内存的存储,方法区、栈与堆)

package collection;

import java.util.ArrayList;
import java.util.Collection;

public class Test {
	public static void main(String[] args) {
		int a = 1;
		String s = "hellow";
		Point p = new Point(1,2);
		Collection c = new ArrayList();
		c.add(p);
		test(a,s,p,c);
		System.out.println("a:"+a);//a:1
		System.out.println("s:"+s);//s:hellow
		System.out.println("p:"+p);//p:(2,2)
		System.out.println("c:"+c);//c:[(2,6)]
		//注意最后一个不是(5,6)
		//因为在add后,p修改了x为2了,这时c中p指向的也发生改变了
	}
	public static void test(int a,String s,Point p,Collection c){
		a++;
		s = s+"world";
		p.setX(2);
		p = new Point(5,6);
		c.clear();
		c.add(p);
		p.setX(a);
		c = new ArrayList();
		c.add(new Point(7,8));
	}
}

集合中的常用方法

1、添加元素(add)

boolean add(E e)
向集合中添加元素,若成功添加则返回值为true

		Collection c = new ArrayList();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		System.out.println(c);

2、添加元素(集合之间求并集 addAll)

boolean addAll(Collection c)
向集合中添加另外一个集合的所有元素(set集合只会添加不重复的)
若有元素添加成功则返回值为true
注意:相当于求两集合的并集,如果是Set(不可重复)列表,还可以去重

public class CollectionDemo3 {
	public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add("java");
		c1.add("c");
		c1.add("c++");
		System.out.println("c1:"+c1);
		
		Collection c2 = new ArrayList();
		c2.add("php");
		c2.add("java");
		c2.add("python");
		c2.add("go");
		System.out.println("c2:"+c2);
		
		c1.addAll(c2);
		System.out.println("c1:"+c1);
		//c1:[java, c, c++, php, java, python, go]
	}
}

3、查看集合长度(size)

int size() 返回当前集合的元素个数

相当于数组中的.length()方法

        int size = c.size();
        System.out.println("size:"+size);

4、判断集合是否为空集(isEmpty)

boolean isEmpty() 判断当前集合是否为空集

**注意:**这里的空集是,集合存在,集合不为null,集合里面没有存放东西,集合里面是空的

        boolean isEmpty = c.isEmpty();
        System.out.println(isEmpty);

5、清空集合(clear)

集合.clear(); 清空集合中的元素,不会删除集合

		c.clear();
		System.out.println("集合已经清空");
		System.out.println(c.isEmpty());

6、判断给定元素是否被包含在集合中(contains)

boolean contains(Object o) 该方法会用于判断给定的元素是否被包含在集合中

若包含则返回true,否则返回false

**注意:**集合在判断元素是否被包含在集合中是根据每个元素的equals()方法进行比较后的结果
所以通常我们自定义的类要作为集合元素的话,有必要重写equals()保证contains()方法的合理结果

equals方法在不重写的情况下,Object定义的是使用"=="比较的

public class ContainsDemo {
	public static void main(String[] args) {
		Collection c = new ArrayList();
        //point为我们自定义的点类(包含x,y坐标)
		c.add(new Point(1,2));
		c.add(new Point(3,4));
		c.add(new Point(5,6));
		c.add(new Point(7,8));
        //输出的集合元素是根据自定义类中的toString方法输出
		System.out.println(c);
		
		Point p = new Point(1,2);
		//该判断根据equals判断(point类中我们重写过equals方法)
		boolean contains = c.contains(p);
		System.out.println("元素是否被包含:"+contains);
	}
}

**注意:**集合输出的元素,根据元素所属类自定义的toString()方法来输出,默认输出引用地址
所以通常我们自定义的类要作为集合元素的话,也有必要重写toString()方法,来保证格式化输出

7、判断集合是否被真包含(containsAll)

boolean 当前集合.containsAll(给定集合);
判断当前集合是否包含给定集合中的所有元素

public class CollectionDemo3 {
	public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add("java");
		c1.add("c");
		c1.add("c++");
		System.out.println("c1:"+c1);
		
		Collection c3 = new ArrayList();
		c3.add("c++");
		c3.add("c");
		System.out.println("c3:"+c3);
		//判断c1是否包含c3中的所有元素
		boolean containsAll = c1.containsAll(c3);
		System.out.println("全包含:"+containsAll);
	}
}

8、删除集合元素(remove)

boolean remove(Object o) 删除集合中的给定元素

**注意:**该方法也是根据equals判断找出,然后删除,若有重复元素,调用一次只会删除第一个

public class RemoveDemo {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add(new Point(1,2));
		c.add(new Point(3,4));
		c.add(new Point(5,6));
		c.add(new Point(7,8));
		System.out.println(c);
		
		Point p = new Point(1,2);
		//remove也是依靠equals比较
		//有重复元素,调用一次只会删除第一个
		c.remove(p);
		System.out.println(c);
	}
}

9、删除交集元素(removeAll)

boolean 当前集合.removeAll(给定集合);

删除当前集合中与给定集合的共有元素,给定集合元素不发生变化

注意:只是删除调用方法的集合元素,删除的是两个集合共有的元素

public class CollectionDemo3 {
	public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add("java");
		c1.add("c");
		c1.add("c++");
		System.out.println("c1:"+c1);
		
		Collection c3 = new ArrayList();
		c3.add("c++");
		c3.add("c");
		System.out.println("c3:"+c3);
		//删除c1中c3的所有元素
		c1.removeAll(c3);
		System.out.println("c1:"+c1);
	}
}

集合的遍历(迭代器)

1、遍历集合

集合提供了统一的遍历操作
无论哪种数据结构实现的集合都提供了该遍历方式:迭代器模式

Iterator iterator()
Collection提供的iterator方法可以获取一个用于遍历当前集合的迭代器

java.util.Iterator接口
该接口是迭代器接口,规定了遍历集合的相关操作
所有集合都有一个用于遍历自身的迭代器实现类
我们无需关注它们的类名,以多态的角度用该接口看待并调用相关遍历方法即可

使用迭代器遍历集合的统一方式遵循为:
问->取->删 (其中删除元素不是必须操作)

2、实现步骤:

  1. 先创建迭代器对象(Iterator it),接收集合调用iterator方法获取的一个用于遍历当前集合的迭代器
    Iterator it = c.iterator();
    该步可以利用向上造型,无需关心迭代器的类名,使用Iterator接口接收即可
  2. 问:it.hasNext(),迭代器通过hasNext方法查看有没有下一个元素,若有返回true
  3. 取:it.next(),迭代器通过next方法,取出该元素,返回值为Object类(若确定返回值的具体类型,可以采取相应的强转)

**注意:**问与取要交替调用,问一次取一次,这个取并不会对原集合造成影响

public class IteratorDemo {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		System.out.println("c:"+c);
		//创建迭代器引用,接收返回的迭代器对象
		Iterator it = c.iterator();
		//问:迭代器通过hasNext方法查看有没有下一个元素,若有返回true
		while(it.hasNext()){
			//取:迭代器通过next方法,取出下一个元素,返回值为Object类
            //这里强转为了String类
			String str = (String)it.next();
			System.out.println(str);
		}
	}
}

3、通过迭代器删除元素

迭代器提供了remove方法
该方法不需要传入参数,删除的就是本次遍历通过next获取的元素

删除元素也遵循问取删原则

**注意:**在迭代器遍历元素时,是不允许通过集合操作,增删元素的,否则会抛异常
java.util.ConcurrentModificationException

public class IteratorDemo {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("one");
		c.add("#");
		c.add("two");
		c.add("#");
		c.add("three");
		c.add("#");
		c.add("four");
		c.add("#");
		c.add("five");
		System.out.println("c:"+c);
		
		Iterator it = c.iterator();
		//问:迭代器通过hasNext方法查看有没有下一个元素,若有返回true
		while(it.hasNext()){
			//取:迭代器通过next方法,取出下一个元素,返回值为Object类
			String str = (String)it.next();
			System.out.println(str);
            //删除满足条件元素
			if("#".equals(str)){
                //不能通过集合方法删除
				//c.remove(str);
                //通过迭代器对象删除,删除next获取的元素
				it.remove();
			}
		}
		System.out.println("c:"+c);
	}
}

4、增强for循环(for each)

JDK5之后推出了一个特性:增强for循环

也称为新循环,采用了迭代器的原理,它不取代传统for循环的工作

仅用来遍历集合或数组使用,遍历其中所有元素,而不需要使用下标值

语法格式:

for(类型 接收变量名 : 数组或集合){}

**注意:**新循环是编译器认可的,而不是虚拟机认可的,
编译器在编译源代码时会将新循环改成相应的格式,
遍历数组改为传统的for循环遍历,遍历集合改为迭代器
所以使用新循环遍历集合时,也不能操作集合增删集合元素

public class NewForDemo {
	public static void main(String[] args) {
		//使用新循环遍历数组
		String[] array = {"one","two","three","four","five"};
		for(String s : array){
			System.out.println(s);
		}
		//使用新循环遍历集合
		Collection c = new ArrayList();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		for(Object o : c){
			String s = (String)o;
			System.out.println(s);
		}
	}
}

泛型

泛型是JDK5之后推出的一个特性,又称为参数化类型

允许我们在使用一个类时指定他的属性、方法的参数和返回值类型,使得我们用起来更灵活

泛型的原型是Object,不指定时就使用它

泛型在集合中广泛使用,用于规定集合中的元素类型(集合中的泛型用来约束元素类型)

设计出来的初衷是,把变量类型的定义交给使用者,解决java作为强类型语言,变量命名时必须声明类型的问题

但其实泛型只是编译器支持,在运行时都是当做Object来看,底层实现用的是Object,我们可以通过反射破坏泛型(反射篇会提到)

public class CollectionDemo4 {
	public static void main(String[] args) {
		//<>里传入指定的类型,指定后,该集合只能存放该类型元素
		Collection<String> c = new ArrayList<String>();
        //JDK7以后,后面的<>指定泛型部分可以省略类型,编译器会自动和前面保持一致
		//List<String> list = new ArrayList<>();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		
		//迭代器也需要传参为集合指定的类型
		Iterator<String> it = c.iterator();
		while(it.hasNext()){
			String s = it.next();
			System.out.println(s);
		}
		
		//可以使用指定的类型来接收迭代器对象
		for(String s:c){
			System.out.println(s);
		}
	}
}

线性表(List集合)

1、java.util.List 线性表

List集合是一个可以重复的集合,并且有序

特点是提供了一组通过下标操作元素的方法

2、常用实现类(ArrayList与LinkedList)

  1. java.util.ArrayList 数组
    一段连续的存储空间,内部使用数组实现,查询性能更好,但是增删元素性能差
  2. java.util.LinkedList 链表
    各元素独立的存储,通过节点来互相连接
    内部使用链表实现,增删元素性能好,尤其首尾增删元素性能最佳,但是查询性能差

如果对性能没有特别苛刻的要求时,通常使用ArrayList即可

3、List常用方法(List独有)

  1. E get(int index) 获取给定下标处对应的元素(E指相应泛型)
  2. E set(int index,E e) 将给定元素设置到指定位置上(替换指定位置元素)

​ 有返回值,返回值为原位置对应的元素,所以set是替换元素操作

  1. void add(int index,E e) 将给定元素插入到指定位置(集合重载的方法)
  2. E remove(int index) 删除并返回给定位置元素(集合重载的方法)
  3. List subList(int start,int end) 获取当前集合中指定范围的子集(注意含头不含尾)
    **注意:**截取子集不会对原集合造成影响,但是截取出来的子集还是属于原集合,
    对截取出来的子集修改,会影响原集合,修改子集元素就是修改原集合对应元素
public class ListDemo1 {
	public static void main(String[] args) {
		//JDK7以后,后面的<>指定泛型部分可以省略类型,编译器会自动和前面保持一致
		List<String> list = new ArrayList<>();
		list.add("one");
		list.add("two");
		list.add("three");
		list.add("four");
		list.add("five");
		System.out.println(list);
		//1、
        //通过get方法获取第一个元素,用相应泛型接收
		String s1 = list.get(0);
		System.out.println("list中第一个元素"+s1);
		/*
		 * 普通for循环遍历List集合(不用迭代器)
		 * 因为List集合可以通过下标获取
		 */
		for(int i=0;i<list.size();i++){
			String str = list.get(i);
			System.out.println(str);
		}
        //2、
		//将第二个元素替换为2
		//list.set(1, "2");
		//可以用相应泛型接收替换下来的值
		String s2 = list.set(1, "2");
		System.out.println(list);
		System.out.println("替换下的元素为:"+s2);
		/*
		 * 在不创建新集合的情况下
		 * 将list集合元素颠倒位置
		 */
		//获取前后的值互换,只用走一半的循环
		for(int i=0;i<list.size()/2;i++){
			//将前面的数保存下来
			String start = list.get(i);
			//将前面的数替换掉后面对应位置的数,再将替换下来的保存
			String end = list.set(list.size()-1-i, start);
			//将替换下来的保存到开始的位置
			list.set(i, end);
			//缩写成一句话为:
			//list.set(i, list.set(list.size()-1-i, list.get(i)));
		}
		System.out.println(list);
		//3、
        //在下标为3的位置添加元素"two"
		list.add(3,"two");
		System.out.println(list);
		//4、
        //删除下标为4的元素
		//list.remove(4);
		//用相应泛型接收删除的值
		String s4 = list.remove(4);
		System.out.println(s4);
		System.out.println(list);
        //5、
        //获取下标3-5的集合元素子集(含下标元素3、4,因为含头不含尾)
		List<String> sublist = list.subList(3, 5);
		System.out.println(sublist);
	}
}

练习:

public class ListDemo2 {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		//对列表赋值
		for(int i=0;i<10;i++){
			list.add(i,i);
		}
		System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
		
		//截取列表子集内容为3-7(要截取3-8,含头不含尾)
		List<Integer> sublist = list.subList(3, 8);
		System.out.println(sublist);//[3, 4, 5, 6, 7]
		//不会对原集合造成影响,但是对该截取出来的子集修改,会影响原集合
		System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
		
		//将子集元素扩大10倍
		for(int i=0;i<sublist.size();i++){
			sublist.set(i, sublist.get(i)*10);
		}
		System.out.println(sublist);//[30, 40, 50, 60, 70]
		//注意这时会对原集合造成影响,原集合同样发生改变
		//修改子集元素就是修改原集合对应元素
		System.out.println(list);//[0, 1, 2, 30, 40, 50, 60, 70, 8, 9]
		
		//利用子集清空原集合中2-8之间的元素
		list.subList(3, 9).clear();
		System.out.println(list);//[0, 1, 2, 9]
	}
}

集合和数组的互转

1、集合转数组的方法(toArray)

T[] toArray(T[] a)

Collection 中定义了一个方法toArray,可以将当前集合转换为数组

用自己指定的泛型数组,接收转换后的数组,如果不指定,默认为Object,需要用Object数组接收

**注意:**toArray(new String[c.size]) 小阔号里面传入我们自己指定的类型数组对象,
这个数组对象只作为模范,能用(大小够用)就用,如果不能用,编译器会自己按照这个模范创建一个字节能用的数组对象

代码示例:

package collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionToArrayDemo {
	public static void main(String[] args) {
		Collection<String> c = new ArrayList<>();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		System.out.println(c);
		//toArray默认转换为Object类数组
		Object[] array = c.toArray();
		//自己传入一个数组对象的模范,按照这个模范生成数组
		String[] strArray = c.toArray(new String[c.size()]);
		System.out.println(Arrays.toString(strArray));
        //注意:这个数组模范能用则用,不能用会按照这个模范自己创建一个
		String[] strArray2 = c.toArray(new String[0]);
		System.out.println(Arrays.toString(strArray2));
	}
}

2、数组转集合(asList)

List<T> Arrays.asList(数组) 数组转为List集合

数组的工具类提供了一个静态方法asList,可以将给定的数组转换为一个List集合

**注意:**只能转为List集合,因为数组不能重复

这个数组转换的集合其实是数组的映射,对这个集合元素的操作就是对相应数组元素的操作

所以,由于数组定长,因此改变由数组转换集合的元素个数的操作(增、删)都是不支持的
会抛出异常 java.lang.UnsupportedOperationException

为了解决这个问题,我们可以创建一个真正的新集合,将数组转换集合的元素,全部添加到新集合中

所有集合都支持一个参数为Collection类型的构造方法
作用是创建该集合的同时包含给定集合中的所有元素

public class ArrayToListDemo {
	public static void main(String[] args) {
		String[] array = {"one","two","three","four","five"};
		//转换为相应的集合
		List<String> list = Arrays.asList(array);
		System.out.println(list);
		//注意:对这个集合的元素操作,就是对原数组相应元素的操作
		list.set(1, "2");
		System.out.println(list);
		//原数组也发生改变
		System.out.println(Arrays.toString(array));
		//由于这个原因,数组定长
		//不能对这个数组转换的集合进行增删操作
		//list.add("six");
		//为了解决这个问题,我们可以创建新集合接收数组转换集合的元素
		List<String> list1 = new ArrayList<>();
		list1.addAll(list);
		System.out.println(list1);
		//使用集合的重载构造方法,传入一个Collection类型的参数
		//创建该集合的同时包含给定集合中的所有元素
		List<String> list2 = new ArrayList<>(list);
		System.out.println(list2);
	}
}

实现一步操作(将数组转换为真正的集合)
public class ArrayToListDemo {
	public static void main(String[] args) {
		String[] array = {"one","two","three","four","five"};
		List<String> list = new ArrayList<>(Arrays.asList(array));
		System.out.println(list);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值