Java 集合Collection常见知识点汇总~

看了一些所谓大公司的JAVA面试问题,发现对于JAVA集合类的使用都比较看重似的,而自己在这方面还真的是所真甚少,抽空也学习学习吧。

java.util包中包含了一系列重要的集合类,而对于集合类,主要需要掌握的就是它的内部结构,以及遍历集合的迭代模式。

接口:Collection

Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。

所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。

主要的一个接口方法:boolean add(Ojbect c)
虽然返回的是boolean,但不是表示添加成功与否,这个返回值表示的意义是add()执行后,集合的内容是否改变了(就是元素的数量、位置等有无变化)。类似的addAll,remove,removeAll,remainAll也是一样的。

用Iterator模式实现遍历集合

Collection有一个重要的方法:iterator(),返回一个Iterator(迭代器),用于遍历集合的所有元素。Iterator模式可以把访问逻辑从不同的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。典型的用法如下:

Iterator it = collection.iterator(); // 获得一个迭代器
while(it.hasNext()) {
    Object obj = it.<span class="hljs-keyword">next(); // 得到下一个元素
}

不需要维护遍历集合的“指针”,所有的内部状态都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成。

每一种集合类返回的Iterator具体类型可能不同,但它们都实现了Iterator接口,因此,我们不需要关心到底是哪种Iterator,它只需要获得这个Iterator接口即可,这就是接口的好处,面向对象的威力。

要确保遍历过程顺利完成,必须保证遍历过程中不更改集合的内容(Iterator的remove()方法除外),所以,确保遍历可靠的原则是:只在一个线程中使用这个集合,或者在多线程中对遍历代码进行同步。

Collection接口派生的两个接口是List和Set。

List接口

    List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。和下面要提到的Set不同,List允许有相同的元素

    除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。

   实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

下面进行小总结:(线读写底层)

Vector、ArrayList、LinkedList均为线型的数据结构,但是从实现方式与应用场景中又存在差别。

1 底层实现方式
ArrayList内部用数组来实现;LinkedList内部采用双向链表实现;Vector内部用数组实现。

读写扩容机制
ArrayList在执行插入元素是超过当前数组预定义的最大值时,数组需要扩容,扩容过程需要调用底层System.arraycopy()方法进行大量的数组复制操作;在删除元素时并不会减少数组的容量(如果需要缩小数组容量,可以调用trimToSize()方法);在查找元素时要遍历数组,对于非null的元素采取equals的方式寻找。

LinkedList插入元素时,须创建一个新的Entry对象,并更新相应元素的前后元素的引用;在查找元素时,需遍历链表;在删除元素时,要遍历链表,找到要删除的元素,然后从链表上将此元素删除即可。
Vector与ArrayList仅在插入元素时容量扩充机制不一致。对于Vector,默认创建一个大小为10的Object数组,并将capacityIncrement设置为0;当插入元素数组大小不够时,如果capacityIncrement大于0,则将Object数组的大小扩大为现有size+capacityIncrement;如果capacityIncrement<=0,则将Object数组的大小扩大为现有大小的2倍。

3 读写效率

ArrayList对元素的增加和删除都会引起数组的内存分配空间动态发生变化。因此,对其进行插入和删除速度较慢,但检索速度很快。

LinkedList由于基于链表方式存放数据,增加和删除元素的速度较快,但是检索速度较慢。

4 线程安全性

ArrayList、LinkedList为非线程安全;Vector是基于synchronized实现的线程安全的ArrayList。

需要注意的是:单线程应尽量使用ArrayList,Vector因为同步会有性能损耗;即使在多线程环境下,我们可以利用Collections这个类中为我们提供的synchronizedList(List list)方法返回一个线程安全的同步列表对象。

问题回答

利用PriorityBlockingQueue或Disruptor可实现基于任务优先级为调度策略的执行调度系统。

LinkedList类

      LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。

      注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(…));

ArrayList类(和vector一样为动态数组,适合随机访问)

      ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。

      每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。

和LinkedList一样,ArrayList也是非同步的(unsynchronized)。(扩容增加50%)

Vector类

      Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。可以根据需要自动的增加容量,当数组已满时,会创建新的数组,并拷贝原有的数据(扩容增加一倍)

Stack 类

      Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口

      Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。

      很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。

