集合
1、概述
1.1 定义
集合是JavaAPI中提供的一种容器工具,可以用来存储多个数据。
1.2 集合和数组之间的区别
- 数组长度固定,集合长度可变
- 数组存储同一类型数据,集合存储数据的类型可以不同
- 数组中可以存放基本类型数据或对象,集合中只能存放对象
- 数组是由JVM中现有的 类型+[] 组合而成的,除了一个length属性,还有从Object中继承过来的方法之外,数组对象就调用不到其他属性和方法了。
- 集合是由JavaAPI中的java.util包里面所提供的接口和实现类组成的,里面定义并实现了很多方法,可以使用集合对象直接调用这些方法,从而操作集合存放的数据。
1.3 集合框架三要素
1、接口
整个集合框架的上层结构,都是用接口进行组织的。接口中定义了集合中必须要有的基本方法。通过接口还把集合划分成了几种不同的类型,每一种集合都有自己对应的接口。
2、实现类
实现类对于上层使用接口划分好的集合种类,每种集合的接口都会有对应的实现类。每一种接口的实现类很可能有多个,每个的实现方式也会各有不同。
3、数据结构
每个实现类都实现了接口中所定义的最基本的方法,例如对数据的存储、检索、操作等方法。但是不同的实现类,它们存储数据的方式不同,也就是使用的数据结构不同。
2、按存储结构分类
关系图(常用)
2.1 java.util.Collection接口
2.1.1 Collection接口
1、定义
Collection接口时单列集和类的父接口,这种集合可以将数据一个一个存放到集合中,他下面有两个重要的子接口,java.util.List和java.util.Set
2、Collection集合类型中的部分基本方法
方法 | 功能 |
boolean add(E e) | 向集合中添加元素 |
boolean addAll(Collection<?> extends E> c) | 把一个指定集合中的所有元素添加到当前集合中 |
boolean clear() | 清空集合中所有元素 |
boolean contains(Object o) | 判断当前集合中是否包含给定的对象 |
boolean containsAll(Collection<?> c) | 判断当前集合中是否包含给定的集合的所有元素 |
boolean isEmpty() | 判断当前集合是否为空 |
boolean retainAll(Collection<?> c) | 判断两个集合中是否有相同的元素,有,则当前集合只保留相同元素,无,则清空当前集合 |
boolean removeAll(Collection<?> c) | 把给定的集合中的所有元素,在当前集合中删除 |
boolean remove(Object o) | 把给定的对象在当前集合中删除 |
int size() | 返回集合中元素的个数 |
Object[] toArray() | 把集合中的元素存储到数组中 |
T[] toArray(T[] a) | 把集合中的元素存储到数组中,并且指定数组的类型 |
Iterator iterator() | 返回遍历这个集合的迭代器对象 |
使用样例:
package blog_Collection_1;
import java.util.ArrayList;
import java.util.Collection;
public class Demo01 {
public static void main(String[] args) {
//创建Collection集合对象,ArrayList类是Collection接口的一个实现类,
//Collection接口是创建不了对象的
Collection c = new ArrayList();
//往集合里面添加元素
c.add("Hello1");
c.add("Hello2");
c.add("Hello3");
//输出集合中的元素,默认调用集合的toString方法
System.out.println("操作之前中的集合元素:" + c);
System.out.println("集合中的元素个数:" + c.size());
System.out.println("集合中是否包含Hello1" + c.contains("Hello!"));
System.out.println("删除Hello1:" + c.remove("Hello1"));
System.out.println("删除操作之后的元素" + c);
//把集合中的元素存储到数组中,并且遍历输出数组
Object[] array = c.toArray();
for(int i = 0; i<array.length; i++) {
System.out.println(array[i]);
}
//清除集合中的元素
c.clear();
System.out.println("集合中的内容为:" + c);
System.out.println(c.isEmpty()); //true
}
}
结果
操作之前中的集合元素:[Hello1, Hello2, Hello3]
集合中的元素个数:3
集合中是否包含Hello1false
删除Hello1:true
删除操作之后的元素[Hello2, Hello3]
Hello2
Hello3
集合中的内容为:[]
true
2.1.2 List集合
1、定义
java.util.List接口继承了Collection接口,是最常用的一种集合类型,继承了Collection集合的特点和方法。
2、特点
- List是一种有序的集合
- List是一种带索引的集合
- 可以存放重复数据的集合
3、List接口常用的方法
方法 | 功能 |
E get(int index) | 返回集合中指定位置的元素 |
E set(int index, E element) | 用指定元素替换集合中指定位置的元素,并返回被替代的旧元素 |
void add(int indx, E element) | 将指定的元素添加到该集合中的指定位置上 |
boolean addAll(int index, Collection<? extends E> c) | 从指定位置开始把另一个集合的所有元素添加进来 |
E remove(int index) | 移除列表中指定位置的元素,并返回被移除的元素 |
int indexOf(Object o) | 从前往后查指定元素第一次出现时的索引 |
int lastIndexOf(Object o) | 从后往前查指定元素最后一次出现时的索引 |
int subList(int fromIndex, int toIndex) | 根据指定开始和结束索引位置截取集合中一部分数据 |
除此之外还有从父接口Collection继承过来的接口
代码样例:
public class Demo02 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello1");
list.add("hello1");
list.add("java");
list.add("hello1");
System.out.println(list);
//往指定索引位置添加元素
list.add(1,"world");
System.out.println(list);
//修改指定位置元素
list.set(0, "HELLO");
System.out.println(list);
//查找hello1第一次和最后一次出现的索引
System.out.println(list.indexOf("hello1"));
System.out.println(list.lastIndexOf("hello1"));
//遍历集合
for(int i = 0; i<list.size(); i++) {
System.out.println(list.get(i));
}
}
}
结果:
[hello1, hello1, java, hello1]
[hello1, world, hello1, java, hello1]
[HELLO, world, hello1, java, hello1]
2
4
HELLO
world
hello1
java
hello1
4、List实现类
常用的实现类有如下:
(1) java.util.ArrayList实现类:最常用的一种List类型集合,ArrayList类中使用数组来实现的存储,特点是:增删慢,查找快,多用于查询。
(2) java.util.LinkedList实现类:存储数据采用的数据结构是链表,特点是:增删快,查找慢。多用在需要删除大量数据的时候。
(3) Vector实现类:采用数组存储数据,Vector中的方法大多数都是线程安全的方法,在多线并发访问的环境中,可以使用Vector来保证集合中元据操作的安全。这里就不介绍了。
这里描述的增删与查找快慢,需要在大量的数据操作下,才可以体现,如果数据量不大的话,集合每一种集合的操作几乎没有任何区别。
测试如下:
public static void main(String[] args) {
//操作集合的次数
final int NUM = 100000;
List list;
//list = new ArrayList();
list = new LinkedList();
long start1 = System.currentTimeMillis();
for (int i = 0; i < NUM; i++) {
list.add(0,"hello"+i);
}
long end1 = System.currentTimeMillis(); //可以获取当前时刻的时间戳
System.out.println(list.getClass().getSimpleName()+"插入"+NUM+"条数据耗时"+(end1-start1)+"毫秒");
//list.getClass().getSimpleName()方法可以获取list引用当前指向对象的实际类型的简单名字
long start2 = System.currentTimeMillis();
for(int i=0;i<list.size();i++){
list.get(i);
}
long end2 = System.currentTimeMillis();
System.out.println(list.getClass().getSimpleName()+"检索"+NUM+"条数据耗时"+(end2-start2)+"毫秒");
}
在我电脑上运行结果如下(每台电脑结果可能不同):
ArrayList插入100000条数据耗时631毫秒
ArrayList检索100000条数据耗时3毫秒
LinkedList插入100000条数据耗时30毫秒
LinkedList检索100000条数据耗时24252毫秒
1、ArrayList
ArrayList中的常用方法基本就和List接口中的方法一样,或者查下API就知道了。。。
2、LinkedList
下面多介绍下LinkedList实现类,LinkedList比List接口多出了定义操作头节点和尾节点的方法
方法 | 功能 |
void addFirst(E e) | 将指元素插入此列表的开头 |
void addLast(E e) | 将指定元素添加到此列表的末尾 |
E getFirst() | 返回此列表的第一个元素 |
E getLast() | 返回此列表的最后一个元素 |
E pop() | 从此列表所表示的堆栈处弹出一个元素 |
void push(E e) | 将元素推入此列表所表示的堆栈 |
E removeFirst() | 移除并返回此列表的第一个元素 |
E removeLast() | 移除并返回此列表的最后一个元素 |
ArrayList底层数据结构是数组,所以带下标;LinkedList底层数据结构是链表,也有下标吗??
答案是有的,ArrayList下标是固定的,通过下标可以快速找到对应的集合元素,LinkedList的下标是隐性的,下标是可以不断变化的,每次查找都是从集合第一个元素开始查找开始,也就是从这个时候开始标记下标的。
2.1.3 Set集合
1、定义
java.util.Set接口继承了Collection接口,是常用的一种结合类型。
2、特点
除了具有Collection集合的特点之外还具有自己的一些特点
- Set是一种无序集合
- Set是一种不带下标索引的集合
- Set是一种不能存放重复数据的集合
3、set中的方法
全部继承自他的父接口Collection,所以就是父接口中的方法
4、Set接口常用的实现类
1、HashSet实现类
java.util.HashSet类的实现,主要依靠的是HashMap,而HashMap的实现主要是利用【哈希表】
【思考】HashSet为什么是无序的,它是怎么判断元素是否是重复的?
HashSet中存储元素是无序的,主要因为它是靠对象的哈希值来确定元素在集合中的存储位置。HashSet中元素不可重复,主要是靠对象的hashCode和equals方法来判断对象是否重复
【思考】HashSet是如何保证重复元素的存在的?比较过程是怎样的?
HashSet中判断对象是否重复是根据对象的hashCode值和equals的比较结果,而不是根据对象的属性值是否相等
比较过程:如果俩个对象的hashCode值相等,那么再使用equals判断是否俩对象是否相同,如果俩个对象的hashCode值不同等,那么就不再使用equals进行判断了,因为hashCode不同的俩个对象一定是不同的俩个对象
想要将Student对象根据我们的要求进行去重,就要重写Student中的hashCode方法和equals方法
例如如下两个代码(未重写和重写后的)
未重写的
public class Student{
String name;
int age;
public Student(){}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) {
Set set = new HashSet();
set.add(new Student("tom",20));
set.add(new Student("tom",20));
set.add(new Student("tom",20));
set.add(new Student("tom",20));
for(Object obj : set){
System.out.println(obj);
}
}
//运行结果为:
Student{name='tom', age=20}
Student{name='tom', age=20}
Student{name='tom', age=20}
Student{name='tom', age=20}
重写后的
public class Student{
String name;
int age;
public Student(){}
public Student(String name, int age) {
this.name = name;this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int hashCode() {
return Objects.hash(name, 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);
}
}
public static void main(String[] args) {
Set set = new HashSet();
set.add(new Student("tom",20));
set.add(new Student("tom",20));
set.add(new Student("tom",20));
set.add(new Student("tom",20));
for(Object obj : set){
System.out.println(obj);
}
}
//输出结果:Student{name='tom', age=20}
2、SortedSet接口下的TreeSet实现类
TreeSet实现类可以将我们存进去的数据进行排序,排序方式有以下两种:
(1)自然排序
定义:自然排序指的是一个类实现了java.lang.Comparable接口,那么这个类的两个对象是可以比较大小的。
// comparable接口
public interface Comparable<T> {
public int compareTo(T o);
}
关于comparaTo方法
int result = o1.comparaTo(o2);
- result的值大于0,说明o1比o2大
- result的值小于0,说明o1比o2小
- result的值等于0,说明o1与o2相等
样例1:
public class Test01 {
public static void main(String[] args) {
Set set = new TreeSet<>();
set.add(5);
set.add(4);
set.add(2);
set.add(1);
set.add(7);
System.out.println(set);
}
}
结果如下:[1, 2, 4, 5, 7]
分析:
(1)调用add方法时,会进行自动装箱,把基本类型数据变为Integer对象,然后在存放到集合中
(2)Integer类是实现了Comparable接口的,所以可以调用compareTo方法比较大小,因此结果就是按照默认的从小到大的顺序排序输出的。
样例2:
public class Test01 {
public static void main(String[] args) {
Set set = new TreeSet();
set.add(new Student("mary",23));
set.add(new Student("jack",34));
set.add(new Student("rose",33));
set.add(new Student("jerry",24));
for(Object obj : set) {
System.out.println(obj);
}
}
}
class Student implements Comparable{
String name;
int age;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//重写接口中的compareTo方法,比较学生对象中的年龄属性的大小
@Override
public int compareTo(Object o) {
//下面的this和other可以理解为:this代表我,other代表你,你就是当前要和我比较的对象
Student other = (Student) o;
//如果我的年龄比你大,就返回正数
if(this.age > other.age) {
return 1;
}
//如果我的年龄比你小,就返回负数
else if(this.age < other.age) {
return -1;
}
//其他情况返回0,
else {
return 0;
}
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
一个类实现Comparable接口后才可以使用CompareTo方法
(2)比较器排序(客户化排序)
java.util.Comparator接口,是一个比较器接口,它的实现类可以对俩个对象作出大小的比较,即使对象没有实现Comparable接口,也可以进行比较。
public interface Comparator<T> {
int compare(T o1, T o2);
}
比较规则和自然排序规则一样,只关心正数,负数,零,不关心具体的值是多少。
样例如下:
public class Test01 {
public static void main(String[] args) {
//使用Comparator接口,创建出匿名内部类对象,这个对象就是要用的比较器对象
Comparator c = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//先将参数中的Object对象向下转型为Student类对象
Student s1 = (Student) o1;
Student s2 = (Student) o2;
//这里返回结果1,或-1,或0,并且是通过三目运算符求出的结果
return s1.age>s2.age ? 1 : (s1.age == s2.age ? 0 : -1);
}
};
//创建TreeSet对象时要将比较器对象传入
Set set = new TreeSet(c);
set.add(new Student("mary",23));
set.add(new Student("jack",34));
set.add(new Student("rose",33));
set.add(new Student("jerry",24));
for(Object obj : set) {
System.out.println(obj);
}
}
}
class Student {
String name;
int age;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
结果:
Student [name=mary, age=23]
Student [name=jerry, age=24]
Student [name=rose, age=33]
Student [name=jack, age=34]
2.1.4 Queue接口及其子接口Deque
Queue是队列接口,Deque是双端队列,JavaAPI中提供了java.util.Stack来实现栈结构,但官方目前已不推荐使用,而是推荐使用java.util.Deque双端队列来实现队列与栈的各种需求,LinkedList同样实现了这个两个接口,所以LinkedList同时具有队列和栈的操作方法,pop和push都是栈结构的操作方法。
Queue中方法和Deque中方法对比:
Stack方法和Deque中方法作对比:
2.2 java.util.Map接口
2.2.1 定义
关于映射:成对出现,并且一一对应的数据关系。
而专门处理这种映射关系数据的集合类型就是Map集合了
与Collection集合类型比较,存储数据的形式是不同的
如图可见:Map集合每次需要一对数据,key-value(键值对)
key与value的关系如下:
(1)key值必须是惟一的,value值允许重复
(2)键(key)和值(value)一一映射,一个key对应一个value
(3)在Map中通过key值可以快速的找到对应的value值
2.2.2 Map接口中常用方法
方法 | 功能 |
V put(K key, V value) | 把key和value存到当前Map集合中 |
void putAll(Map<? extend K, ? extends V> m) | 把指定map中的所有key-value存到当前Map集合中 |
boolean containsKey(Object key) | 判断当前Map集合是否包含指定的key值 |
boolean containsValue(Object value) | 当前Map集合中是否包含指定的value值 |
void clear() | 清空当前Map集合中的所有数据 |
V get(Object key) | 在当前Map集合中,通过指定的key值,获取对应的value |
V remove(Object key) | 在当前Map集合中,移除指定key及其对应的value |
int size() | 返回当前Map集合中的元素个数(一对key-value,算一个元素数据) |
boolean isEmpty() | 判断当前Map集合是否为空 |
Set keySet() | 返回Map集合中所有的key值 |
Collection values() | 返回Map集合中所有的value值 |
Set <Map.Entry<K,V>> entrySet() | 把Map集合中的的key-value封装成Entry类型对象,再存放到set集合中,并返回 |
//map集合中部分方法的使用
public class Demo01 {
public static void main(String[] args){
Map map = new HashMap();
map.put(1,"tom");
map.put(2,"jack");
map.put(3,"jerry");
map.put(4,"lucy");
System.out.println("map是否为空:" + map.isEmpty());
System.out.println("map中元素个数:" + map.size());
System.out.println("map中是否包含指定key值1:" + map.containsKey(1));
System.out.println("map中是否包含指定value值Mary" + map.containsValue("mary"));
System.out.println("map中获取指定key值对应的value值:" + map.get(1));
}
}
结果:
map是否为空:false
map中元素个数:4
map中是否包含指定key值1:true
map中是否包含指定value值Mary:false
map中获取指定key值对应的value值:tom
2.2.3 Map常用实现类
1、HashMap<K, V>实现类
简介: 存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。(重要,最常用)
子类:LinkedHashMap实现类
简介: 该类是HashMap的子类,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;(存入顺序就是取出顺序)
public static void main(String[] args){
Map map = new LinkedHashMap();
map.put(4,"mary");
map.put(2,"jack");
map.put(1,"tom");
map.put(3,"lucy");
Set keys = map.keySet();
for(Object key : keys){
System.out.println(key + ":" + map.get(key);
}
}
结果:
4 : mary
2 : jack
1 : tom
3 : lucy
2、Hashtable<K,V>实现类
简介:和List集合中的Vector的功能类似,可以在多线程环境中,保证集合中的数据的操作安全,类中的方法大多数使用了synchronized修饰符进行加锁。(线程安全)
3、SortedMap<K,V>接口下面的TreeMap实现类
简介:该类是Map接口的子接口SortedMap下面的实现类,和TreeSet类似,它可以对key值进行排序,同时构造器也可以接收一个比较器对象作为参数。支持key值的自然排序和比较器排序俩种方式。(支持key排序)
(1)自然排序
public static void main(String[] args) {
Map map = new TreeMap();
map.put(4,"mary");
map.put(2,"jack");
map.put(1,"tom");
map.put(3,"lucy");
Set keys = map.keySet();
for(Object key : keys){
System.out.println(key+" : "+map.get(key));
}
}
//运行结果:
1 : tom
2 : jack
3 : lucy
4 : mary
(2)比较器排序
public class Demo02 {
public static void main(String[] args) {
//创建一个比较器对象,使用匿名内部类重写Comparator接口中的compare方法
Comparator c = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer k1 = (Integer) o1; //为什么是转为Integer类型,因为键值是Integer类型的
Integer k2 = (Integer) o2;
//这一行返回正常的比较结果,即1表示大于,0表示等于,-1表示小于
// return k1.compareTo(k2); //代码一
//这行在默认的比较结果基础上加了一个负号,即原来返回的正数变负数、返回的负数变正数
return -k1.compareTo(k2); //代码二
}
};
//创建集合对象,并且将比较器对象传入
Map map = new TreeMap(c);
map.put(4,"mary");
map.put(2,"jack");
map.put(1,"tom");
map.put(3,"lucy");
Set keys = map.keySet();
for (Object key : keys ){
System.out.println(key + " : " + map.get(key));
}
}
}
结果:
代码一:
1 : tom
2 : jack
3 : lucy
4 : mary代码二:
4 : mary
3 : lucy
2 : jack
1 : tom
2.2.4 Map集合的遍历方式
public class Demo01 {
public static void main(String[] args){
Map map = new HashMap();
map.put(1,"tom");
map.put(2,"jack");
map.put(3,"jerry");
map.put(4,"lucy");
// System.out.println("map是否为空:" + map.isEmpty());
// System.out.println("map中元素个数:" + map.size());
// System.out.println("map中是否包含指定key值1:" + map.containsKey(1));
// System.out.println("map中是否包含指定value值Mary:" + map.containsValue("mary"));
// System.out.println("map中获取指定key值对应的value值:" + map.get(1));
//遍历
//方式一:先获取key键,间接获取value值
//keySet方法:返回Map集合中所有的key值
System.out.println("--------------");
Set keys = map.keySet();
for(Object key : keys){
System.out.println(key + ":" + map.get(key));
}
//方式二:直接获取value值
//values()方法:返回Map集合中所有的value值
System.out.println("--------------");
Collection values = map.values();
for(Object value : values){
System.out.println(value);
}
//方式三:-获取map中所有的key-value(键值对),封装成Entry类型对象
System.out.println("--------------");
Set entrySet = map.entrySet(); //获取所有键值对
for(Object obj : entrySet){
Map.Entry entry = (Map.Entry) obj; //封装成Entry类型对象
System.out.println(entry.getKey() + ":" + entry.getValue()); //调用getKey()方法和getValue()方法分别获取键值
}
}
}
1:tom
2:jack
3:jerry
4:lucy
--------------
tom
jack
jerry
lucy
--------------
1:tom
2:jack
3:jerry
4:lucy
3、集合工具类
java.util.Collections是一个工具类,专门用来操作集合对象的,里面都是静态方法,可以直接调用方法
(1)fill方法,使用指定元素替换指定列表中的所有元素
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
Collections.fill(list, 20);
for(Object o:list){
System.out.println(o);
}
(2)max和min方法,根据元素的自然排序返回给定集合的最大和最小元素
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
(3)reverse方法:反转集合中的元素
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
Collections.reverse(list);
for(Object obj:list){
System.out.println(obj);
}
(4)sort方法:根据元素的自然顺序,对指定列表按升序进行排序
List list = new ArrayList();
list.add(1);
list.add(9);
list.add(3);
//如果需要,也可以在第二个参数位置传一个比较器对象
//Collections.sort(list,c);
Collections.sort(list);
for(Object o:list){
System.out.println(o);
}
(5)shuffle方法:使用默认随机源对指定列表进行置换
List list = new ArrayList();
list.add(1);
list.add(9);
list.add(3);
Collections.shuffle(list);
for(Object o:list){
System.out.println(o);
}
(6)addAll方法,往集合中添加元素
List list = new ArrayList();
//注意,addAll的第二个参数,是可变参数
Collections.addAll(list,1,3,5,7);
for(Object o:list){
System.out.println(o);
}
4、集合的迭代遍历
下面我们来稍微详细的讲解下集合迭代的方式
设有下面如下集合,用三种方式遍历该集合
List list = new ArrayList();
list.add("hello");
list.add("java");
list.add("world");
方式1:普通for循环
对于有下标标记的集合可以使用普通for循环
for(int i = 0; i<list.size(); i++){
System.out.println(list.get(i));
}
方式2:增强for循环(foreach)
for(Object obj : list){
System.out.println(obj);
}
方式3:迭代器遍历
注意:
只有List集合和Set集合才可以使用迭代器遍历,因为Set、List集合都继承了Collection接口,而Collection接口又继承了Iterable接口,Iterable接口中提供了iterator方法,该方法可以返回一个迭代器,用来遍历集合。Map集合则没有继承该接口。
Iterator it = list.iterator(); //list调用iterator方法,返回一个迭代器对象
while(it.hasNext()){ //当集合中还有元素时,循环继续
Object obj = it.next(); //next()方法获取集合中的元素
System.out.println(obj);
}