Java集合框架总结

  • Collection一次存一个元素,是单列集合;

  • Map一次存一对元素,是双列集合。Map存储的一对元素:键–值,键(key)与值(value)间有对应(映射)关系。

单列集合继承关系图

单列集合

Collection集合主要有List和Set两大接口

  • List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。

  • Set:无序(存入和取出顺序有可能不一致),不可以存储重复元素。必须保证元素唯一性。

List集合

List是元素有序并且可以重复的集合。

List的主要实现:ArrayList, LinkedList, Vector。

List常用方法

List常用方法

ArrayList、LinkedList、Vector 的区别

| | ArrayList | LinkedList | Vector |

| — | — | — | — |

| 底层实现 | 数组 | 双向链表 | 数组 |

| 同步性及效率 | 不同步,非线程安全,效率高,支持随机访问 | 不同步,非线程安全,效率高 | 同步,线程安全,效率低 |

| 特点 | 查询快,增删慢 | 查询慢,增删快 | 查询快,增删慢 |

| 默认容量 | 10 | / | 10 |

| 扩容机制 | int newCapacity = oldCapacity + (oldCapacity >> 1);//1.5 倍 | / | 2 倍 |

总结

  • ArrayList 和 Vector 基于数组实现,对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。
  • LinkedList 不会出现扩容的问题,所以比较适合随机位置增、删。但是其基于链表实现,所以在定位时需要线性扫描,效率比较低。

  • 当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;

  • 当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。

遍历时操作元素

遍历集合时,同时操作集合中的元素(增删等)

/**

  • Description: for循环遍历

  • 输出结果:

  • [a, b, c, d, e]

  • 由结果可知,第二个元素b并未删除,原因是当第一个元素b被删除后,它后面所有的元素都向前移动了一个单位,循环时导致第二个元素b漏掉了

*/

public static void remove(List list) {

for (int i = 0; i < list.size(); i++) {

String s = list.get(i);

if (s.equals(“b”)) {

list.remove(s);

}

}

}

/**

  • Description: foreach循环遍历

  • 会报错:java.util.ConcurrentModificationException。这是因为在这里,foreach循环遍历容器本质上是使用迭代器进行遍历的,会对修改次数modCount进行检查,不允许集合进行更改操作

*/

public static void remove2(List list) {

for (String s : list) {

if (s.equals(“b”)) {

list.remove(s);

}

System.out.println(s);

}

}

/**

  • Description: 使用迭代器遍历

*/

public static void remove3(List list) {

Iterator it = list.iterator();

while (it.hasNext()) {

String s = it.next();

if (s.equals(“b”)) {

it.remove();

}

}

}

使用迭代器遍历删除时,能够避免方法二中出现的问题。这是因为:在ArrayList中,modCount是指集合的修改次数,当进行add或者delete时,modCount会+1;expectedModCount是指集合的迭代器的版本号,初始值是modCount,但是当集合进行add或者delete操作时,modCount会+1,而expectedModCount不会改变,所以方法二中会抛出异常。但是it.remove操作时,会同步expectedModCount的值,把modCount的值赋予expectedModCount。所以不会抛出异常。

测试方法

public static void main(String[] args) {

List arrayList = new ArrayList();

arrayList.add(“a”);

arrayList.add(“b”);

arrayList.add(“b”);

arrayList.add(“c”);

arrayList.add(“d”);

arrayList.add(“e”);

// remove(arrayList);

// remove2(arrayList);

remove3(arrayList);

System.out.println(arrayList);

}

总结:如果想正确的循环遍历删除(增加)元素,需要使用方法三,也就是迭代器遍历删除(增加)的方法。

Set集合

Set集合元素无序(存入和取出的顺序不一定一致),并且没有重复对象。

Set的主要实现类:HashSet, TreeSet。

Set常用方法

Set常用方法

HashSet、TreeSet、LinkedHashSet的区别

| | HashSet | TreeSet | LinkedHashSet |

| — | — | — | — |

| 底层实现 | HashMap | 红黑树 | LinkedHashMap |

| 重复性 | 不允许重复 | 不允许重复 | 不允许重复 |