请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Map接口

     请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

Hashtable类

      Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。

      添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。

      Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。

      使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
Hashtable numbers = new Hashtable();
numbers.put(“one”, new Integer(1));
numbers.put(“two”, new Integer(2));
numbers.put(“three”, new Integer(3));

要取出一个数,比如2,用相应的key:
Integer n = (Integer)numbers.get(“two”);
System.out.println(“two = ” + n);

      由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作

      如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。

Hashtable是同步的。

HashMap类

      HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代器操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低

WeakHashMap类

      WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

总结

  • 如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  • 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
  • 要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  • 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。
参考博文:https://www.cnblogs.com/pureEve/p/6546286.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/559010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内部类详解————匿名内部类

内部类三连击&#xff1a; 《内部类详解————匿名内部类》 《内部类详解————局部内部类》 《内部类详解————静态嵌套类》 应用场景 由于匿名内部类不利于代码的重用&#xff0c;因此&#xff0c;一般在确定此内部类只会使用一次时&#xff0c;才会使用匿名内部…

内部类详解————局部内部类

内部类三连击&#xff1a; 《内部类详解————匿名内部类》 《内部类详解————局部内部类》 《内部类详解————静态嵌套类》 定义 在方法或某个作用域内的内部类&#xff0c;称为局部内部类。匿名内部类就是一种局部内部类。 实现方式 public class OutterType …

关于面向对象以及三大特征的解释

关于面向对象以及三大特征的解释面向对象&#xff1a;在C语言编程中是面向过程而在Java编程中是面向对象的。面向过程更重要的是看重实现的具体过程&#xff0c;面向对象看重的是结果&#xff0c;不管其里面的具体过程&#xff0c;只看结果。举一个例子&#xff1a;我们从住的地…

内部类详解————静态内部类

内部类三连击&#xff1a; 《内部类详解————匿名内部类》 《内部类详解————局部内部类》 《内部类详解————静态内部类》 定义 静态内部类&#xff0c;又叫静态嵌套类或嵌套类。是使用static关键字修饰的内部类。 静态内部类可以用 private 修饰&#xff0c;这…

jvm gc垃圾回收机制和参数说明amp;amp;Java JVM 垃圾回收(GC 在什么时候,对什么东西,做了什么事情)

jvm gc&#xff08;垃圾回收机制&#xff09; Java JVM 垃圾回收&#xff08;GC 在什么时候&#xff0c;对什么东西&#xff0c;做了什么事情&#xff09; 前言&#xff1a;&#xff08;先大概了解一下整个过程&#xff09;作者&#xff1a;知乎用户 链接&#xff1a;https:…

【Mathematical Model】Ransac线性回归Python代码

Ransac算法&#xff0c;也称为随机抽样一致性算法&#xff0c;是一种迭代方法&#xff0c;用于从一组包含噪声或异常值的数据中估计数学模型。Ransac算法特别适用于线性回归问题&#xff0c;因为它能够处理包含异常值的数据集&#xff0c;并能够估计出最佳的线性模型。 1 简介 …

异常解析————Parameter metadata not available for the given statement

引言 在将数据存入mysql数据库时抛出异常&#xff1a;Parameter metadata not available for the given statement。参数元数据对于给定的声明不可用。 SQL本身并没有错误&#xff1a; Autowiredprivate JdbcTemplate jdbc;public Integer saveScenicSequence(ScenicSequence…

Java中HashMap的常用操作

Java中HashMap的常用操作HashMap<Integer, Integer> hashMap new HashMap<>();hashMap.put(5, 2);hashMap.put(9, 2);hashMap.put(8, 1);hashMap.put(7, 3);hashMap.put(16, 1);hashMap.put(10, 2);hashMap.put(6, 2);//其实下面两个键值对是没有存的hashMap.put(…

MySQL忽略主键冲突,避免重复插入数据的三种方式

方案一&#xff1a;ignore 插入时检索主键列表&#xff0c;如存在相同主键记录&#xff0c;不更改原纪录&#xff0c;只插入新的记录。 INSERT IGNORE INTO ignore关键字所修饰的SQL语句执行后&#xff0c;在遇到主键冲突时会返回一个0&#xff0c;代表并没有插入此条数据。…

