为什么有了数组还要有集合
数组的缺点:
数组一旦初始化后, 其长度就固定了 数组一旦定义好,其元素也就确定了 我们只能操作其指定的类型的数据 比如:String [] str = new String[3] ; 这就是指定了 这个string类型的数组对大只能是3个长度 然后其数组类型只能存放string的
数组在存储方面的缺点:
> 一旦初始化后其长度就不可以修改 > 数组中提供的方法非诚有限 对于添加,删除, 插入数据都非常不方便, 同时效率不高 >获取数组中实际元素的个数的需求 数组没有现成的属性和方法使用 > 数组存储数据的特定 有序,可重复 对于无序的需求不可以满足
Java 集合可分为 Collection 和 Map 两种体系
Collection接口:单列数据,定义了存取一组对象的方法的集合
List:元素有序、可重复的集合
Set:元素无序、不可重复的集合
Map接口:双列数据,保存具有映射关系“key-value对”的集合
|----Collection : 单列数据定义了存储一组对象的方法集合 |----List 元素有序,可重复的集合 |---- ArrayList, LinkedList,Vector |----Set 元素无需, 不可重复 去重 |---- HashSet, LinkedHashSet, TreeSet |----Map 双列集合,存储的是键值对 (python的字典) |---- HashMap, LinkedHashMap, TreeMap,Hashtable,Properties
eg:
建立 List集合 Collection coll = new ArrayList(); Set集合 Collection coll = new HashSet(); Map集合 Collection coll = new HashMap();
Collection的操作方法
增加
add(Object obj) 添加元素
addAll(Collection c) 将集合c内的元素复制到当前集合
// add(Object obj) 添加元素 c1.add("老王"); c1.add(new String("东三旗")); c1.add(16); System.out.println(c1); // [老王, 东三旗, 16] // addAll(Collection c) 添加集合 Collection c2 = new ArrayList(); c2.add(18); c1.addAll(c2); System.out.println(c1); // [老王, 东三旗, 16, 18] System.out.println(c2); // [18]
size()获取集合内的元素个数
// Collection.size() 获取集合内的元素个数 System.out.println(c2.size()); // 1
clear()清空集合
// clear() 清空集合 c2.clear(); // 清空集合c2 System.out.println(c2); // []
remove(Object obj) 移除集合内的obj元素
// remove(Object c) 移除集合内的元素c c1.remove(18); // 移除c1集合内的元素18
removeAll(Collection c) 移除当前集合和c集合内相同的元素 不影响c集合
//removeAll(Collection c) // 移除当前集合和c集合内相同的元素 不影响c集合 System.out.println("移除交集"); c2.add(17); c2.add("老李"); c1.add(17); System.out.println(c1); // [老王, 东三旗, 16, 17] c1.removeAll(c2); System.out.println(c1); // [老王, 东三旗, 16] System.out.println(c2); // [17, 老李]
isEmpty()判断是否为空
// isEmpty() 判断是否为空 System.out.println(c1.isEmpty()); // false
contains(Object obj) 判断当前集合是否包含obj元素
//contains(Object obj) 判断集合内是否包含obj元素 System.out.println(c1.contains("西二旗")); // false
containsAll(Collection c)比较两个集合内的元素是否相等
// containsAll(Collection c) 比较两个集合内的元素是否全部相等 System.out.println("====="); c1.clear(); c2.clear(); c1.add("containsAll"); c2.add("containsAll"); System.out.println(c1); // [containsAll] System.out.println(c2); // [containsAll] System.out.println(c1.containsAll(c2));
retainAll(Collection C) 当前集合的元素之存放与c集合的交集 ,也就是把当前集合清空后只存放和c集合相同的内容
> retainAll(Collection c) 把两个集合的交集存放在当前集合内 不影响c集合 也就是对当前集合内只存放两个集合的交集 c.retainAll(c1) // 结果就是c集合 只存放与c1集合的交集元素 c1集合的元素不变
> retainAll(Collection c) 把两个集合的交集存放在当前集合内 不影响c集合 也就是对当前集合内只存放两个集合的交集 c1.retainAll(c1) // 结果就是c集合 只存放与c1集合的交集元素 c1集合的元素不变
c1.add(16); c2.add("123"); System.out.println(c1); // [containsAll, 16] System.out.println(c2); // [containsAll, 123] System.out.println(c1.retainAll(c2)); // true System.out.println(c1); // [containsAll] 祛除了本身的元素 只保留和c2集合内相同的元素 System.out.println(c2); // [containsAll, 123]
equals(Collection c)判断两个集合的内容是否相等
// equals() Collection c3 = new ArrayList(); c3.addAll(c1); System.out.println(c1.equals(c2)); //false System.out.println(c1.equals(c3)); // true
集合与数组相互转化:
集合 ---> 数组 : Collection.toArray()
数组 ----> 集合 : Arrays.asList(数组)
// 集合---》 数组 Collection.toArray() // 数组 ---> 集合 Arrays.asList(obj) 将数组obj转化为集合 Object [] obj = c1.toArray(); for (int i = 0; i < obj.length; i++) { System.out.println(obj[i]); } // 数组转化为集合 Arrays.asList() Collection c4 = Arrays.asList(obj); System.out.println(c4.size());
数组转化为集合后不能直接使用集合的操作方法
Exception in thread "main" java.lang.UnsupportedOperationException
在项目中对List进行操作时报错java.lang.UnsupportedOperationException,后来发现操作的List是由数组转换而成的,通过看源码发现问题
原因
调用Arrays.asList()产生的List中add、remove方法时报异常,这是由于Arrays.asList()返回的是Arrays的内部类ArrayList,
而不是java.util.ArrayList。Arrays的内部类ArrayList和java.util.ArrayList都是继承AbstractList,remove、add
等方法在AbstractList中是默认throw UnsupportedOperationException而且不作任何操作。java.util.ArrayList
重写这些方法而Arrays的内部类ArrayList没有重写,所以会抛出异常。
数组调用 Arrays.asList()方法转化为集合后要不能直接使用集合的方法要将其转化为ArrayList放能使用集合的方法
String str [] = new String[3]; Collection cStr = Arrays.asList(str); // 数组---》 集合 Collection arList = new ArrayList(cStr); // 转化为ArrayList方可使用集合的方法 arList.add("1"); arList.add("3"); System.out.println(cStr.size()); System.out.println(cStr.contains("o")); cStr.remove("1"); System.out.println(arList);
iterator迭代器
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小
迭代器主要是为了遍历集合容器存在的, 有人会问我们直接循环集合遍历不就行了吗,但是那样我们要提前知道知道这个容器内的元素的个数和元素内容,而使用了迭代器我们只需要去一个一个去取元素就可以了
不用使用循环也可以取出其内容
用于遍历容器中的每个对象而不用考虑容器中对象的个数,而且保护数据不显示的表现出这样使得对容器的遍历操作与其具体的底层实现相隔离,达到解耦的效果。
遍历集合: 遍历集合需要用到iterator 因为集合相当于一个容器 遍历它要转化为迭代器来取值
eg:
使用iterator需要先将容器转化为迭代器
iterator的方法
hasNext() 判断其是否有下一个值
next() 取出下一个值
remove() 移除当前迭代器内的值
每次使用一个next它的指针就移动到下一位的开始 比如现在集合内有3个元素,转化为迭代器之后 使用一次next取出第一个值 那么再使用next就是从第二位开始的,因为指针已经移动到下一位了,想要让指针归到开始处
就要从新生产一下这个集合的迭代器
Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合 的第一个元素之前。
迭代器执行的流程
集合转化为迭代器调用iterator()方法
Collection.iterator()
Collection coll = new ArrayList(); Iterator iterator = coll.iterator(); // 将coll集合转化为迭代器
每次调用iterator都会生成一个新的迭代器 所以我们在调用iterator的时候要确保使用的额是同一个迭代器
eg
// 死循环,因为每次调用iterator()都是一个新的迭代器 所以下面是两个迭代器 while (coll.iterator().hasNext()){ System.out.println(coll.iterator().next()); }
remove()方法
Collection coll = new ArrayList(); coll.add(new String("老王")); coll.add(17); coll.add("上地"); System.out.println(coll); // [老王, 17, 上地] Iterator iterator = coll.iterator(); // 删除集合内的老王元素 while (iterator.hasNext()){ Object object = iterator.next(); if(object.equals("老王")){ iterator.remove(); // 移除当前迭代器内老王的元素 } } System.out.println(coll); // [17, 上地] 此时看到元素老王就被删除了 iterator = coll.iterator(); // 因为上面的指针已经移动到了最后面所以必须要让指针回到开始处 while (iterator.hasNext()){ System.out.println(iterator.next()); }
List :
list可以理解为动态数组使用它来替换原有的数组
list 和ArrayList
List是一个接口,而ArrayList是List接口的一个实现类。
ArrayList类继承并实现了List接口。
因此,List接口不能被构造,也就是我们说的不能创建实例对象,但是我们可以像下面那样为List接口创建一个指
向自己的对象引用,而ArrayList实现类的实例对象就在这充当了这个指向List接口的对象引用。
eg:
List listOne = new ArrayList();
这个时候可以理解为List是抽象父类 而ArrayList为子类 父类的引用执行子类对象就是多态了
鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据 序号存取容器中的元素。
JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector
* |---- Collection 接口 单列集合 用来存储一个一个的对象
* |----List接口 存储有序的 可重复的数组。“动态数组”替换原有的数组
* |---- ArrayList 作为list接口的主要实现类,线程不安全 效率高,底层使用Object[]elementDate存储
* |---- LinkedList: 对于频繁插入删除操作使用此比ArrayList高,地城使用双向链表
* |---- Vector : 作为list接口的古老实现类 线程安全的效率低,底层使用Object[]elementDate存储
*
*
* 三者异同点:
* 同:三个类都实现了list接口,存储数据的特点相同:存储有序的可重复的数据
* 不同
ArrayList LinkedList Vector 三者的异同 同 : 三个类都实现了List 接口 存储数据的特点相同: 存储有序 可重复 不同 : ArrayList 作为List的主要实现,线程不安全,效率高, 底层使用Object [] 存储 LinkedList 对于频繁的使用插入, 删除操作效率比ArrayList效率高 底层使用双向链表存储 Vector 线程安全 效率低 底层使用Object[] elementData存储 源码分析 // ArrayList() jdk1.7 源码分析 ArrayList list = new ArrayList(10); // 底层创建了长度是10的Object[]数组 elementDate list.add(123); ---> elementData[0] = new Integer(123); ... list.add(); // 如果此次的添加导致底层的elementData数组容量不够,则扩容,默认情况下扩容为原来的1.5倍 同时需要将原有数组中的数据复制到新的数组中 建议开发中使用带参的构造器 :ArrayList list = new ArrayList(int capacity) ArrayList() jdk1.7 源码分析 ArrayList list = new ArrayList(10) elementData 初始化为{}并没有创建底层为10 的数组 list.add(123); 第一次调用add添加操作的时候才创建底层为10 的数组并添加数据 扩容的机制和jdk7的相同 小结: jdk7的ArrayList的对象创建相当于单例模式的饿汉式, jdk8的ArrayList的对象创建相当于单例模式的懒汉式 LinkedList 源码分析 LinkedList list = new LinkedList()内部声明了Node的类型first和last属性,默认值为null list.add(123)// 将123 封装到Node中 创建了Node对象 其中Node就是双向链表的体现
list的方法操作
void add(int index, Object ele):在index位置插入ele元素 boolean addAll(int index, Collection eles):从index位置开始将eles中 的所有元素添加进来 Object get(int index):获取指定index位置的元素 int indexOf(Object obj):返回obj在集合中首次出现的位置 int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index, Object ele):设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
集合:
集合可分为Collection 和Map两个体系
|----Collection : 单列数据定义了存储一组对象的方法集合 |----List 元素有序,可重复的集合 |---- ArrayList, LinkedList,Vector |----Set 元素无需, 不可重复 去重 |----Map 双列集合,存储的是键值对 (python的字典)
Practice:
求
public static void main(String[] args) { List list = new ArrayList(); list.add(1); list.add(2); list.add(3); updateList(list); System.out.println(list); } public static void updateList(List list){ list.remove(2); }
result
[1, 2]
。