| 有无序 | 无序 | 有序,支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。 | 有序,以元素插入的顺序来维护集合的链接表 |

| 时间复杂度 | add(),remove(),contains()方法的时间复杂度是O(1) | add(),remove(),contains()方法的时间复杂度是O(logn) | LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet,时间复杂度是 O(1)。 |

| 同步性 | 不同步,线程不安全 | 不同步,线程不安全 | 不同步,线程不安全 |

| null值 | 允许null值 | 不支持null值,会抛出 java.lang.NullPointerException 异常。因为TreeSet应用 compareTo() 方法于各个元素来比较他们,当比较null值时会抛出 NullPointerException异常。 | 允许null值 |

| 比较 | equals() | compareTo() | equals() |

HashSet如何检查重复

当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。

hashCode()与equals()的相关规定:

  • 如果两个对象相等,则hashcode一定也是相同的

  • 两个对象相等,equals方法返回true

  • 两个对象有相同的hashcode值,它们也不一定是相等的

  • 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖

hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。

总结:

HashSet是一个通用功能的Set,而LinkedHashSet 提供元素插入顺序保证,TreeSet是一个SortedSet实现,由Comparator 或者 Comparable指定的元素顺序存储元素。

Map接口


Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。

Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap

双列集合继承关系图

双列集合

Map常用方法

Map常用方法

HashMap、HashTable、TreeMap的区别

  • TreeMap:基于红黑树实现。

  • HashMap:基于哈希表实现。

  • HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。

  • LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。

| | HashMap | HashTable | TreeMap |

| — | — | — | — |

| 底层实现 | 哈希表(数组+链表) | 哈希表(数组+链表) | 红黑树 |

| 同步性 | 线程不同步 | 同步 | 线程不同步 |

| null值 | 允许 key 和 Vale 是 null,但是只允许一个 key 为 null,且这个元素存放在哈希表 0 角标位置 | 不允许key、value 是 null | value允许为null。

当未实现 Comparator 接口时,key 不可以为null

当实现 Comparator 接口时,若未对 null 情况进行判断,则可能抛 NullPointerException 异常。如果针对null情况实现了,可以存入,但是却不能正常使用get()访问,只能通过遍历去访问。 |

| hash | 使用hash(Object key)扰动函数对 key 的 hashCode 进行扰动后作为 hash 值 | 直接使用 key 的 hashCode() 返回值作为 hash 值 | |

| 容量 | 容量为 2^4 且容量一定是 2^n | 默认容量是11,不一定是 2^n | |

| 扩容 | 两倍,且哈希桶的下标使用 &运算代替了取模 | 2倍+1,取哈希桶下标是直接用模运算 | |

HashMap在JDK1.7和JDK1.8中有哪些不同

| 不同 | JDK 1.7 | JDK 1.8 |

| :-- | :-- | :-- |

| 存储结构 | 数组 + 链表 | 数组 + 链表 + 红黑树 |

| 初始化方式 | 单独函数:inflateTable() | 直接集成到了扩容函数resize()中 |

| hash值计算方式 | 扰动处理 = 9次扰动 = 4次位运算 + 5次异或运算 | 扰动处理 = 2次扰动 = 1次位运算 + 1次异或运算 |

| 存放数据的规则 | 无冲突时,存放数组;冲突时,存放链表 | 无冲突时,存放数组;冲突 & 链表长度 < 8:存放单链表;冲突 & 链表长度 > 8:树化并存放红黑树 |

| 插入数据方式 | 头插法(先讲原位置的数据移到后1位,再插入数据到该位置) | 尾插法(直接插入到链表尾部/红黑树) |

| 扩容后存储位置的计算方式 | 全部按照原来方法进行计算(即hashCode ->> 扰动函数 ->> (h&length-1)) | 按照扩容后的规律计算(即扩容后的位置=原位置 or 原位置 + 旧容量) |

集合工具类Collections


Collections:集合工具类,方便对集合的操作。这个类不需要创建对象,内部提供的都是静态方法。

静态方法:

Collections.sort(list);//list集合进行元素的自然顺序排序。