SQL分页查询的介绍以及好处~~

SQL分页查询的介绍以及好处~~分页查询就是将过多的结果在有限的界面上分多页来显示&#xff0c;一般将分页查询分为两类&#xff1a; 逻辑分页、物理分页。 逻辑分页是在用户第一次访问时&#xff0c;将数据库的所有记录全部查询出来&#xff0c;添加到一个大集合中&#xff0c…

Swagger使用————接口参数注解的使用缺陷

问题描述 在使用springboot开发web项目时&#xff0c;用到了swagger框架&#xff0c;来生成web api文档。但是其中有一项是举例说明参数的结构&#xff0c;如下图&#xff1a;但是&#xff0c;这个功能真的是非常方便&#xff0c;因为可以让前端开发人员第一时间得知参数的内部…

分布式事务最终一致性常用方案

分布式事务最终一致性常用方案目前的应用系统&#xff0c;不管是企业级应用还是互联网应用&#xff0c;最终数据的一致性是每个应用系统都要面临的问题&#xff0c;随着分布式的逐渐普及&#xff0c;数据一致性更加艰难&#xff0c;但是也很难有银弹的解决方案&#xff0c;也并…

数据列表的分页实现————分页敏捷开发

概要 分页功能是比较常见的基础功能&#xff0c;虽然比较简单&#xff0c;但是每次需要用到这个功能的时候还是需要现写一遍。为了实现更加宏观的业务复用&#xff0c;特将本人特别喜欢的简易分页逻辑在此记述&#xff0c;以备日后重用。 逻辑描述 一般的分页实现方式多是通…

Java基础————理解Integer对象的缓存策略

一个简单的面试题public static void main(String[] args) {Integer in1 100;Integer in2 100;Integer in3 200;Integer in4 200;System.out.println(in1 in2);System.out.println(in3 in4);} 运行结果&#xff1a; true false 从自动装箱谈Integer缓存 上述面试题中&…

Java面试日常总结大杂烩

日常总结大杂烩&#xff1a;一。 取出特定行数的数据1. select* from 表名 limit m,n; 2. select * from 表名 limit [offset,] rows;1. m代表从m1条记录行开始检索&#xff0c;n代表取出n条数据。&#xff08;m可设为0&#xff09;如&#xff1a;select * from表名 limit 6,5…

Eclipse深度患者设置VSCode快捷键

VSCode设置Eclipse中常用的快捷键 将eclipse中一些基本的快捷键输入右侧用户快捷键设置中&#xff1a; // Place your key bindings in this file to overwrite the defaults [{ "key": "alt/", "command": "editor.action.triggerSugges…

Java基础日常总结!!

Java基础日常总结&#xff01;&#xff01;1. Java的字符类型采用的是Unicode编码方案&#xff0c;每个Unicode码占用( )个比特位 在java中一个unicode占2个字节&#xff08;byte&#xff09;.一个字节等于8比特位&#xff08;bit&#xff09;.所以每个Unicode码占用 16 个比特…

NodeJS学习————关于let和const命令的使用理解

let的基本用法 在新的js规范ES6中&#xff0c;新增了let 命令&#xff0c;用来声明变量。用法类似于var&#xff0c;但不同的是所声明的变量&#xff0c;只在let 命令所在的代码块内有效。 { let a 10; var b 10; } //ReferenceError: a is not defined console.log(a …

forward和redirect的区别是什么?

forward和redirect是什么&#xff1f; 是servlet种的两种主要的跳转方式。forward又叫转发&#xff0c;redirect叫做重定向。 区别&#xff1a;&#xff08;本地效应次数&#xff09; 地址栏&#xff0c;数据共享&#xff0c;应用场景&#xff0c;效率&#xff0c;本质&…

MYSQL的索引类型:PRIMARY, INDEX,UNIQUE,FULLTEXT,SPAIAL 有什么区别?各适用于什么场合?

一、MySQL索引类型 MySql常见索引类型有&#xff1a;主键索引、唯一索引、普通索引、全文索引、组合索引 PRIMARY KEY&#xff08;主键索引&#xff09; ALTER TABLE table_name ADD PRIMARY KEY ( column ) UNIQUE(唯一索引) ALTER TABLE table_name ADD UNIQUE (colu…