什么是java集合? 1、java集合大致分为Set、List、Queue、Map四种体系。 List代表有序、可重复的集合;(有序指存储顺序和取出顺序一致) Set表示无序、不可重复(元素唯一)的集合;(无序指存储顺序和取出顺序不一致) Map代表具有映射关系的集合; Queue代表一种队列集合。 2、java集合就像一个容器,可以把多个对象(实际上是对象的引用,习惯都称之为对象)“丢进”该容器中。 3、java集合可以记住容器中对象的数据类型,从而编写出更简洁、简装的代码。
为什么要使用java集合? 1、在编程时,常常需要集中存放多个数据。可以使用数组来存放多个对象,但是数组的长度是不可变化的。在一开始定义了数组的长度之后,这个长度就是不可变化的,当数据量超过数组的长度之后,数组就无能为力了。 2、数组不能存储具有映射关系的数据。 3、数组只能存储同一类型的元素,而集合可以存储不同类型元素 4、数组可以存储基本数据类型,也可以存储引用数据类型;但是集合只能存储引用类型
为什么会出现集合类? 面向对象语言对事务的体现都是以对象的形式,所以为了方便对多个对象的操作,java就提供了集合类。
集合类的特点? 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
集合只能存储引用类型,那么它要存储基本类型数据该怎么办呢? 在JDK5以后有了自动装箱的功能,JVM会把基本类型的数据自动转化为对应的包装类型,然后进行存储。 自动装箱和自动拆箱详解请看我这篇文章:http://blog.csdn.net/qq_36748278/article/details/77484436
如何访问不同集合中的元素? 1、如果访问List集合中的元素,可以直接根据元素的索引来访问 2、如果访问Map集合中的元素,可以根据每项元素的key来访问其value 3、如果访问Set集合中的元素,则只能根据元素本身来访问(因为把一个对象加入到Set集合时,Set无法记住添加这个元素的顺序,所以Set里的元素不能重复)
集合的使用步骤: 1、创建集合对象 2、创建元素对象 3、把元素添加到集合 4、遍历集合 a、通过集合对象获取迭代器 b、通过迭代器hasNext()方法判断是否有元素 c、通过迭代器对象的next()方法获取元素并移动到下一个位置
Collection接口:集合的顶层接口 Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法即可用于操作Set集合,也可用于操作List和Queue集合。
Collection接口里定义了如下操作集合的方法: 1、boolean add(Object o):向集合里添加一个元素,成功添加则返回true。 2、boolean addAll(Collection c):把集合c里的所有元素添加到指定集合里,成功添加,返回true。
3、void clear():清除集合里的所有元素,将集合长度变为0。
5、boolean contains(Object o):判断集合中是否包含指定元素 6、boolean containsAll(Collection c):判断集合中是否包含集合c中的所有元素。
7、boolean isEmpty():判断集合是否为空。为空的时候返回true,否则返回false
8、boolean remove(Object o):删除集合中指定的o元素,当集合中含有多个o元素的时候,只删除第一个符合条件的元素,并将返回true 9、boolean removeAll(Collection c):从集合中删除集合c里包含的所有元素(相当于调用该方法的集合 - 集合c)
10、boolean retainAll(Collection c):从集合中删除集合c里不包含的元素(也就是把调用该方法的集合变成该集合和集合c的交集)(交集方法)。 11、int size():返回集合里元素的个数 12、Object[] toArray():该方法把一个集合转换为数组,所有的集合元素变成对应的数组元素
什么是集合的继承体系结构? 由于需求不同,java就提供了不同的集合类。这些集合类的数据结构不同,但是他们都要提供存储和遍历功能的,把他们的共性不断向上提取,最终就像成了集合的继承体系图。
Iterator接口 Iterator iterator():返回一个Iterator对象,用于遍历集合中的元素
测试一下retainAll 假设有集合A和B,A调用此方法。把A与B的交集,交集的结果保存在A中,B中保持不变。 返回值表示A中内容是否发生过改变。
public class TestRtainAll { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("第一个元素"); list.add("第二个元素"); list.add("第三个元素"); ArrayList<String> list1 = new ArrayList<String>(); list1.add("第二个元素"); list1.add("第四个元素"); list1.add("第三个元素"); boolean ret = list.retainAll(list1); System.out.println(ret); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } System.out.println("------"); for (int i = 0; i < list1.size(); i++) { System.out.println(list1.get(i)); } } }输出结果为:
true 第二个元素 第三个元素 ------ 第二个元素 第四个元素 第三个元素boolean ret = list.retainAll(list1); 调用retainAll方法之后,list集合就变成了list集合与list1集合的交集。而list1集合保持不变
测试一下aaddAll(Collection c)方法
public class CollectionDemo { public static void main(String[] args) { // 创建集合1 Collection<String> c1 = new ArrayList<String>(); c1.add("abc1"); c1.add("abc2"); c1.add("abc3"); //创建集合2 Collection<String> c2 = new ArrayList<String>(); c2.add("def1"); c2.add("abc2"); c2.add("def3"); System.out.println(c1); c1.addAll(c2); System.out.println(c1); c1.removeAll(c2); System.out.println(c1); } }输出:
[abc1, abc2, abc3] [abc1, abc2, abc3, def1, abc2, def3] //可见ArrayList()里面的值可以重复 [abc1, abc3]集合的遍历—–Object[] toArray():把集合转换成数组,可以实现集合的遍历(不推荐)
public class CollectionDemo { public static void main(String[] args) { Collection<String> c = new ArrayList<String>(); c.add("I"); c.add("love"); c.add("you"); Object[] objs = c.toArray(); //数组里存放的是Object类型 for(int i = 0;i < objs.length;i++){ String s = (String)objs[i]; //把Object类型转换为String类型 System.out.println(s); } } }输出:
I love you集合的遍历—–增强for循环:
public class CollectionDemo { public static void main(String[] args) { Collection<String> c = new ArrayList<String>(); c.add("I"); c.add("love"); c.add("you"); for(int i = 0;i < c.size();i++){ System.out.println(c.get(i)); } } }输出:
I love you集合的遍历—–Iterator迭代器。迭代器是依赖于集合而存在的。先有集合,再有迭代器。 Iterator是个接口。这个接口有3个方法: boolean hasNext() :如果仍有元素可以迭代,则返回 true。 E next() : 返回迭代的下一个元素。 最初指向第一个元素的上面开始。 void remove() :从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
public class CollectionDemo { public static void main(String[] args) { Collection<String> c = new ArrayList<String>(); c.add("you"); c.add("and"); c.add("me"); Iterator it = c.iterator(); //Iterator是个接口,所以实际返回的是子类对象。it是集合c的迭代器 while(it.hasNext()){ System.out.println(it.next()); } } }一定要记住next()的强大作用。一定要慎用next()方法,不要多次使用这个方法。因为每次使用都是访问一个对象 比如Iterator遍历一个集合中存放的是Student对象(有name和age属性)的情况的时候;
public class Test { public static void main(String[] args) { Collection<Student> c = new ArrayList<Student>(); Student s1 = new Student("梨梨",21); Student s2 = new Student("熊熊",24); Student s3 = new Student("菜菜",10); c.add(s1); c.add(s2); c.add(s3); Iterator it = c.iterator(); //Iterator是个接口,所以实际返回的是子类对象。 while(it.hasNext()){ //System.out.println(((Student) it.next()).getName() + "-----" ((Student)it.next()).getAge()); //报错,因为遍历最后一个对象的时候,next()在getName()的时候已经是最后一个对象元素了,在后面的getAge()的时候又进行了一次next()方法,所以越界了 Student s = (Student)it.next(); System.out.println(s.getName() + "----" + s.getAge()); } } }迭代器为什么不定义成一个类?而是要定义成一个接口? 因为如果是实现类,它就必须提供具体实现。但是集合分为很多种不同的结构,它的每一个的实现类当然也就不同。
Collections:针对集合操作的工具类,都是静态方法。
public static <T> void sort(List<T> list):排序。默认情况下是自然排序。 public static <T> int binarySearch(List<?> list,T key):二分查找 public static <T> T max(Collection<?> coll):最大值 public static void reverse(List<?> list):反转 public static void shuffle(List<?> list):随机置换当ArrayList存储基本包装类时,Collections操作方法使用:
public class CollectionsDemo { public static void main(String[] args) { //创建集合对象 List<Integer> list = new ArrayList<Integer>(); //添加元素 list.add(30); list.add(50); list.add(10); list.add(40); list.add(20); System.out.println(list); //public static <T> void sort(List<T> list):排序,默认情况下是自然排序。 Collections.sort(list); System.out.println(list); //public static <T> int binarySearch(List<?> list,T key):二分查找 System.out.println(Collections.binarySearch(list, 30)); System.out.println(Collections.binarySearch(list, 300)); //public static <T> T max(Collection<?> coll):最大值 System.out.println(Collections.max(list)); //public static void reverse(List<?> list):反转 Collections.reverse(list); System.out.println(list); //public static void shuffle(List<?> list):随机置换(没有固定的顺序) Collections.shuffle(list); System.out.println(list); } }输出:
[30, 50, 10, 40, 20] [10, 20, 30, 40, 50] 2 -6 50 [50, 40, 30, 20, 10] [10, 30, 20, 50, 40]当ArrayList存储自定义对象的时候,Collections的这些静态方法的使用如下: Student类:
public class Student2{ private String name; private int age; public Student2() { } public Student2(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; } }测试类:
public class CollectionsDemo2 { public static void main(String[] args) { //创建集合对象 List<Student2> list = new ArrayList<Student2>(); //创建学生对象 Student2 stu1 = new Student2("caicai",21); Student2 stu2 = new Student2("lili",12); Student2 stu3 = new Student2("xiong",34); Student2 stu4 = new Student2("xiong",34); Student2 stu5 = new Student2("hehe",12); //添加元素对象 list.add(stu1); list.add(stu2); list.add(stu3); list.add(stu4); list.add(stu5); //排序 Collections.sort(list); //自然排序,报错 //遍历集合 for(Student2 s : list){ System.out.println(s.getName() + "----" + s.getAge()); } } }我们会发现,程序sort()方法报错了。为什么呢?我们来看看sort方法:
public static <T extends Comparable<? super T>> void sort(List<T> list)可发现列表中的所有元素都必须实现 Comparable 接口。而此时列表中元素是Student类型,所以必须在Student类中实现Comparable 才行。 修改后的Student类如下:
public class Student2 implements Comparable<Student2>{ private String name; private int age; public Student2() { } public Student2(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(Student2 s) { int num = this.age - s.age; int num2 = (num == 0 ? this.name.compareTo(s.name) : num); return num2; } }输出:
hehe----12 lili----12 caicai----21 xiong----34 xiong----34上面问题还有一种解决办法:通过构造器排序
public static <T> void sort(List<T> list,Comparator<? super T> c)我们可以重写这个Comparator来达到同样的目的。 如果同时又自然排序和比较器排序,以比较器排序为主
//法二:通过比较器 Collections.sort(list,new Comparator<Student2>() { @Override public int compare(Student2 s1, Student2 s2) { int num1 = s1.getAge() - s2.getAge(); int num2 = (num1 == 0 ? s1.getName().compareTo(s2.getName()):num1); return num2; } });输出:
hehe----12 lili----12 caicai----21 xiong----34 xiong----34List:有序,可重复。(存入和取出顺序一致) ArrayList:底层数据结构是数组, 查询快,增删慢。 线程不安全,效率高。 Vector:底层数据结构是数组, 查询快,增删慢。 线程安全,效率低。 LinkedList:底层数据结构是链表, 查询慢,增删快。 线程不安全,效率高。 Set:无序,唯一。 HashSet:底层数据结构是哈希表。 保证元素唯一性:hashCode()和equals()方法。 LinkedHashSet:底层数据结构是链表和哈希表 保证元素唯一性:哈希表 保证元素有序(存入和取出顺序一致,是特殊的Set):链表 TreeSet:底层数据结构是红黑树。 保证元素排序:自然排序和比较器排序 自然排序(元素具有比较性):让元素所属的类实现Comparable接口 比较器排序(集合具有比较性):让集合接收一个Comparator的实现类对象 保证元素唯一性:根据比较的返回值是否是0来决定
List集合的特点是什么? 1、有序(存储和取出的元素一致) 2、可重复的。
List集合的三个子类各有什么特点? ArrayList 1、底层数据结构是数组,查询快,增删慢。 2、线程不安全,效率高
Vector 1、底层数据结构是数组,查询快,增删慢。 2、线程安全,效率低
LinkedList 1、底层数据结构是链表,查询慢,增删快。 2、线程不安全,效率高。
List的3个子类都在什么情况下使用? 看自己的需求: 1、要求安全性:Vector(不过现在大部分都不用Vector) 2、不要求安全性:ArraayList或者LinkedList 2.1、查询多:ArraayList 2.1、增删多:LinkedList
ArrayList()方法里面为什么允许有重复的值存在? 我们看一下add()方法的源码:
public boolean add(E e) { ensureCapacity(size + 1); elementData[size++] = e; return true; }我们可以看到他永远返回的都是true,也就是他每次添加都能成功,所以也就是可以添加重复的对象。
List集合特有的一些功能(父元素没有的功能)。 要注意索引的范围是否越界。 void add(int index, Object element) :在指定位置添加元素 Object get(int index):获取指定位置的元素 ListIterator listIterator():List集合特有的迭代器。 Object remove(int index):根据索引删除元素,返回被删除的元素 Object set(int index, Object element):根据索引修改元素,返回被修改的元素
public class ListDemo { public static void main(String[] args) { ArrayList list = new ArrayList<String>(); list.add("hello"); list.add("you"); list.add(1,"java"); //list.add(4,"no"); //出错,索引越界了 list.add(3,"last"); //可以添加此位置 System.out.println("get方法获取要获得的元素:" + list.get(1)); System.out.println(list); System.out.println("set方法返回被修改的元素:" + list.set(2,"me")); System.out.println(list); } }输出:
get方法获取要获得的元素:java [hello, java, you, last] set方法返回被修改的元素:you [hello, java, me, last]List集合特有的遍历:size()+get()方法结合(普通for循环)
public class ListDemo { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("today"); list.add("is"); list.add("yours"); for(int i = 0;i < list.size();i++){ String s = (String)list.get(i); System.out.println(s); } } }输出:
today is yoursList集合特有的迭代器— >列表迭代器ListIterator(它的父亲是Iterator) 该迭代器继承了Iterator迭代器,所以就可以直接使用hasNext()和next()方法。 特特有的功能是它也可以向前访问元素。 Object previous():获取上一个元素。 boolean hasPrevious():判断是否有上一个元素。 此时就需要注意指针的位置。
public class ListDemo { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("today"); list.add("is"); list.add("yours"); ListIterator<String> lit = list.listIterator(); //此时指针在最前面,逆向遍历就没有元素。 while(lit.hasPrevious()){ String s = (String)lit.previous(); System.out.println(s); } System.out.println("------"); while(lit.hasNext()){ String s = (String)lit.next(); System.out.println(s); } System.out.println("------"); //ListIterator还可以往前找元素。先正向后逆向即可 while(lit.hasPrevious()){ String s = (String)lit.previous(); System.out.println(s); } } }输出:
------ today is yours ------ yours is todayvoid addElement(Object obj):添加功能 ———————————– > 被add()替代 Object elementAt(int index):获取功能 ———————————— > 被get()替代 Enumeration elements():获取功能 ———————————– > 被Iterator iterator()替代 boolean hasMoreElements()— —————————————— > 被hasNext()替代 Object nextElement()—————————————————- > 被next()替代
只需要了解一下。不推荐使用了。
public class VectorDemo { public static void main(String[] args) { Vector<String> v = new Vector<String>(); v.addElement("I"); v.addElement("miss"); v.addElement("you"); //第一种方式 for(int i = 0;i < v.size();i++){ String s = (String)v.elementAt(i); System.out.println(s); } System.out.println("--------"); //第二种方式 //Enumeration是接口 Enumeration<String> en = v.elements(); //返回的是实现类的对象 while(en.hasMoreElements()){ String s = (String)en.nextElement(); System.out.println(s); } } }输出:
I miss you -------- I miss youaddFirst(Object o): addLast(Object o): getFirst(): getLast(): removeFirst(): removeLast():
public class LinkedListDemo { public static void main(String[] args) { LinkedList<String> link = new LinkedList<String>(); link.add("I"); link.add("love"); link.add("you"); link.addFirst("first"); link.addLast("last"); System.out.println("removeFirst方法:" + link.removeFirst()); System.out.println("removeLast方法:" + link.removeLast()); System.out.println(link.getFirst()); System.out.println(link.getLast()); System.out.println(link); } }输出:
removeFirst方法:first removeLast方法:last I you [I, love, you]并发修改异常:ConcurrentModificationException 迭代器遍历集合,集合修改元素的时候会发生这个异常。 因为迭代器是依赖于集合而存在的,集合中新添加了元素,而迭代器却不知道,迭代器获取的还是修改之前的那个集合,所以会报错。这个错叫并发修改异常。 也就是说迭代器遍历元素的时候,集合是不可以修改元素的。
public class ListDemo { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("today"); list.add("is"); list.add("yours"); //迭代器遍历。 Iterator<String> it = list.iterator(); while(it.hasNext()){ String s = (String)it.next(); if("is".equals(s)){ //list.add("haha"); //报错。并发修改异常 } System.out.println(s); } } }解决方法: 1、迭代器遍历元素,迭代器修改元素。Iterator没有添加方法,而它的子方法ListIterator有。 元素是跟在他刚才迭代的元素后面的。 2、集合遍历元素,集合修改元素。
public class ListDemo { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("today"); list.add("is"); list.add("yours"); //迭代器遍历。迭代器添加 ListIterator<String> lit = list.listIterator(); while(lit.hasNext()){ String s = (String)lit.next(); if("is".equals(s)){ lit.add("haha"); } } System.out.println(list); } }输出:[today, is, yours, haha]
public class ListDemo { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("today"); list.add("is"); list.add("yours"); //集合遍历元素,集合修改元素 for(int i = 0;i < list.size();i++){ String s = (String)list.get(i); if("is".equals(s)){ list.add("haha"); } } System.out.println(list); } }输出:[today, is, yours, haha]
Set集合:无序(存储顺序和取出顺序不一致),值不可以重复。但是虽然Set集合的元素无序,但是作为集合来说,他有自己的存储顺序。
为什么HashSet存储字符串的时候,字符串相同的值只存储了一个呢? 可以去看我这篇文章:http://blog.csdn.net/qq_36748278/article/details/77842660
LinkedHashSet:特殊的set集合。底层数据结构由哈希表和链表组成。哈希表保证元素的唯一性,链表保证元素有序(存储和取出顺序是一致的)。
TreeSet:底层是二叉树。并且是红黑树。红黑树是一种自平衡二叉树。 TreeSet:能够对元素按照某种规则进行排序。 1、一种叫做自然排序。根据元素的自然顺序对元素进行排序 2、根据创建set时提供的Comparator进行排序。具体取决于使用的构造方法。
public TreeSet():构造一个新的空 set,该 set 根据其元素的自然顺序进行排序 public TreeSet(Comparator<? super E> comparator):构造一个新的空 TreeSet,它根据指定比较器进行排序。TreeSet是如何保证元素的唯一性和排序的呢? 请看我这篇文章,有源码,解析的很透彻:http://blog.csdn.net/qq_36748278/article/details/77915801#t1
集合的toString方法的作用原理是什么呢?
public static void main(String[] args) { Collection c = new ArrayList(); c.add("I"); c.add("am"); c.add("here"); System.out.println(c); }输出:
[I, am, here]现在我们就有个疑惑了,为什么打印输出c输出的不是地址而是值呢? 出现这种情况,我们应该就会猜想集合c应该是调用了toString()方法,所以才没有输出地址值。我们假设是调用了toString()方法。Collection c = new ArrayList();这是多态的用法,所以调用的也可定时ArrayList的toString方法才对。为了解决我们的疑惑,我觉得看源码是直接的方式。 于是我去ArrayList类中找toString方法,但是没有找到,那怎么办呢?只能去ArrayList类的父类中找啦,于是去它的父类AbstractList类中找,还是没有找到,于是我们就去AbstractList类的父类中找,终于找到了toString()方法。 代码如下:
public String toString() { Iterator<E> it = iterator(); //集合本身调用迭代器方法,得到集合迭代器 if (! it.hasNext()) //如果没有元素,就返回空 return "[]"; StringBuilder sb = new StringBuilder(); sb.append('['); for (;;) { E e = it.next(); //如果有元素,就进行拼接 sb.append(e == this ? "(this Collection)" : e); if (! it.hasNext()) return sb.append(']').toString(); sb.append(',').append(' '); } }1、ArrayList()方法默认的是构造一个初始容量为10的空列表。数据增长:当需要增长时,Vector 默认增长为原来一培,而ArrayList却是原来的一半 。 2、数组求长度用length属性;字符串String求长度用length()方法;集合求长度用size()方法 3、对象数组:数组即可以存储基本数据类型,也可以存储引用数据类型,它存储引用数据类型的时候的数组叫做对象数组。