集合、数组都是对多个数据进行存储操作的结构,简称Java容器。
说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)
> 一旦初始化以后,其长度就确定了。
> 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。比如:String[] arr;int[] arr1;Object[] arr2;只能存储对于定义的类型元素
> 一旦初始化以后,其长度就不可修改。
> 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
> 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
> 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
>数组是固定长度的;集合可变长度的。
>数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
>数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型(其实集合一般存储的数据也是同一类型的)。
Collection 单列集合,用来存储一个一个的对象
|——- List 接口:元素按进入先后有序保存,可重复(”动态“数组)
|—————- LinkedList 接口实现类, 底层是链表实现的,增和删比较快,查找和修改比较慢,没有同步, 线程不安全
|—————- ArrayList 接口实现类, 底层是数组实现的, 随机访问,查找和修改快,增和删比较慢, 没有同步,线程不安全
|—————- Vector 接口实现类 底层是数组实现的,同步,线程安全
|———————- Stack 是Vector类的实现类
|——- Set接口:存储无序的、不可重复的数据 -->高中讲的“集合”
|—————- HashSet 使用hash表(数组)存储元素
|————————- LinkedHashSet 链表维护元素的插入次序,可以保证存储的元素唯一
|—————-TreeSet 底层是二叉树算法实现,元素排好序
Map接口:双列集合,用来存储一对(key - value)一对的数据 -->高中函数:y = f(x)
|———- Hashtable 接口实现类, 同步, 线程安全
|———- HashMap 接口实现类 ,没有同步, 线程不安全-
|—————–- LinkedHashMap 双向链表和哈希表实现
|—————–- WeakHashMap
|——–- TreeMap 红黑树对所有的key进行排序
|———- IdentifyHashMap
- @Test
- public void test1(){
- Collection coll = new ArrayList();
- coll.add("aaa");
- coll.add(123);//自动装箱
- coll.add(new Date());
- System.out.println(coll);
- }
System.out.println(coll.size());//3
- Collection coll1 = new ArrayList();
- coll1.add("bbb");
- coll.addAll(coll1);
- System.out.println(coll);//[aaa, 123, Fri Jun 10 15:30:17 CST 2022, bbb]
- coll.clear();
- System.out.println(coll);//[]
System.out.println(coll.isEmpty());//true
- Collection coll = new ArrayList();
- coll.add(123);
- coll.add(new String("嘻戏i"));
- coll.add(new Person("嘻戏i",31));
- System.out.println(coll.contains(123));//true
- System.out.println(coll.contains(new String("嘻戏i")));//true
- //我们在判断时会调用obj对象所在类的equals()。Person类未重写equals方法则该处为false,反之为true
- System.out.println(coll.contains(new Person("嘻戏i",31)));//false -->true
- Collection coll1 = Arrays.asList(123,"嘻戏i");
- System.out.println(coll.containsAll(coll1));//true
- coll.remove(123);
- System.out.println(coll);//[嘻戏i, Person{name='嘻戏i', age=31}]
- coll.removeAll(coll1);
- System.out.println(coll);//[Person{name='嘻戏i', age=31}]去除和coll1相同的元素
- Collection coll = new ArrayList();
- coll.add(123);
- coll.add("嘻戏i");
- coll.add(new Person("嘻戏i",31));
- coll.add(false);
-
- Collection coll1 = Arrays.asList(123,"嘻戏i","aaa");
- coll.retainAll(coll1);
- System.out.println(coll);//[123, 嘻戏i]
- System.out.println(coll1);//[123, 嘻戏i, aaa]
- boolean equals = coll.equals(coll1);
- //要想返回true 需要当前集合和形参集合的元素都相同。(因为我们这里使用的是collection子类ArrayList是有序的,所有元素的位置也需要一致)
- System.out.println(equals);
- int i = coll.hashCode();
- System.out.println(i);
- Collection coll = new ArrayList();
- coll.add("嘻戏i");
- coll.add(123);
- coll.add(new Person("嘻戏i",31));
-
- Object[] objects = coll.toArray();
- for (int i = 0; i < objects.length; i++) {
- System.out.println(objects[i]);
- }
- List list = Arrays.asList(new String[]{"aa","bb","cc"});
- System.out.println(list);//[aa, bb, cc]
-
- //基本数据类型的数组转化成集合时,会将整个数组默认一个整体
- List list1 = Arrays.asList(new int[]{111,222,333});
- System.out.println(list1);//[[I@3d82c5f3]
-
- List list2 = Arrays.asList(new Integer[]{111,222,333});
- System.out.println(list2);//[111, 222, 333]
- public void test5() {
- Collection coll = new ArrayList();
- coll.add("嘻戏");
- coll.add(123);
- coll.add(new Person("嘻戏", 28));
-
- Iterator iterator = coll.iterator();//获取迭代器
- while (iterator.hasNext()) { //hasNext():判断是否还有下一个元素
- System.out.println(iterator.next()); //next():①指针下移 ②将下移以后集合位置上的元素返回
- }
- }
- public void test5() {
- Collection coll = new ArrayList();
- coll.add("嘻戏");
- coll.add(123);
- coll.add(new Person("嘻戏", 28));
-
- Iterator iterator = coll.iterator();//获取迭代器
- //方式一:不推荐
- // for (int i = 0; i < coll.size(); i++) {
- // System.out.println(iterator.next());
- // }
- //方式二:推荐
- while (iterator.hasNext()) { //hasNext():判断是否还有下一个元素
- System.out.println(iterator.next()); //next():①指针下移 ②将下移以后集合位置上的元素返回
- }
使用迭代器Iterator的错误方式
下面三种错误方式代码用到的集合
- Collection coll = new ArrayList();
- coll.add("嘻戏");
- coll.add(123);
- coll.add(new Person("嘻戏", 28));
错误方式1: 报异常:NoSuchElementException
- //方式1.1
- Iterator iterator1 = coll.iterator();
- while (iterator1.next() != null){
- System.out.println(iterator1.next());
- }
- //方式1.2
- System.out.println(iterator.next());
- System.out.println(iterator.next());
- //报异常:NoSuchElementException
- System.out.println(iterator.next());
错误方式2 一直无限循环下去
- //集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
- while (coll.iterator().hasNext()){
- System.out.println(coll.iterator().next());
- }
//错误方式3 关于Iterator中的remove()
- //如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
- Iterator iterator2 = coll.iterator();
- while (iterator2.hasNext()){
- // iterator2.remove(); //报异常:IllegalStateException
- //正确操作:删除集合中的new Person("嘻戏",28)
- Object obj = iterator2.next();
- if (new Person("嘻戏",28).equals(obj)){
- iterator2.remove();
- }
- }
- iterator2 = coll.iterator();
- while (iterator2.hasNext()){
- System.out.println(iterator2.next());
- }
- Collection coll = new ArrayList();
- coll.add(123);
- coll.add("嘻戏");
- coll.add(new Person("嘻戏",28));
-
- //for(集合元素的类型 局部变量 : 集合对象)
- //内部仍然调用了迭代器。
- for (Object obj: coll) {
- System.out.println(obj);
- }
-
- System.out.println("*****************");
- //for循环和增加for循环
- String[] arr = new String[]{"MM","MM","MM"};
- for (int i = 0; i < arr.length; i++) {
- // arr[i] = "GG";//改变了字符串arr
- }
-
- for (String s :
- arr) {
- s = "GG";//只改变了局部变量s,未改变字符串arr
- // System.out.println(s);
- }
- for (int i = 0; i < arr.length; i++) {
- System.out.println(arr[i]);
- }
|----Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储有序的、可重复的数据。 -->“动态”数组,替换原有的数组
|----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
|----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
|----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
1 jdk 7情况下
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
list.add(123);//elementData[0] = new Integer(123);
list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
2 jdk 8中ArrayList的变化:
ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
后续的添加和扩容操作与jdk 7 无异。
3 小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象。
其中,Node定义为:体现了LinkedList的双向链表的说法
- private static class Node<E> {
- E item;
- Node<E> next;
- Node<E> prev;
-
- Node(Node<E> prev, E element, Node<E> next) {
- this.item = element;
- this.next = next;
- this.prev = prev;
- }
- }
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。在扩容方面,默认扩容为原来的数组长度的2倍。
ArrayList、LinkedList、Vector三者的异同:
同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
异:通过上面的源代码分析
增:
在index位置插入元素 ——void add(int index, Object ele)
list.add(1,456);
从index位置开始将另外集合中的所有元素添加进来 ——boolean addAll(int index, Collection eles)
- List list1 = Arrays.asList(1,2,3);
- //从list索引2的位置开始将list1中的所有元素添加进来
- list.addAll(2,list1);
- //从list末尾的位置开始将list1中的所有元素当做一个整体添加进来
- list.add(list1);
删:
移除指定索引位置的元素,并返回此元素——Object remove(int index) / boolean remove(Object obj)
- //删除list集合中索引为2的元素,并返回该元素
- Object Obj = list.remove(2);
- System.out.println(Obj);
- //删除list集合中list1集合,有就返回true,没有返回false
- boolean obj1 = list.remove(list1);
- System.out.println(obj1);
改:
设置指定索引位置的元素为其他元素——Object set(int index, Object ele)
- Object obj2 = list.set(3, 33);
- System.out.println(obj2);
查:
获取指定索引位置的元素 ——Object get(int index)
- Object obj3 = list.get(3);
- System.out.println(obj3);
获取集合长度——int size()
int size = list.size();
返回obj在集合中首次出现的位置(集合中不存在查询的元素返回-1)——int indexOf(Object obj)
int i = list.indexOf(123);
返回obj在当前集合中末次出现的位置——int lastIndexOf(Object obj)
int i2 = list.lastIndexOf(123);
返回从fromIndex到toIndex位置的子集合(左闭右开)——List subList(int fromIndex, int toIndex)
List list2 = list.subList(1, 4);
Iterator迭代器方式
- Iterator iterator = list.iterator();
- while (iterator.hasNext()){
- System.out.println(iterator.next());
- }
增强for循环
- for (Object obj : list) {
- System.out.println(obj);
- }
普通for循环(List集合有索引,结合get方法,可以用普通for循环)
- for (int j = 0; j < list.size(); j++) {
- System.out.println(list.get(j));
- }