常用java集合类详解
- 格式:doc
- 大小:69.50 KB
- 文档页数:6
java中集合的概念Java中的集合是一种非常重要的数据结构,用于存储和操作一组对象。
集合框架包含了许多类和接口,可以方便地进行数据的存储、查询、排序等操作,使得Java程序开发变得更加高效和便捷。
在本文中,我们将逐步介绍Java中集合的概念和用法。
一、集合框架概述Java中的集合框架是一个包含了多个接口和类的层次结构,用于表示和操作一组对象。
集合框架包含了通用的集合接口和实现,以及特定的集合类和接口,如List、Set、Map等。
集合框架的接口和类都是通过泛型实现的,可以存储任意类型的对象,比如基本类型和自定义类型的对象。
二、集合框架的接口Java中的集合框架包含了多个接口,其中包括:1. Collection:代表一组对象的集合,是其他集合接口的父接口。
它定义了一些通用的方法,如添加、删除、迭代等。
2. List:代表有序的集合,其中每个元素都有一个对应的索引。
List允许重复元素出现,并且可以通过索引访问、添加、删除元素。
3. Set:代表无序的集合,其中每个元素都是唯一的。
Set不允许重复的元素出现,可以用来去重。
4. Map:代表一组键值对的集合,其中每个键都是唯一的。
Map 允许多个值对应同一个键,可以用来快速查找和存储数据。
三、集合类的实现Java中的集合类可以通过实现集合接口来实现。
如ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等都是集合类的实现,我们可以通过这些集合类来方便地实现对一组对象的操作。
例如:1. 使用ArrayList来实现List接口,可以进行元素的添加、删除、查询等操作:List<String> list = new ArrayList<>();list.add("Alice");list.add("Bob");System.out.println(list.get(1));2. 使用HashSet来实现Set接口,可以去重并存储元素:Set<Integer> set = new HashSet<>();set.add(1);set.add(2);set.add(2);System.out.println(set.size());3. 使用HashMap来实现Map接口,可以快速查找并存储数据:Map<String, Integer> hashMap = new HashMap<>(); hashMap.put("Alice", 12);hashMap.put("Bob", 18);System.out.println(hashMap.get("Bob"));四、集合的迭代Java中的集合类都实现了Iterable接口,因此可以使用迭代器来访问集合中的元素。
HashMap工作原理与扩容机制详解HashMap是Java中常用的集合类之一,它实现了基于键值对的存储结构,是一种哈希表。
在本文中,我们将深入探讨HashMap的工作原理以及扩容机制。
HashMap的工作原理HashMap内部实现了一个数组,称为哈希表,存储键值对。
当我们向HashMap中放入键值对时,首先会将键通过哈希函数转换为一个哈希码,然后根据哈希码计算出该键值对在数组中的位置(也称为桶)。
因此,在HashMap中查找或插入元素的时间复杂度为O(1)。
如果多个键的哈希码相同,这就发生了哈希冲突。
为了解决哈希冲突,HashMap使用了链地址法,即将相同哈希码的键值对存储在同一个桶中的链表中。
当碰撞的次数较多时,链表可能会转换为红黑树,以提高查找效率。
HashMap的扩容机制HashMap在插入元素时,会根据当前元素的数量来判断是否需要进行扩容。
默认情况下,当HashMap中元素数量超过容量的75%时,就会触发扩容操作。
HashMap的扩容机制会使HashMap的容量变为原来的两倍,并将已有的键值对重新计算哈希码并放入新的桶中。
这样就可以减少碰撞的概率,提高HashMap的性能。
扩容是一个相对耗时的操作,因为需要重新计算哈希码、重新分配桶等,所以在开发中应当合理设置HashMap的初始容量和负载因子,以尽量减少扩容次数。
总结HashMap是Java中使用广泛的数据结构之一,其通过哈希表实现了高效的键值对存储和查找操作。
HashMap的工作原理基于哈希码和桶的概念,通过哈希函数将键映射到桶中。
当元素数量达到一定阈值时,HashMap会触发扩容操作,将容量变为原来的两倍,以减少哈希冲突,提高性能。
希望通过本文的介绍,您对HashMap的工作原理和扩容机制有了更深入的了解。
java中集合知识点总结1. Collection接口Collection接口是Java中集合类的基本接口,它定义了一组通用的操作方法,包括添加、删除、查找等操作。
Collection接口有三个主要的子接口:List、Set和Queue。
(1) ListList是一种有序的集合,它允许重复的元素,并且可以按照索引访问元素。
List接口中有常用的实现类:ArrayList、LinkedList和Vector。
其中,ArrayList是基于数组实现的,它支持快速的随机访问和插入操作;LinkedList是基于双向链表实现的,它支持快速的插入和删除操作;Vector是线程安全的类,它支持并发访问。
(2) SetSet是一种不允许重复元素的集合,它用来存储唯一的元素。
Set接口中有常用的实现类:HashSet、LinkedHashSet和TreeSet。
其中,HashSet是基于哈希表实现的,它提供了快速的查找和插入操作;LinkedHashSet是基于哈希表和链表实现的,它保持了元素的插入顺序;TreeSet是基于红黑树实现的,它提供了有序的集合。
(3) QueueQueue是一种先进先出的集合,它用来存储元素,并且支持插入和删除操作。
Queue接口中有常用的实现类:LinkedList和PriorityQueue。
其中,LinkedList可以作为Queue来使用,它支持快速的插入和删除操作;PriorityQueue是基于堆实现的,它提供了优先级队列的功能。
2. Map接口Map接口是Java中的映射表,它用来存储键值对的数据。
Map接口中有常用的实现类:HashMap、LinkedHashMap、TreeMap和Hashtable。
其中,HashMap是基于哈希表实现的,它提供了快速的查找和插入操作;LinkedHashMap是基于哈希表和链表实现的,它保持了键值对的插入顺序;TreeMap是基于红黑树实现的,它提供了有序的映射表;Hashtable是线程安全的类,它支持并发访问。
java集合的写法Java集合是Java语言中重要的一部分,用于存储和操作数据集合。
在Java中有许多不同的集合类,包括List、Set、Map等。
这些集合类都实现了Java集合框架中的接口。
使用Java集合可以实现高效方便的数据操作。
下面将介绍集合的写法及使用。
1. List集合List是一个有序的集合,可以存储重复的元素。
常用的实现类有ArrayList和LinkedList。
ArrayList的写法:```List<String> list = new ArrayList<>();list.add("apple");list.add("banana");list.add("orange");list.set(0, "pear");list.remove(2);System.out.println(list.get(1));```LinkedList的写法:```List<String> list = new LinkedList<>();list.add("apple");list.add("banana");list.add("orange");list.set(0, "pear");list.remove(2);System.out.println(list.get(1));```2. Set集合Set是一个无序的集合,不允许存储重复的元素。
常用的实现类有HashSet和TreeSet。
HashSet的写法:Set<String> set = new HashSet<>();set.add("apple");set.add("banana");set.add("orange");set.add("apple");System.out.println(set.size());```TreeSet的写法:```Set<Integer> set = new TreeSet<>();set.add(3);set.add(2);set.add(1);System.out.println(set.size());```3. Map集合Map是一种键值对的集合,可以根据键来获取值。
Java中常用的集合类有哪些?它们的使用场景是什么?Java作为目前最为流行的编程语言之一,其优越的面向对象编程思想和强大的类库使其成为了广大编程爱好者和专业开发者的首选语言之一。
在Java开发中,常用的集合类具有广泛的应用场景,可以大大简化我们代码的编写和维护。
在本篇文章中,我们将介绍Java中常用的集合类有哪些,它们的使用场景是什么,以及如何选择合适的集合类来应对各种场景。
一、Java中常用的集合类Java中常用的集合类包括List、Set、Map等,具体如下:1.ListList是Java中最基础和最常用的集合类之一,它是一个有序的集合,可以存储重复的元素。
List提供了一系列的方法用来操作列表中的元素,如添加、删除、获取、修改等。
常见的List有ArrayList 和LinkedList。
2.SetSet是Java中的另一个基础集合类,它是一个无序的集合,不允许存储重复的元素。
Set提供了一系列的方法用来操作集合中的元素,如添加、删除、获取等。
常见的Set有HashSet、TreeSet。
3.MapMap是Java中常用的映射关系集合,它存储键值对,支持通过键来访问值。
Map提供了一系列的方法用来操作映射关系,如添加、删除、获取、修改等。
常见的Map有HashMap、TreeMap、ConcurrentHashMap等。
二、Java中常用集合类的使用场景不同的集合类有不同的使用场景,我们需要根据具体的业务需求来选择合适的集合类。
下面我们来介绍几种常见的使用场景及其对应的集合类。
1.需要随机访问元素的情况:ArrayListArrayList是Java中常用的集合类之一,它支持随机访问,通过索引访问元素的时间复杂度为O(1),是处理元素数量较大的情况下的较好选择。
2.需要频繁插入或删除元素的情况:LinkedListLinkedList是另一个常用的集合类,它支持快速的插入和删除操作,通过节点互相关联实现。
java中的常⽤集合类整理⽬录Collection接⼝集合的遍历:iterator 接⼝集合的遍历:增强for循环List接⼝ArrayListSet接⼝Map接⼝HashMapLinkedHashMapTreeMapPropertiesCollections⼯具类总结集合、数组都是对多个数据进⾏存储操作(主要是内存层⾯存储)的结构,简称Java容器。
数组的特点1.数组初始化以后,长度确定不可变2.数组定义好,其元素的类型确定不可变(可能有多态性)3.数组中提供的⽅法有限,对于添加、删除、插⼊数据等操作不⽅便。
4.获取数组中实际元素的个数是没有办法的。
5.数组存储数据的特点是有序、可重复的。
Java集合可分为Collection和Map两种体系,集合存储的优点是解决数组存储数据⽅⾯的弊端。
Collection接⼝:单列数据,⽤来存储⼀个⼀个的对象List接⼝:元素有序,可重复的集合 --> '动态'数组Set接⼝ :元素⽆序、不可重复的集合Map接⼝:双列数据,保存有映射关系(键值对)的集合Collection接⼝向collection接⼝的实现类的对象中添加数据obj时,要求obj所在类要重写equals⽅法。
Abstract Methodsadd(Object e):将元素e添加到集合中size():获取添加的元素个数addAll(Collection coll):将形参coll集合的元素添加到当前集合中clear():清空集合元素,集合仍然存在,只是集合⾥没有元素isEmpty():判断当前集合是否为空contains(Object obj):判断当前集合中是否包含obj,是否包含是通过调⽤obj的equals判断containsAll(Collection coll):判断形参coll中的所有元素是否都存在当前集合中。
remove(Object obj):移除某个元素,同样通过equals寻找移除的元素removeAll(Collection coll):从当前集合中移除coll集合中所有的元素,需要调⽤equals函数retainAll(Collection coll):求两个集合的交集,结果为修改当前集合后的集合。
java常⽤集合使⽤⽅法1.初步尝试java中的集合使⽤⽅式:public static void main( String [] args ){//List 对象User user=User.builder().id(0).name("huhua"+0).build();//a.最常见Add的初始化⽅式List<User> users=new ArrayList<User>();for (int i=0;i<100;i++){users.add(User.builder().id(i).name("huhua"+i).build());}//b.使⽤双花括号在初始化的同时赋值List<User> users2= new ArrayList<User>() {//这个⼤括号就相当于我们 new 接⼝{//这个⼤括号就是构造代码块会在构造函数前调⽤this.add(new User(0,"huahua0"));this.add(new User(1,"huahua1"));}};//c.利⽤Lists⼯具类 https:///google-guava///c1. Lists 提供了两个⽅法:⼀个是创建⼀个空列表;。
List<String> list1 = Lists.newArrayList();list1.add("str1");list1.add("str2");list1.add("str3");//c2.⼀个是创建空列表的同时遍历迭代器,将它的值添加到列表中List<String> list2 = Lists.newArrayList(list1.iterator());//d. 利⽤Arrays⼯具类List<String> arrList= Arrays.asList( new String[]{"huahu0","huahau1","huahua2"});//e.Collections 还提供了⼀个为 List ⼀次性添加所有元素的⽅法,弥补了原先 List 只能添加 Collections,⽽不⽀持数组的缺憾。
集合面试题java1. 什么是集合?集合是Java中的一个常用数据结构,用来存储一组有序或无序的数据。
它是由一组固定大小的元素构成,可以添加、删除和访问其中的元素。
Java中的集合类被包含在java.util包中。
2. Java中有哪些类型的集合?Java中有三种类型的集合,分别是List、Set和Map。
- List:可以存储有序的元素,可以包含重复的元素。
常见的List类有ArrayList和LinkedList。
- Set:主要用于存放无序、不可重复的元素,不存在索引。
常见的Set类有HashSet和TreeSet。
- Map:用于存储键值对,每个存储的元素包含两部分,即键和值。
常见的Map类有HashMap和TreeMap。
3. ArrayList和LinkedList的区别是什么?ArrayList是基于数组实现的集合,具有一定的随机访问性能,可以快速获取指定位置的元素。
但是它在插入和删除元素时效率相对较低,需要重新排列数组。
而LinkedList是基于链表实现的集合,插入和删除元素时速度比ArrayList要快,但是获取指定位置的元素需要遍历整个链表,效率相对较低。
4. HashSet和TreeSet的区别是什么?HashSet是基于哈希表实现的集合,不保证元素的顺序,具有快速的添加、删除和查找元素的能力。
但是它不支持对元素的有序遍历。
而TreeSet是基于红黑树实现的集合,对元素进行自然排序或者指定排序规则进行排序,可以通过迭代器对元素进行有序的遍历。
5. 什么是迭代器?迭代器是一种遍历集合元素的方式,用于逐个访问集合元素。
它是Java中的一个接口,包含常用的方法,如hasNext()和next()。
Iterator 接口是Java中访问集合元素的标准方式,可以访问所有实现了java.util.Collection接口的集合类。
6. 如何遍历一个集合?Java中提供了三种遍历集合的方式,分别是for-each循环、迭代器和Java 8中的Lambda表达式。
Java集合排序及java集合类详解(Collection, List, Set, Map)摘要内容Java里面最重要,最常用也就是集合一部分了。
能够用好集合和理解好集合对于做Java程序的开发拥有无比的好处。
本文详细解释了关于Java中的集合是如何实现的,以及他们的实现原理。
关键字:Collection , List ,Set , Map , 集合,框架。
目录1 集合框架 (2)1.1 集合框架概述 (2)1.1.1 容器简介 (2)1.1.2 容器的分类 (4)1.2 Collection (6)1.2.1 常用方法 (6)1.2.2 迭代器 (8)1.3 List (10)1.3.1 概述 (10)1.3.2 常用方法 (11)1.3.3 实现原理 (15)1.4 Map (18)1.4.1 概述 (18)1.4.2 常用方法 (18)1.4.3 Comparable 接口 (23)1.4.4 实现原理 (25)1.4.5 覆写hashCode() (29)1.5 Set (33)1.5.1 概述 (33)1.5.2 常用方法 (34)1.5.3 实现原理 (38)1.6 总结:集合框架中常用类比较 (39)2 练习 (40)3 附录:排序 (41)1集合框架1.1集合框架概述1.1.1容器简介到目前为止,我们已经学习了如何创建多个不同的对象,定义了这些对象以后,我们就可以利用它们来做一些有意义的事情。
举例来说,假设要存储许多雇员,不同的雇员的区别仅在于雇员的身份证号。
我们可以通过身份证号来顺序存储每个雇员,但是在内存中实现呢?是不是要准备足够的内存来存储1000个雇员,然后再将这些雇员逐一插入?如果已经插入了500条记录,这时需要插入一个身份证号较低的新雇员,该怎么办呢?是在内存中将500条记录全部下移后,再从开头插入新的记录? 还是创建一个映射来记住每个对象的位置?当决定如何存储对象的集合时,必须考虑如下问题。
【Java必修课】各种集合类的合并(数组、List、Set、Map)1 介绍集合类可谓是学习必知、编程必⽤、⾯试必会的,⽽且集合的操作⼗分重要;本⽂主要讲解如何合并集合类,如合并两个数组,合并两个List等。
通过例⼦讲解⼏种不同的⽅法,有JDK原⽣的⽅法,还有使⽤第三库的⽅法。
2 第三⽅库引⼊⼗分常⽤的优秀的第三⽅库Guava和Apache Commons;通过配置pom.xml如下:<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>28.1-jre</version></dependency><dependency><groupId>mons</groupId><artifactId>commons-collections4</artifactId><version>4.4</version></dependency><dependency><groupId>mons</groupId><artifactId>commons-exec</artifactId><version>1.3</version></dependency><dependency><groupId>mons</groupId><artifactId>commons-lang3</artifactId><version>3.5</version></dependency>最新版本可以去官⽹搜索查看。
详解Java中list,set,map的遍历与增强for循环详解Java中list,set,map的遍历与增强for循环Java集合类可分为三⼤块,分别是从Collection接⼝延伸出的List、Set和以键值对形式作存储的Map类型集合。
关于增强for循环,需要注意的是,使⽤增强for循环⽆法访问数组下标值,对于集合的遍历其内部采⽤的也是Iterator的相关⽅法。
如果只做简单遍历读取,增强for循环确实减轻不少的代码量。
集合概念:1.作⽤:⽤于存放对象2.相当于⼀个容器,⾥⾯包含着⼀组对象,其中的每个对象作为集合的⼀个元素出现3.java的容器有集合类和数组,不同之处是区别及其常⽤实现类List接⼝:列表有序元素可重复实现类:ArrayList:动态数组列表LinkedList:双向链表Set接⼝:集⽆序,元素不可重复实现类:HashSet:散列集TreeSet:树集内部排序Map接⼝:以键值对的⽅式存储数据数据-键不允许重复实现类:HashSet:散列集TreeSet:树集内部排序JDK1.0出现的集合类都是线程安全的,但效率低JDK1.2出现的集合类都不是线程安全的,但效率⾼代码⽰例如下:import java.util.ArrayList;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Set;public class ListAndSet{public static void main(String[] args) {setTest();listTest();}// 遍历Set集合private static void setTest() {Set<string> set = new HashSet<string>();set.add("A");set.add("B");set.add("C");set.add("D");set.add("E");//set集合遍历⽅法1:使⽤iteratorIterator<string> it = set.iterator();while (it.hasNext()) {String value = it.next();System.out.println(value);}//set集合遍历⽅法2:使⽤增强for循环。
Java8常⽤的集合操作Java8常⽤的集合操作说明 objList:List的集合,StudentInfo:学⽣的实体类,strList:List集合类1、选出更新时间最新的记录的IdLong id = byCrmChance.stream().max(paring(CrmChanceAudit::getUpdateTime)).orElse(new CrmChanceAudit()).getId();2、使⽤指定的符号来连接集合中的元素String resultStr = String.join(",", strList)3、按照年龄升序排序objList = objList.stream().sorted(paring(StudentInfo::getAge)).collect(Collectors.toList());4、按照年龄降序排序objList = objList.stream().sorted(paring(StudentInfo::getAge).reversed()).collect(Collectors.toList());5、按下标遍历数组元素Stream.iterate(0, i -> i + 1).limit(objList.size()).forEach(i -> {System.out.println(objList.get(i));});6、获取最新的数据StudentInfo s = objList.stream().max(paringLong(a -> a.getCreateTime().getTime())).orElse(null);7、List转MapMap<Long, StudentInfo> map = objList.stream().collect(Collectors.toMap(StudentInfo::getId, Function.identity(), (key1, key2) -> key2)) 8、List对某个属性进⾏分组Map<Long, List<StudentInfo>> map = objList.stream().collect(Collectors.groupingBy(StudentInfo::getGrade));9、对list进⾏先分组再对Map中的value进⾏类型转换Map<Long, List<Long>> map = serviceType.stream().collect(Collectors.groupingBy(EnterpriseModel::getId)).entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, v ->v.getValue().stream().map(EnterpriseModel::getServiceTypeId).collect(Collectors.toList())));10、去除List中的null元素list.removeAll(Collections.singleton(null));。
Java中List集合的常⽤⽅法详解⽬录1、voidadd(intindex, Eelement)2、booleanaddAll(intindex, Collection<?extends E>c)3、E get(intindex)4、intindexOf(Objecto)5、ListIterator<E>listIterator()6、ListIterator<E>listIterator(intindex)7、E remove(intindex)8、E set(intindex, Eelement)9、List<E>subList(intfromIndex,inttoIndex)总结List接⼝是继承Collection接⼝,所以Collection集合中有的⽅法,List集合也继承过来。
这篇⽂章就不讲继承Collection接⼝的那些⽅法了1、void add(intindex, Eelement)在指定位置插⼊元素,后⾯的元素都往后移⼀个元素。
2、boolean addAll(intindex, Collection<? extends E>c)在指定的位置中插⼊c集合全部的元素,如果集合发⽣改变,则返回true,否则返回false。
意思就是当插⼊的集合c没有元素,那么就返回false,如果集合c有元素,插⼊成功,那么就返回true。
3、E get(intindex)返回list集合中指定索引位置的元素4、int indexOf(Objecto)返回list集合中第⼀次出现o对象的索引位置,如果list集合中没有o对象,那么就返回-15、ListIterator<E> listIterator()返回此列表元素的列表迭代器(按适当顺序)。
listIterator和iterator的区别是什么呢?下⾯这篇博客⽐较详细的分析,可以参考。
java常⽤集合类型集合Java集合集合框架是为表⽰和操作集合⽽规定的⼀种统⼀的标准体系结构接⼝:表⽰集合的抽象数据类型实现:集合框架中接⼝的具体实现算法:在实现了某个集合框架中的接⼝的对象⾝上完成某种有⽤的计算⽅法Java集合框架的三⼤类接⼝Collection包含:List和Set;三⼤接⼝为:List、Set、Map共同点:都是集合接⼝,实现存储多个对象接⼝定义Collection接⼝存储⼀组不唯⼀(允许重复)、⽆序的对象Set接⼝继承Collection接⼝,存储⼀组唯⼀(你允许重复)、⽆序的对象List接⼝继承Collection接⼝,存储⼀组不唯⼀,有序的对象Mao接⼝存储⼀组成对的键-值对象,提供key(键)到值(value)的映射Iterator接⼝负责定义访问和遍历元素的接⼝List接⼝实现List接⼝常⽤类ArrayList和linkedList。
他们都可以存储所有类型对象。
包括null,允许重复。
并且都是有序排列ArrayList的有点便于遍历元素和随机访问元素的效率⾼LinkedList才有链表存储⽅式,优点在于插⼊、删除元素时效率⾼提供addFist()、addLast()、removeFist()、removeLast()等⽅法,可以在头部尾部插⼊或删除元素List接⼝常⽤⽅法⽅法名称说明Boolean add(object 0)在列表末尾顺序添加元素,起始索引位置为0在指定索引位置添加元素,原索引位置以及后⾯的元素依次后移Void add(int i,object 0)注意:新添加元素索引必须结余0和列表元素个数之间Int size()返回列表中的元素个数返回指定索引位置处的元素Object get(int i)注意:使⽤前必须强转Boolean contains(objecto)判断列表中是够存在指定元素Boolean remove(object o)从列表中删除元素Object remove(int i)从列表中删除指定位置元素,起始索引位置从0开始LinkedList的特殊⽅法⽅法名称说明Void addFirst()在列表的⾸部添加元素Void addLast()在列表的末尾添加元素Object getFirst()返回列表第⼀元素Object getLast()返回列表最后⼀个元素Object removeFirst()删除并返回列表中的第⼀个元素Object removeFirst()删除并返回列表中的第⼀个元素Map接⼝Map常⽤⽅法⽅法名称说明Object put(object key,objectvalue)以“键-值对”的⽅式存储注意:键必须的唯⼀的,值可以重复如果键重复了后加⼊的“键-值队”覆盖原来的“键-值对”Object get(object key)根据键返回相关联的值,如不存在,返回nullObject remove(object key)删除指定的键映射的“键-值对”Int size()返回元素个数Set keyset()返回键的集合Collection values()返回值的集合Boolean containsKey(objectkey)若存在指定的键映射的“键-值对”,返回true Boolean isEmpty()若不存在键-值映射关系,则返回trueVoid clear()从此映射中移除所有映射关系Iterator迭代器专门实现集合的遍历Collection接⼝的intertor()⽅法返回⼀个Iterator,然后通过Iterator接⼝的两个⽅法即可实现遍历Boolean hasNext():判断是否存在另⼀个可访问的元素Object next():返回访问的下⼀个元素。
import java.util.*;public class Testset {public static void main(String args[]){ Set set = new HashSet();set.add("aaa");set.add("bbb");set.add("aaa");set.add("bbb");set.add("aaa");set.add("bbb");set.add("aaa");set.add("bbb");set.add("aaa");set.add("bbb");Iterator ite=set.iterator();set.size();while(ite.hasNext())System.out.println("------"+ite.next()); }}在使用Java的时候,我们都会遇到使用集合(Collection)的时候,但是Java API提供了多种集合的实现,我在使用和面试的时候频频遇到这样的“抉择” 。
:)(主要还是面试的时候)久而久之,也就有了一点点的心得体会,写出来以供大家讨论。
总的说来,Java API中所用的集合类,都是实现了Collection接口,他的一个类继承结构如下:Collection<--List<--VectorCollection<--List<--ArrayListCollection<--List<--LinkedListCollection<--Set<--HashSetCollection<--Set<--HashSet<--LinkedHashSetCollection<--Set<--SortedSet<--TreeSetVector : 基于Array的List,其实就是封装了Array所不具备的一些功能方便我们使用,它不可能走入Array的限制。
java list和set使用场景Java中的List和Set是两种常用的集合类型,它们各自有着不同的使用场景和特点。
本文将分别介绍List和Set的使用场景,并对其进行详细解析。
一、List的使用场景List是一种有序的集合,可以存储重复的元素。
List的使用场景如下:1. 数据存储:List可以用来存储一组有序的数据,比如存储学生成绩、员工工资等。
List中的元素可以按照插入的顺序进行访问,也可以根据索引进行随机访问。
2. 数据检索:List提供了丰富的方法来检索、访问和修改集合中的元素。
可以根据索引、元素值或自定义条件来进行检索。
3. 数据排序:List可以对集合中的元素进行排序,通过调用Collections.sort(List)方法可以对List进行升序排序。
4. 数据操作:List提供了一系列方法来操作集合中的元素,比如添加元素、删除元素、替换元素等。
5. 数据重复:List允许存储重复的元素,因此适用于需要存储重复数据的场景。
二、Set的使用场景Set是一种不允许存储重复元素的集合,它保证了集合中的元素是唯一的。
Set的使用场景如下:1. 数据去重:Set可以用来去除重复的数据,比如从一个数组或List中去除重复元素。
2. 数据筛选:Set可以用来筛选出满足特定条件的元素,比如筛选出一组不重复的IP地址或手机号码。
3. 数据集合:Set可以用来将多个集合合并成一个集合,并且保证合并后的集合中没有重复的元素。
4. 数据判断:Set提供了判断元素是否存在于集合中的方法,可以快速判断一个元素是否在集合中。
5. 数据去序:Set中的元素是无序的,因此适用于不关心元素顺序的场景。
三、List和Set的区别List和Set在使用上有一些区别,主要体现在以下几个方面:1. 元素顺序:List是有序的,可以根据元素的插入顺序进行访问;而Set是无序的,不保证元素的顺序。
2. 元素重复:List允许存储重复的元素;而Set不允许存储重复的元素,保证集合中的元素是唯一的。
常用java集合类详解在尽可能短的篇幅里,将所有集合与并发集合的特征,实现方式,性能捋一遍。
适合所有”精通Java”其实还不那么自信的人阅读。
ListArrayList以数组实现。
节约空间,但数组有容量限制。
超出限制时会增加50%容量,用System.arraycopy()复制到新的数组,因此最好能给出数组大小的预估值。
默认第一次插入元素时创建大小为10的数组。
按数组下标访问元素–get(i)/set(i,e) 的性能很高,这是数组的基本优势。
直接在数组末尾加入元素–add(e)的性能也高,但如果按下标插入、删除元素–add(i,e), remove(i), remove(e),则要用System.arraycopy()来移动部分受影响的元素,性能就变差了,这是基本劣势。
LinkedList以双向链表实现。
链表无容量限制,但双向链表本身使用了更多空间,也需要额外的链表指针操作。
按下标访问元素–get(i)/set(i,e) 要悲剧的遍历链表将指针移动到位(如果i>数组大小的一半,会从末尾移起)。
插入、删除元素时修改前后节点的指针即可,但还是要遍历部分链表的指针才能移动到下标所指的位置,只有在链表两头的操作–add(), addFirst(),removeLast()或用iterator()上的remove()能省掉指针的移动。
CopyOnWriteArrayList并发优化的ArrayList。
用CopyOnWrite策略,在修改时先复制一个快照来修改,改完再让内部指针指向新数组。
因为对快照的修改对读操作来说不可见,所以只有写锁没有读锁,加上复制的昂贵成本,典型的适合读多写少的场景。
如果更新频率较高,或数组较大时,还是Collections.synchronizedList(list),对所有操作用同一把锁来保证线程安全更好。
增加了addIfAbsent(e)方法,会遍历数组来检查元素是否已存在,性能可想像的不会太好。
补充无论哪种实现,按值返回下标–contains(e), indexOf(e), remove(e) 都需遍历所有元素进行比较,性能可想像的不会太好。
没有按元素值排序的SortedList,在线程安全类中也没有无锁算法的ConcurrentLinkedList,凑合着用Set与Queue中的等价类时,会缺少一些List特有的方法。
MapHashMap以Entry[]数组实现的哈希桶数组,用Key的哈希值取模桶数组的大小可得到数组下标。
插入元素时,如果两条Key落在同一个桶(比如哈希值1和17取模16后都属于第一个哈希桶),Entry用一个next属性实现多个Entry以单向链表存放,后入桶的Entry 将next指向桶当前的Entry。
查找哈希值为17的key时,先定位到第一个哈希桶,然后以链表遍历桶里所有元素,逐个比较其key值。
当Entry数量达到桶数量的75%时(很多文章说使用的桶数量达到了75%,但看代码不是),会成倍扩容桶数组,并重新分配所有原来的Entry,所以这里也最好有个预估值。
取模用位运算(hash & (arrayLength-1))会比较快,所以数组的大小永远是2的N次方,你随便给一个初始值比如17会转为32。
默认第一次放入元素时的初始值是16。
iterator()时顺着哈希桶数组来遍历,看起来是个乱序。
在JDK8里,新增默认为8的閥值,当一个桶里的Entry超过閥值,就不以单向链表而以红黑树来存放以加快Key的查找速度。
LinkedHashMap扩展HashMap增加双向链表的实现,号称是最占内存的数据结构。
支持iterator()时按Entry的插入顺序来排序(但是更新不算,如果设置accessOrder属性为true,则所有读写访问都算)。
实现上是在Entry上再增加属性before/after指针,插入时把自己加到Header Entry的前面去。
如果所有读写访问都要排序,还要把前后Entry的before/after拼接起来以在链表中删除掉自己。
TreeMap以红黑树实现,篇幅所限详见入门教程。
支持iterator()时按Key值排序,可按实现了Comparable接口的Key的升序排序,或由传入的Comparator控制。
可想象的,在树上插入/删除元素的代价一定比HashMap的大。
支持SortedMap接口,如firstKey(),lastKey()取得最大最小的key,或sub(fromKey, toKey), tailMap(fromKey)剪取Map的某一段。
ConcurrentHashMap并发优化的HashMap,默认16把写锁(可以设置更多),有效分散了阻塞的概率,而且没有读锁。
数据结构为Segment[],Segment里面才是哈希桶数组,每个Segment一把锁。
Key 先算出它在哪个Segment里,再算出它在哪个哈希桶里。
支持ConcurrentMap接口,如putIfAbsent(key,value)与相反的replace(key,value)与以及实现CAS的replace(key, oldValue, newValue)。
没有读锁是因为put/remove动作是个原子动作(比如put是一个对数组元素/Entry 指针的赋值操作),读操作不会看到一个更新动作的中间状态。
ConcurrentSkipListMapJDK6新增的并发优化的SortedMap,以SkipList实现。
SkipList是红黑树的一种简化替代方案,是个流行的有序集合算法,篇幅所限见入门教程。
Concurrent包选用它是因为它支持基于CAS的无锁算法,而红黑树则没有好的无锁算法。
很特殊的,它的size()不能随便调,会遍历来统计。
补充关于null,HashMap和LinkedHashMap是随意的,TreeMap没有设置Comparator 时key不能为null;ConcurrentHashMap在JDK7里value不能为null(这是为什么呢?),JDK8里key与value都不能为null;ConcurrentSkipListMap是所有JDK里key与value都不能为null。
SetSet几乎都是内部用一个Map来实现, 因为Map里的KeySet就是一个Set,而value是假值,全部使用同一个Object。
Set的特征也继承了那些内部Map实现的特征。
HashSet:内部是HashMap。
LinkedHashSet:内部是LinkedHashMap。
TreeSet:内部是TreeMap的SortedSet。
ConcurrentSkipListSet:内部是ConcurrentSkipListMap的并发优化的SortedSet。
CopyOnWriteArraySet:内部是CopyOnWriteArrayList的并发优化的Set,利用其addIfAbsent()方法实现元素去重,如前所述该方法的性能很一般。
补充:好像少了个ConcurrentHashSet,本来也该有一个内部用ConcurrentHashMap的简单实现,但JDK偏偏没提供。
Jetty就自己封了一个,Guava则直接用java.util.Collections.newSetFromMap(new ConcurrentHashMap()) 实现。
QueueQueue是在两端出入的List,所以也可以用数组或链表来实现。
–普通队列–LinkedList是的,以双向链表实现的LinkedList既是List,也是Queue。
它是唯一一个允许放入null的Queue。
ArrayDeque以循环数组实现的双向Queue。
大小是2的倍数,默认是16。
普通数组只能快速在末尾添加元素,为了支持FIFO,从数组头快速取出元素,就需要使用循环数组:有队头队尾两个下标:弹出元素时,队头下标递增;加入元素时,如果已到数组空间的末尾,则将元素循环赋值到数组[0](如果此时队头下标大于0,说明队头弹出过元素,有空位),同时队尾下标指向0,再插入下一个元素则赋值到数组[1],队尾下标指向1。
如果队尾的下标追上队头,说明数组所有空间已用完,进行双倍的数组扩容。
PriorityQueue用二叉堆实现的优先级队列,详见入门教程,不再是FIFO而是按元素实现的Comparable接口或传入Comparator的比较结果来出队,数值越小,优先级越高,越先出队。
但是注意其iterator()的返回不会排序。
–线程安全的队列–ConcurrentLinkedQueue/ConcurrentLinkedDeque无界的并发优化的Queue,基于链表,实现了依赖于CAS的无锁算法。
ConcurrentLinkedQueue的结构是单向链表和head/tail两个指针,因为入队时需要修改队尾元素的next指针,以及修改tail指向新入队的元素两个CAS动作无法原子,所以需要的特殊的算法,篇幅所限见入门教程。
PriorityBlockingQueue无界的并发优化的PriorityQueue,也是基于二叉堆。
使用一把公共的读写锁。
虽然实现了BlockingQueue接口,其实没有任何阻塞队列的特征,空间不够时会自动扩容。
DelayQueue内部包含一个PriorityQueue,同样是无界的。
元素需实现Delayed接口,每次调用时需返回当前离触发时间还有多久,小于0表示该触发了。
pull()时会用peek()查看队头的元素,检查是否到达触发时间。
ScheduledThreadPoolExecutor用了类似的结构。
–线程安全的阻塞队列–BlockingQueue的队列长度受限,用以保证生产者与消费者的速度不会相差太远,避免内存耗尽。
队列长度设定后不可改变。
当入队时队列已满,或出队时队列已空,不同函数的效果见下表:ArrayBlockingQueue定长的并发优化的BlockingQueue,基于循环数组实现。
有一把公共的读写锁与notFull、notEmpty两个Condition管理队列满或空时的阻塞状态。
LinkedBlockingQueue/LinkedBlockingDeque可选定长的并发优化的BlockingQueue,基于链表实现,所以可以把长度设为Integer.MAX_VALUE。
利用链表的特征,分离了takeLock与putLock两把锁,继续用notEmpty、notFull管理队列满或空时的阻塞状态。
补充JDK7有个LinkedTransferQueue,transfer(e)方法保证Producer放入的元素,被Consumer取走了再返回,比SynchronousQueue更好,有空要学习下。