导图社区 java- Collection
Java基础中的 Collection,相比较于数组的优点在于其能存储引用类型数据,即对象。
编辑于2020-07-27 10:47:20Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程
用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。
平和保存和搜索的一些好用的网站,分享一波,好用拿走。
社区模板帮助中心,点此进入>>
Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程
用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。
平和保存和搜索的一些好用的网站,分享一波,好用拿走。
java集合
1. 集合的概念
Java 集合概述: java 集合类是一种特别的类,可以用来存储数量不等的对象,并可以实现常用的数据结构,如栈、对列等。除此之外还可以用于保存具有映射关系的数组。java 集合就像是一种容器,可以将多个对象(实际是对象的引用)放进容器中。 简言之: 1.集合类是一种用来存储对象的容器, 2.实现数据结构, 3.保存具有映射关系的数组。 在编程时,常常需要存放多个数据,使用数组长度不可变化,而且数组无法保存具有映射关系的数组。为了保存不确定的数组,java 提供了集合类。集合类主要用于保存盛装其他数据,因此集合也被称为容器类。
1.1. 存储对象
1.2. 实现数据结构
1.3. 保存具有映射关系的数组
1.4. 不确定长度的数组
2. 集合与数组的区别
集合与数组的区别: 1.存放内容: 数组--基本数据类型 集合--对象/引用数据类型/具有映射关系的数组/不确定的数组 2.长度(可变性) 数组--不可变 集合--可变 3.操作 数组长度 : a.length; 集合元素长度 a.size();
2.1. 存放内容
2.2. 长度
3. 集合的体系结构
java 集合分为两大体系: Collection 接口:唯一元素(元素间没有关系) Map 接口:对象元素具有映射关系(元素间有关联) java 集合大致分为: Set、List、Queue、Map 四大结构, 其中 Set 代表无序,不可重复的集合; List 代表有序、重复的集合; Map 则代表具有映射关系的集合; java5中增加了Queue体系的集合,代表一种对列集合的实现。 Queue 接口的特点:在两端出入的 List,所以也可以用数组或链表来实现 底层实现:对列。
3.1. Collection接口
Collection 接口概念: Collection 接口是层次结构中的根接口。构成 Collection 的单位,被称之为元素 Collection 接口通常不能直接使用,但该接口提供了添加元素、删除元素、管理数据的方法。由于 List 接口与 Set 接口都继承了 Collection 接口,因此这些方法对 List 集合与 Set 集合是通用的。
3.1.1. List
采用线性列表的存储方式,长度可以动态改变。 元素特点:有序、可重复 List 集合总结: 非并发 写多读少 LinkedList 读多写少 ArrayList 并发 Vector 存在的问题: Java 中遍历 ArrayList 的过程中删除元素操作会发生并发修改异常? 循环遍历的时候,删除元素,首先会定位到元素位置删除,然后复制数组往前移动一位,遍历到最后就会出现 null,报错了
1. LinkedList
底层实现: 底层使用双向循环链表数据结构 同时实现了List接口和Queue接口 数据结构:双向链表 线程安全:不安全 插入和删除:链表存储,所以插入和删除不受元素位置影响,都是近似O(1) 快速随机访问:链表不支持 内存空间占用:空间消耗比 ArrayList 更大,因为每个元素需要更多的空间前指针和后指针及数据 ----------------------------------------- public class DemoTest { public static void main(String args[]) { LinkedList list =new LinkedList(); list.add("1"); list.add("2"); // 将栈顶的元素弹出栈 System.out.println(list.pop()); } }
1.1. 底层数据结构是链表结构
1.2. 查询较慢,增删块
2. ArrayList
Arrays: java.util.Arrays 此类是大小可变的数组,包含用来操作数组的各种方法,比如排序和搜索等。其所有的方法均为静态方法。 ArrayList: java.util.ArrayList 是数组的实现,存储在内的数称为元素。此类提供一些方法来操作内部存储的元素。添加元素,大小自增。 ArrayList 集合不能存储基本数据类型,只能存储引用数据类型,但是可以存储基本类型包装类: Byte、Short、Integer、Long、Float、Double、Character、Boolean ---------------------------------------- 底层实现: 1.默认为10个长度,当容量不够时,ArrayList 增加的长度是当前容量的*1.5+1 2.底层使用数组(数据结构:动态数组) 插入和删除: 插入和删除受元素位置影响,如果是add(e),就是添加到尾部, 时间复杂度为O(1)如果是1,指定位置插入的时间复杂度为O(n-i) 快速随机访问:支持,通过下标get(index)来迅速获取元素对象。 内存空间占用:尾部需要预留一段空间,防止数组越界。
2.1. 特点
2.1.1. 底层数据结构是数组
2.1.2. 查询很快,增删较慢
2.1.3. 线程不同步(不安全)
2.2. 操作
1.操作数组的方法: //返回指定数组内容的字符串表示形式。 public static String toString(int[] a); //数组内容转化为字符串 String s=Arrays.toString(a); 数组输出: arr是二维数组 arr[0] / arr[1] 是一维数组 arr[0][0]是数据 数组长度 : a.length; 集合元素长度 a.size(); //对指定的 int 型数组按数字升序进行排序。 public static void sort(int[] a) Arrays.sort(arr); 2.ArrayList集合操作 ArrayList list = new ArrayList(); 向上转型:List list = new ArrayList(); 2.1.添加元素: list.add(" "); //在指定位置添加元素 arrayList.add(2,2); 2.2.判断元素 判断arrayList是否包含5 System.out.println("ArrayList contains 5 is: " + arrayList.contains(5)); // 清空ArrayList arrayList.clear(); // 判断ArrayList是否为空 System.out.println("ArrayList is empty: " + arrayList.isEmpty()); 3.删除元素 删除指定索引处元素,返回被删除的元素: System.out.println("remove:"+list.remove(1)); // 删除指定位置上的元素 arrayList.remove(2); // 删除指定元素 arrayList.remove((Object)3); 4.返回值 返回指定元素: System.out.println("get:"+list.get(1)); 返回元素个数: System.out.println("size:"+list.size()); 遍历输出: for(int i = 0; i System.out.println(list.get(i)); } 3.ArrayList类型作为参数 ArrayList list=new ArrayList(); list.add(""); //调用方法printArrayList(list); public static void printArrayList(printArrayList list){System.out.println(s)} ArrayList类型作为返回值 return list;//list[ ]
2.2.1. 遍历
//创建,添加数据 List listNames = new ArrayList(); listNames.add("qiuqiu"); listNames.add("kaka"); for 循环: for (int i = 0; i String name = listNames.get(i); System.out.println(name); //System.out.println(listNames.get(i)); } for-each: for (String s : listNames) { System.out.println(s); } 使用Lambda表达式的forEach: listNames.forEach(name -> System.out.println(name)); 迭代方式:迭代器遍历 Iterator itr = listNames.iterator(); while (itr.hasNext()) { String name = itr.next(); System.out.println(name); //System.out.println(itr.next()); }
2.2.2. toArray
第一种方式(最常用) Integer[] integer = arrayList.toArray(new Integer[0]); 第二种方式(容易理解) Integer[] integer1 = new Integer[arrayList.size()]; arrayList.toArray(integer1); // 抛出异常,java不支持向下转型 //Integer[] integer2 = new Integer[arrayList.size()]; //integer2 = arrayList.toArray(); System.out.println();
3. Vector
底层实现: 当容量不够时,Vector默认扩展一倍容量 线程安全:安全 原理:其实就是方法加了synchronized修饰的ArrayList ------------------------------------------- public class DemoTest { public static void main(String args[]) { List vector =new Vector(); vector.add("1"); vector.add(1, "2"); System.out.println(vector.get(1)); } }
3.1. 底层数据结构是数组
3.2. 线程同步(安全)
3.3. 无论查询还是增删都慢
3.1.2. Set
set 集合的特点: 1.元素无序、 2.不允许重复,不会有多个元素引用相同的对象、 3.最多只能包含一个null元素。
3.1.2.1. HashSet
public class DemoTest { public static void main(String args[]) { Set set =new HashSet(); set.add("1"); set.add("2"); set.add("3"); set.add("4"); //HashSet排列是无序的,看打印结果可知 for(String str:set){ System.out.println(str); } } } //其他输出方式 System.out.println(set.next()) Iterator it = set.iterator(); while (set.hasNext()) { System.out.println(set.next()); } --------------------------------------- HashSet 怎么查询重复的数据: 首先计算对象的hashcode然后匹配是否有其他的已经存入的hashcode与之匹配,若没有那么加入,如果有,要么要通过equals来检查hashcode的对象是否真的相同。 -------------------------------------------- HashSet和HashMap的区别: 1.前者实现了set接口,后者是Map接口 前者存储对象,后者是键值对, 2.前者用add新增,后者是put。 3.前者是用hashcode和equals来判断对象是否相等,后面只用hashcode 前者效率慢,后者快。 ----------------------------------------- 底层实现: 1.使用Hash表实现元素的存储 2.底层是由HashMap实现的 当向HashSet存入一个元素时,HashSet会调用该对象的hashCode()方法获取该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置。 **注意:判断元素是否相同的标准是hashCode相同并且equals()方法返回为ture** 以下列举了所有四种插入情况: 01.若元素的hashCode不相同,且equals方法返回为fasle ---》直接根据hashCode值存储; 02.若元素的hashCode相同,但是equals方法返回为fasle ---》存放于之前元素的相同的位置之上的链式结构中,但这样会降低性能; 03.若元素的hashCode不同,但equals方法返回为ture ---》直接根据hashCode值存储; 04.若元素的hashCode相同,且equals方法返回为ture ---》元素插入不成功;
3.1.2.1.1. 使用Hash表实现元素的存储,底层实现基于HashMap实现
3.1.2.1.2. 1.排列无序,不可重复
3.1.2.1.2.1. 2.存储速度快
3.1.2.1.3. 不保证Set的迭代顺序
3.1.2.1.3.1. 不保证数据永久不变
3.1.2.2. LinkedHashSet
public class DemoTest { public static void main(String args[]) { Set set =new LinkedHashSet(); set.add("1"); set.add("2"); set.add("3"); set.add("4"); //LinkedHashSet排列是有序的,看打印结果可知 for(String str:set){ System.out.println(str); } } } ----------------------------------- 底层实现: 1.在HashSet的基础上加入了链式存储 2.采用双向链表记录插入顺序 链表和hash表组成,由链表保证元素的排序。由hash表保证元素的唯一。
3.1.2.2.1. 1.在HashSet的基础上加入了链式存储
3.1.2.2.2. 2.采用双向链表记录插入顺序
3.1.2.2.3. 链表和hash表组成,由链表保证元素的排序。由hash表保证元素的唯一。
3.1.2.3. *TreeSet
public class DemoTest { public static void main(String args[]) { // 必须保证泛型类型已经实现了Comparable接口,不然会引发ClassCastException异常 TreeSet set =new TreeSet(); set.add(3); set.add(1); set.add(4); set.add(2); // 输出集合元素,可以发现集合元素已经处于排序状态 System.out.println(set); // 输出集合里的第一个元素 System.out.println(set.first()); // 输出集合里的最后第一个元素 System.out.println(set.last()); // 输出小于3的集合,不包含3 System.out.println(set.headSet(3)); // 输出大于等于3的集合 System.out.println(set.tailSet(3)); // 输出大于等于1且小于3的集合 System.out.println(set.subSet(1,3)); } } -------------------------------------- 底层实现: 1.是SortedSet接口的实现类 2.排序存储 3.使用二叉树实现:红黑树
3.1.2.3.1. 1.元素排列可定制,不可重复
3.1.2.3.2. 2.可以方便的将元素排序存储,分为自然排序和定制排序
自然排序即已经实现了Compareable接口的类型,排序方式已经定制好了; 定制类型即还未实现Compareable接口,若未继承该接口,那么会引发异常;
3.1.2.3.3. 1.是SortedSet接口的实现类
3.1.2.3.4. 2.排序存储:3.使用二叉树实现:红黑树
3.2. Map 接口
Map 集合: 将键映射到值的对象。 使用键值对存储,Map会维护与Key有关联的值。 两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。 map 集合存储方式: 采用键值对的方式进行存储,长度可以动态改变。
3.2.1. HashMap类
特点: 1.键不可重复,值可以重复 2.只允许一个key为null,value可以为null 3.线程不安全 底层实现: 1.使用HashCode判断存放位置 2.引入负载极限和初始容量 HashMap初始容量是16,默认的负载极限是0.75,如果集合中的元素超过当前集合总容量的75%,hash表会自动成倍的增加容量(桶的数量),并将原有的对象 重新的分配并加入新的桶内,这称为rehash。这个过程是十分消耗性能的。 一般建议设置比较大的初始化容量,防止rehash,但是也不能设置过大,初始化容量过大 浪费空间 数据结构-- JDK1.7 散列(数组+链表) JDK1.8 散列(数组+链表)+红黑树//链表长度大于8就会转为红黑树 初始大小是16,扩容因子是0.75,扩容都是2的幂次方,如果设置的初始值不是2的n次方,hashmap会自动扩充为2的n次方 线程安全:不安全 是否支持NULL键和值:支持 解决hash冲突 拉链法 我们先根据key来获取Hash值,然后通过(数组length-1)和hash取模获取当前数组存放的下标。若是链表上有值,那么追加到尾部,然后取值的时候判断key==和equals 并发闭环问题 扩容方法会新建一个数组,复制原数组到新的数组,由于下标挂着链表,扩容之后会导致环形链表的出现,JDK1.8已经解决了这个问题了。
3.2.1.1. 基于哈希表的接口实现元素存储,利用哈希算法根据hashCode()来配置存储地址
3.2.1.2. 常用操作
HashMap 语 法: //创建对象 Map map = new HashMap(); //添加数据 map.put("key", "value"); //获取数据 map.get("key1"); //删除数据 map.remove(key2);
3.2.1.3. 遍历
1.创建对象 Map map = new HashMap(); 2.给map中添加元素 map.put("张三", "李四"); 3.HashMap的遍历 for-each: 3.1.使用keySet()遍历 for (String key : map.keySet()) { System.out.println(key + " :" + map.get(key)); } 3.2.使用entrySet()遍历 for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey() + " :" + entry.getValue()); } Iterator 迭代器遍历: 1,使用keySet()遍历 Iterator iterator = map.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); System.out.println(key + " :" + map.get(key)); } 2,使用entrySet()遍历 Iterator> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); System.out.println(entry.getKey() + " :" + entry.getValue()); } //遍历删除 Iterator> it = map.entrySet().iterator(); while(it.hasNext()){ Map.Entry entry= it.next(); String key= entry.getKey(); int k = Integer.parseInt(key); if(k%2==1){ System.out.printf("delete key:%s value:%s\r\n", key, entry.getValue()); it.remove(); }}
3.2.2. HashTable
特点: 1.键不可重复,值可重复 2.线程安全 3.key、value都不能是null 底层实现: 使用HashCode判断存放位置; 数据结构:初始大小是11,每次扩容都是2n+1 线程安全:安全//内部方法基本上都经过了synchronized修饰 是否支持NULL键和值:不支持 -------------------------------------------- public class DemoTest { public static void main(String args[]) { Map map =new Hashtable(); map.put("1", "zhangsan"); System.out.println(map.toString()); } }
3.2.3. TreeMap类
特点: 键不可重复,值可重复。 底层实现: 底层二叉树 数据结构:红黑树//一种特殊的AVL树 是否有序:有序 字典顺序来排序【升序】 我们可以通过往对象中构造一个Comparator对象来定制排序 原理 put 1)构建二分搜索树 2)构建红黑树 左旋 右旋 着色 deleteEntry 删除比较特殊,他不是直接刪除,而是找到要刪除节点子节点,然后用子节点来代替删除节点这样就删除子节点即可,简单的说就是把要删除节点和他的左孩子的最右和右孩子的最左交换
3.2.3.1. 基于红黑树(red-black tree)的NavigableMap实现,该映射基于红黑树( Red-Black tree)的 NavigableMap实现,该映射根据其键的自然顺序进行排序,或者根创建射时提供的 Comparator进行排序,具体取决于使用的构适方法
3.2.4. LinkedHashMap
底层实现: 数据结构 继承于HashMap,同时内部增加了一个链表,用来保证元素的顺序 是否有序 基于元素进入集合的顺序或者被访问的先后顺序 与TreeMap的有序性的区别 1)LinkedHashMap是根据元素增加或者访问的先后顺序进行排序 2)TreeMap是基于元素的固有顺序(借助Comparator或者Comparable确定) 3)如果是自然顺序或者自定义顺序的话treemap好,如果需要输出顺序和输入顺序一致的话用LinkedHashMap好
3.2.5. ConcurrentHashMap
底层结构: jdk1.7 数据结构 分段锁 segment,锁的粒度更加精细 分段的数组+链表的形式 原理 put 1)二阶段hash,第一阶段是定位到哪个segment 第二阶段是定位到哪个entry 2)entry中,除了value,还有key,hash和next都是final修饰 意味着不能从中心或者尾部添加或者删除节点,一律添加到头部 3)通过count来统计段内的数据个数,只有新增和删除 会修改这个值,每次都是在上述俩个步骤的最后一步进行修改 get get不需要加锁,采取volatile修饰共享变量 这样每次get的时候都可以获取最新的结构更新 由于遍历不需要加锁的原因是因为next是final。要么 是不为空返回,为空的话就加锁重读 remove 由于是final,所以删除节点的时候会删除某个节点 然后那个节点之上都复制,然后最后一个节点指向 被删节点的下一个节点 resize 扩容只会对段扩容而非整个桶 跟HashMap不同的是,段是先判断 是否需要扩容再put,而hashmap是 先put再判断是否要扩容 size 先尝试不锁住segment的方式来统计segment的大小 如果统计过程中,容器的count发生变化则采取加锁的方式 jdk1.8 取消了分段锁,而是采取了cas和synchronized来保证并发安全, synchronized只锁住当前链表或者红黑二叉树的首节点,只要hash 不冲突,就不会产生并发,效率很高 线程安全 安全 ConcurrentHashMap与Hashtable的区别 底层数据结构: 1.7分段数组+链表 1.8散列红黑树,而hashtable一直是散列 实现线程安全的方式【重要】 ConcurrentHashMap 1.7分段锁对整个桶进行切割,增加锁的粒度如果不是访问同一个segment,就不存在竞争 1.8Node数组+链表+红黑树,并发控制使用CAS与Synchronized HashTable 使用synchronized来保证,效率低下,多线程访问同一个同步方法会导致阻塞或者轮询,竞争会愈发激烈
3.2.6. SortedMap接口
3.2.6.1. 进一步提供关于键的总体排序的Map
3.3. Iterator接口
3.3.1. 迭代器是一种设计模式,它是一个对象,它可以遍历(循环)并选择序列中的对象,而开发人员不需要了解该序列的底层结构
3.3.2. 常用方法
3.3.2.1. 集合.hasNext()
3.3.2.2. 集合.next()
3.3.2.3. 集合.remove()
4. Collection常用方法
4.1. Boolean add(E e)
4.1.1. 在集合末尾添加元素
4.2. boolean addAll(collection c)
4.2.1. 集合复制
4.3. boolean remove(Object o)
4.3.1. 删除集合中的o元素
4.4. Void clear()
4.4.1. 清空集合
4.5. Boolean contains(object o)
4.5.1. 判断元素o是否存在
4.6. boolean isEmpty()
4.6.1. 判断集合是否为空
4.7. int size()
4.7.1. 返回集合中元素个数
4.8. Object[] toArray()
4.8.1. 返回一个数组,类型为Object[]
4.9. Iterator iterator()
4.9.1. 迭代器,集合的专用遍历方式
5. Map主要方法
5.1. void Clear()
5.1.1. 清空Map集合
5.2. boolean containsKey(Object key)
5.2.1. 查询Key,有则返回true
5.3. boolean containsValue(Object Value)
5.3.1. 查询Value,有则返回true
5.4. Set entrySet()
5.4.1. 返回Map中所包含的键值对所组成的set集合,每个集合元素都是Map.Entry对象(Entry是Map内部类)
5.5. Object get(Object Key)
5.5.1. 返回key对用value,没有返回null
5.6. Boolean isEmpty()
5.6.1. 判空,若为空,返回true
5.7. Set KeySet()
5.7.1. 返回Map中所包含的key所组成的set集合
5.8. Object put(Object key,Object value)
5.8.1. 添加一个键值对,若key相同,覆盖旧的
5.9. void putAll(Map m)
5.9.1. 复制Map集合
5.10. Object remove(Object key)
5.10.1. 删除key对应value,若key不存在返回null
5.11. is size()
5.11.1. 返回键值对个数
5.12. Collection calues()
5.12.1. 返回该Map里所有value组成的collection
5.13. 内部类Entry
5.13.1. Object getKey()
5.13.1.1. 返回该Entry里的Key
5.13.2. Object getValue()
5.13.2.1. 返回该Entry里的Value
5.13.3. Object setValue(V value)
5.13.3.1. 设置该Entry里的value并返回
6. Collections工具类
6.1. 排序操作
public class DemoTest { public static void main(String args[]) { List list=new ArrayList(); list.add("1"); list.add("2"); list.add("3"); // 反转指定List集合中元素的顺序 Collections.reverse(list); System.out.println(list); // 对List集合进行随机排序 Collections.shuffle(list); System.out.println(list); // 根据元素的自然顺序对指定的List集合进行排序 Collections.sort(list); System.out.println(list); // 将指定的List集合中的i处元素和j处的元素进行交换 Collections.swap(list, 1, 2); System.out.println(list); } }
6.2. 查找替换操作
public class DemoTest { public static void main(String args[]) { 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)); // 返回指定集合中指定元素的出现次数 System.out.println(Collections.frequency(list, "1")); // 将集合中的"3"元素全部替换为"1" Collections.replaceAll(list, "3", "1"); System.out.println(list); } }
6.3. 同步控制
public class DemoTest { public static void main(String args[]) { // 下面程序创建了4个线程安全的集合对象 List list=Collections.synchronizedList(new ArrayList()); Set set=Collections.synchronizedSet(new HashSet()); Map map=Collections.synchronizedMap(new HashMap()); } }
7. 如何选用集合
主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用 Map 接口下的集合, 需要排序时选择 TreeMap , 不需要排序时就选择 HashMap, 需要保证线程安全就选用 ConcurrentHashMap. 当我们只需要存放元素值时,就选择实现 Collection 接口的集合, 需要保证元素唯一时选择实现 Set 接口的集合比如 TreeSet 或 HashSet, 不需要就选择实现 List 接口的比如 ArrayList 或 LinkedList,然后再根据实现这些接口的集合的特点来选用。