Collections.sort(list,new ComparatorByLen());//按指定的比较器方法排序。

class ComparatorByLen implements Comparator{

public int compare(String s1,String s2){

int temp = s1.length()-s2.length();

return temp==0?s1.compareTo(s2):temp;

}

}

Collections.max(list);//返回list中字典顺序最大的元素。

int index = Collections.binarySearch(list,“zz”);//二分查找,返回角标。

Collections.reverseOrder();//逆向反转排序。

Collections.shuffle(list);//随机对list中的元素进行位置的置换。

//将非同步集合转成同步集合的方法:Collections中的 XXX synchronizedXXX(XXX);

//原理:定义一个类,将集合所有的方法加同一把锁后返回。

List synchronizedList(list);

Map synchronizedMap(map);

Collection 和 Collections的区别

Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。

Collection是个java.util下的接口,它是各种集合结构的父接口,继承于它的接口主要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历等。

数组工具类 Arrays


用于操作数组对象的工具类,里面都是静态方法。

数组 -> 集合:asList方法,将数组转换成list集合。

String[] arr ={“abc”,“kk”,“qq”};

List list =Arrays.asList(arr);//将arr数组转成list集合。

将数组转换成集合,有什么好处呢?用aslist方法,将数组变成集合;

可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexOf、set;

注意(局限性):数组是固定长度,不可以使用集合对象增加或者删除等,会改变数组长度的功能方法。比如add、remove、clear。(会报不支持操作异常UnsupportedOperationException);

如果数组中存储的引用数据类型,直接作为集合的元素可以直接用集合方法操作。

如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。

集合 -> 数组:用的是Collection接口中的toArray()方法;

如果给toArray传递的指定类型的数据长度小于了集合的size,那么toArray方法,会自定再创建一个该类型的数据,长度为集合的size。

如果传递的指定的类型的数组的长度大于了集合的size,那么toArray方法,就不会创建新数组,直接使用该数组即可,并将集合中的元素存储到数组中,其他为存储元素的位置默认值null。

所以,在传递指定类型数组时,最好的方式就是指定的长度和size相等的数组。

将集合变成数组后有什么好处?限定了对集合中的元素进行增删操作,只要获取这些元素即可。

用基本数据类型的数组转换ArrayList,ArrayList的size有问题

public static void main(String[] args) {

int[] arr1 = { 1, 2, 3, 4, 5 };

List<int[]> intList = Arrays.asList(arr1);

// intList size: 1

System.out.println(String.format(“intList size: %s”, intList.size()));

Integer[] arr2 = { 1, 2, 3, 4, 5 };

List integerList = Arrays.asList(arr2);

// integerList size: 5

System.out.println(String.format(“integerList size:%s”, integerList.size()));

}

asList方法接受的参数是一个泛型的变长参数,我们知道基本数据类型是无法泛型化的,也就是说基本类型是无法作为asList方法的参数的, 要想作为泛型参数就必须使用其所对应的包装类型。但是这个这个实例中为什么没有出错呢?因为该实例是将int 类型的数组当做其参数,而在Java中数组是一个对象,它是可以泛型化的。所以该例子是不会产生错误的。既然例子是将整个int 类型的数组当做泛型参数,那么经过asList转换就只有一个int 的列表了.

结论:

在使用asList()时尽量不要将基本数据类型数组转List.

asList转换得到的ArrayList不是java.util.ArrayList

public static void main(String[] args) {

String[] arr = {“abc”, “kk”, “qq”};

List list = Arrays.asList(arr);

// 添加一个元素,抛出异常UnsupportedOperationException

list.add(“bb”);

}

原因:

此处ArrayList是Arrays的内部类,并没有add方法,add方法是父类AbstractList的,但是没有具体实现,

而是直接抛出UnsupportedOperationException异常.

Arrays内部类ArrayList

正确操作

