本文共 6705 字,大约阅读时间需要 22 分钟。
今天我们来学习Collection集合,学习这个集合之前我们先要知道为什么要引入集合。我们都直到Java语言是一种面向对象的语言,所以为了存取对象方便,就引入了集合的概念。
那么集合和数组到底又有什么区别呢?
了解了集合的基本概念以后,我们也必须要掌握常用的Collection集合的继承体系,如下:
在Collection中我们最常用的就是List集合和Set集合这两种集合,那么就先要只知道顶层父类的方法:这些方法简单易懂,就不做详细的解释了
当然子类的方法会更加丰富,现在我们就先从List这个分支学起:
List集合的特点:元素有序,并且每一个元素都存在一个索引,元素可以重复
ArrayList集合的底层数据结构是数组,查询快,增删慢;线程不安全,效率高
Vector集合底层数据结构是数组,查询快,增删慢。线程安全,效率低
Vector集合线程安全是因为它底层源码都用synchronized关键字所修饰LinkedList集合底层数据结构是链表,查询慢,增删快,线程不安全,效率高
下面我们来看看使用迭代器对象遍历集合:
public class Test { public static void main(String[] args) { //创建ArrayList集合对象,泛型定为Integer类型 ArrayListlist = new ArrayList<>(); //向集合中添加元素 list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); list.add(6); list.add(7); list.add(8); list.add(9); list.add(10); //获取Collection集合的父类迭代器对象 Iterator iterator = list.iterator(); //iterator.hasNext()判断下一个是否有元素 while (iterator.hasNext()){ //拿出下个元素,并打印 Integer next = iterator.next(); System.out.println(next); } }}/** * 输出结果:1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 */
当然List集合也有它特有的迭代器对象,listIterator,也都是一样的用法。
下来我们来看用迭代器对象遍历集合的时候,如果往集合中增加元素,会产生一个异常:
public class Test { public static void main(String[] args) { //创建ArrayList集合对象,泛型为Integer类型 ArrayListlist = new ArrayList<>(); //向集合中添加元素 list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); list.add(6); list.add(7); list.add(8); list.add(9); list.add(10); //获取Collection集合的父类迭代器对象 Iterator iterator = list.iterator(); //iterator.hasNext()判断下一个是否有元素 while (iterator.hasNext()){ //拿出元素 Integer next = iterator.next(); if (next==5){ list.add(100); } System.out.println(next); } }}
输出: 1 2 3 4 5 Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at cn.xiyou.Test.main(Test.java:26)
ConcurrentModificationException并发修改异常,产生这个异常的原因是因为使用迭代器进行遍历的时候,迭代器会计算好集合的长度,但是你在迭代的过程中添加元素,集合长度改变,迭代器就不知道长度为多少了,就会出现并发修改异常。
那么解决的办法有两个:
这里我们介绍一种新式的for循环:
public class Test { public static void main(String[] args) { //创建ArrayList集合对象,泛型为Integer类型 ArrayListlist = new ArrayList<>(); //向集合中添加元素 list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); list.add(6); list.add(7); list.add(8); list.add(9); list.add(10); //新式for循环遍历集合 for (Integer i : list) { System.out.println(i); } }}
Set集合的特点:元素无序(存取的顺序不一致)且唯一
HashSet的底层数据结构是哈希表(JDK1.7之前:链表+数组+红黑树;JDK1.7之后:链表+数组+红黑树),线程不安全,可以存null值。
那么HashSet集合是如何保证元素唯一性的呢?
为了保证HashSet集合的元素唯一性,HashSet集合必须要重写hashCode()和equals()方法。 1.当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据算出来的hashCode值去确定该对象在HashSet中的存储位置。 2.当两个对象的hashCode值相等时,我们会重写equals()方法,让两个对象去比较其内容,如果不一致则存储,如果一致则不存储 所以,要保证HashSet集合元素唯一就必须重写hashCode()和equals()方法。LinkedHashSet底层数据结构是哈希表+链表。链表保证元素有序,哈希表保证元素唯一。
TreeSet底层数据结构是二叉树,保证了元素有序
那么在TreeSet中有两种排序方式:
1). 自然排序
比较的对象要实现Comparable接口并重写该接口中compareTo方法,如下://实现Comparable接口,并将泛型定为Studentpublic class Student implements Comparable{ private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } 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 int compareTo(Student student) { //以姓名的字典顺序排序,如果姓名一样,则以年龄大小排序 int num = student.name.compareTo(this.name); int num2 = num==0?student.age-this.age:num; return num2; }}
然后新建一个测试类,使用TreeSet集合添加元素,遍历显示结果
public class Test { public static void main(String[] args) { //创建TreeSet集合并存储Student对象 TreeSetset = new TreeSet<>(); set.add(new Student("张三",23)); set.add(new Student("李四",24)); set.add(new Student("王五",25)); set.add(new Student("赵六",26)); set.add(new Student("田七",27)); set.add(new Student("牛二",28)); //遍历集合,查看自然排序结果 for (Student student : set) { System.out.println(student.getName()+"=="+student.getAge()); } }}
结果:
赵六==26田七==27王五==25牛二==28李四==24张三==23
2). 比较器排序
比较器排序就比较简单了,比较的类不用实现任何接口,直接向TreeSet集合中传入一个比较器Comparator即可:public class Test { public static void main(String[] args) { //创建TreeSet集合并传入一个Comparator比较器,然后重写compare方法 TreeSetset = new TreeSet<>(new Comparator () { @Override public int compare(Student s1, Student s2) { int num = s1.getName().compareTo(s2.getName()); int num2 = num==0?s1.getAge()-s2.getAge():num; return num2; } }); //向集合中存入对象 set.add(new Student("张三",23)); set.add(new Student("李四",24)); set.add(new Student("王五",25)); set.add(new Student("赵六",26)); set.add(new Student("田七",27)); set.add(new Student("牛二",28)); //遍历集合看结果 for (Student student : set) { System.out.println(student.getName()+"=="+student.getAge()); } }}
结果:
张三==23李四==24牛二==28王五==25田七==27赵六==26
我们可以看到,比较器排序与自然排序的结果是一样的,在我们平时,建议使用比较器排序
集合的学习是为了以后在编码中选取更合适的集合去存储数据,后面我们还会学到map集合,敬请期待哦!
转载地址:http://lxiwi.baihongyu.com/