public static void main(String[] args) {

String[] arr = {“abc”, “kk”, “qq”};

// 使用new ArrayList包裹一层

List list = new ArrayList<>(Arrays.asList(arr));

list.add(“bb”);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

结局:总结+分享

看完美团、字节、腾讯这三家的一二三面试问题,是不是感觉问的特别多,可能咱们真的又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。

开篇有提及我可是足足背下了Java互联网工程师面试1000题,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱

  • Java互联网工程师面试1000题

image.png

而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的 《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题。

  • 程序员代码面试指南–IT名企算法与数据结构题目最优解

image.png

  • 其余像设计模式,建议可以看看下面这4份PDF(已经整理)

image.png

  • 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。

image.png

以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
Java互联网工程师面试1000题*,多少还是有点用的呢,换汤不换药,不管面试官怎么问你,抓住本质即可!能读到此处的都是真爱

  • Java互联网工程师面试1000题

[外链图片转存中…(img-1oOVRYWb-1713178140087)]

而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的 《程序员代码面试指南 IT名企算法与数据结构题目最优解》,里面近200道真实出现过的经典代码面试题。

  • 程序员代码面试指南–IT名企算法与数据结构题目最优解

[外链图片转存中…(img-fO081Isv-1713178140087)]

  • 其余像设计模式,建议可以看看下面这4份PDF(已经整理)

[外链图片转存中…(img-vRv7rgkU-1713178140087)]

  • 更多的Java面试学习笔记如下,关于面试这一块,我额外细分出Java基础-中级-高级开发的面试+解析,以及调优笔记等等等。。。

[外链图片转存中…(img-yJEvkktx-1713178140087)]

以上所提及的全部Java面试学习的PDF及笔记,如若皆是你所需要的,那么都可发送给你!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 28
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、Java基础语法 1.变量和常量 变量用于存储数据。Java中的变量有三种类型:局部变量、成员变量和静态变量。 常量是指在程序运行时不会被修改的值,在Java中用final关键字标识。常量分为静态常量和实例常量。 2.数据类型 Java中的数据类型分为基本数据类型和引用数据类型。基本数据类型有byte、short、int、long、float、double、char和boolean。 引用数据类型包括类、接口、数组和枚举。 3.运算符 Java中的运算符包括算术运算符、关系运算符、逻辑运算符、位运算符、三目运算符等。 4.流程控制 Java中的流程控制有if-else语句、switch-case语句、for循环、while循环、do-while循环、break语句、continue语句等。 二、面向对象编程 1.类和对象 类是对象的实体,包含属性和方法。对象是类的实例,具有独立的状态和行为。 2.继承和多态 继承是指一个类可以继承另一个类的属性和方法。 多态是指同一种操作作用于不同的对象,可以有不同的实现方式,实现方式由实际的对象类型决定。 3.封装和抽象 封装是指将类的属性和方法进行隐藏,只对外暴露必要的接口。通过封装可以保证类的安全性和灵活性。 抽象是指将类的共性进行概括,并去除与具体实现无关的内容。通过抽象可以提高代码的复用性和可维护性。 三、异常处理 在Java中,异常是指程序执行时发生的错误或异常情况。Java中的异常处理机制可以有效地避免程序运行中的错误导致程序崩溃。 Java中异常处理机制可以通过try-catch语句捕获异常,并通过throw和throws语句抛出和传递异常。在异常处理中可以利用finally语句完成清理工作。 四、集合框架 Java中的集合框架是对容器类的统称,包括List、Set和Map等容器类。 List是一个有序的容器,允许重复的元素。Set是一个无序的容器,不允许重复的元素。Map是一种键值对的映射容器。 五、IO操作 Java中的IO操作包括字节流和字符流。字节流操作的是二进制数据,字符流操作的是文本数据。 Java中的IO操作可以通过输入流和输出流来实现。输入流用于读取数据,输出流用于写入数据。 六、线程 线程是指程序中的一个执行流程,一个程序可以同时运行多个线程执行不同的任务。 在Java中,可以通过继承Thread类或实现Runnable接口来创建线程。 Java中的线程可以通过synchronized关键字来实现同步,防止线程间数据的冲突。 七、JDBC数据库操作 JDBC是Java数据库连接的一种标准,可以用于连接关系型数据库的操作。使用JDBC需要进行数据库驱动的加载,然后进行数据库连接,最后进行数据库的增删改查等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值