栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

对于Java常见集合的理解及使用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

对于Java常见集合的理解及使用

        我们都知道,在Java开发中,永远离不开集合,有些在学习阶段的人会问,我不就用一个ArrayList就可以了吗?
        我想说的是,不,因为由于在学习阶段所接触到的数据,都是很少很少的,所以每次请求都会跑一遍数据库,得到数据之后进行操作。
        但是在真正的工作环境中,数据都是百万千万级别的,就比如我们经常使用的淘宝京东,数据量我们可想而知,如果每次进行页面跳转或者操作数据之后重新获取一遍,那对于服务器和数据库的压力是非常大的。
        所以我们要用到这些集合,因为集合保存在内存中,查询速度是很快的,对于数据库和服务器的压力也会大大减小。

        所以本章介绍我们常用的3种集合,分别是List、Set和Map

        在此之前,先看一下UML图,这是很常见很经典的一张图,同时也是一张很老的图了,因为还有一个CopyOnWriteArrayList类实现List接口,从图中可以看出,List接口和Set接口都继承Collection接口,唯独Map是分离出来的,面试中可能会本问到

 

1.List

        特点:元素有序,且可重复

        遍历:可通过下标,foreach,迭代器遍历

        扩容:初始容量10,负载因子0.5,扩容增量0.5倍

                   新容量 = 原容量 + 原容量 * 0.5

        实现类:

        1.1 ArrayList

                简单数据结构,超出容量自动扩容,动态数组

                内部实现是基于基础的对象数组的

                随机访问快,因为是用数组的方式,而且也保存在内存中

                不适合随机增加或删除,因为如果是在数组的中间添加或者删除数据,会打乱之后数据的顺序,比如删除其中一个数据之后,后面的数据的下标都会 -1, 所以不适合

                线程不安全

                删除时的注意点:

                1)下标循环删除方式1

for(int i = 0; i 

                这种方式是可以删除元素的,但是如果要删除的元素挨在一起,那么就会出问题,因为当在ArrayListt中,删除一个元素之后,下面的元素会挤上去,就比如一个数组的第一位和第二位是你要删除的,循环的下标为0,1,当你删除掉第一个时,第二个就会跑到第一个的位置,但是第二次循环已经从第三位开始了,虽然这时的下标还是1,但是位置已经改变了,所以这种方式不可取

                2) 下标循环删除方式2

for(int i = 0; i < list.size(); i++) {
			if(list.get(i) == 3) list.remove(i--);
		}

                这种方式不管元素是否挨在一起,都能正常删除

        

                3)下标循环倒序删除

for(int i = list.size() - 1; i >= 0; i--) {
			if(list.get(i) == 3) list.remove(i);
		}

                这种方式也是可取的,因为是从数组的最后一个开始删除,就不会出现移位删不到的情况

                4) foreach循环删除

for(Integer e: list) {
			if(e == 3) {
				list.remove(e);
			}
		}

                这种方式看上去好像是没有问题的,但是最大的问题就是,foreach循环完全不能用,如果你要删除的元素在倒数第二位,那么刚好是可以运行的,如果不是的话,会直接报错

                5) 删除的参数是基本型的整数

list.remove(i)

                如果remove方法中传入的是个数值类型,那么就会根据下标删除对应元素,如果是对象类型,那么list就会删除其保存的那个对象

        1.2 LinkedList

                LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部进行操作

                线程不安全

                LinkedList可被用作堆栈(stack)【包括了push,pop方法】,队列(queue)或双向队列(deque)

                以双向链表实现,链表无容量限制,允许元素为null,线程不安全

                适合做随机的增加或删除

        1.3 Vector

                线程安全

                并行性能慢,不建议使用,就是当有一个线程访问时,会把整个集合锁死,很影响效率,现在一般不用Vector了

        1.4 CopyOnWriteArrayList

                这就是之前所说新加的一个实现类

                写时复制

                线程安全

                适合于读多,写少的场景

                写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array

                比Vector性能高

                最终一致性

                实现了List接口,使用方式与ArrayList类似

        

2.Set

        特点:无序,不重复

        遍历:foreach,迭代器

        扩容: 初始容量16,负载因子0.75,扩容增量1倍

        实现类:

        2.1 HashSet

                它存储唯一元素并允许空值,依据对象的hashcode来确定该元素是否存在

                由HashMap支持

                不保持插入顺序

                非线程安全

                性能参数:初始容量,负载因子(默认值: 初始容量16,负载因子0.75)

                注意点:

                1)set不能通过下标删除,因为是无序的,但是可以根据元素删除。

                2)HashSet实际上是根据Map实现的,HashSet刚好只用到了Map中的“键”

        2.2 TreeSet

                是一个包含有序的且没有重复元素的集合

                作用是提供有序的Set集合,自然排序或者根据提供的Comparator进行排序

                TreeSet是基于TreeMap实现的

                自定义比较器:

                1)通过构造函数传入比较器

TreeSet tset = new TreeSet(new Comparator() {
			@Override
			public int compare(Integer o1, Integer o2) {
				// TODO Auto-generated method stub
				return o2 - o1;
			}
		});

                2)实现排序接口

public class Student implements Comparable{
	
	private Integer sid;
	
	private String name;
	
	private int age;
	
	//构造函数,getter,setter,hashCode,equals等方法省略
	
	@Override
	public int compareTo(Student o) {
		// TODO Auto-generated method stub
		return o.getAge() - this.getAge();
	}

}

3.Map

        特点:无序,键值对,键不能重复,值可以重复,键重复则覆盖,没有继承Collection接口

        扩容:初始容量16,负载因子0.75,扩容增量1倍

        遍历:先获取所有键的Set集合,再遍历(通过键获取值),取出保存所有Entry(保存键值对的实体)的Set,再遍历此Set即可

        实现类:

        3.1 HashMap

                线程不安全,最常用,速度快

                内部采用数组来存放数据

                基本原理:

                put执行过程:在调用PUT方法时,会对传入的key进行哈希运算得到一个hashcode,然后再通过逻辑与操作得到一个数组下标,最后将key+value存在这个数组下标处。确定了key+value该存的位置之后,对于不同的参数可能会得到相同的HashCode,也就是会发生哈希冲突,反应到HashMap中就是,当PUT两个不同的key时可能会得到相同的HashCode从而得到相同的数组下标,其实在HashMap中就算key所对应的HashCode不一样,那么也有可能在经过逻辑与操作之后得到相同的数组下标,那么这时HashMap是如何处理的呢?对,就是链表
 

 

                 Table数组中的的Node

                流程图中绿色标出的部分为JDK8新增的处理逻辑,目的是在Table[i]中的Node节点数量大于8时,通过红黑树提升查找速度。

        3.2 HashTable

                线程安全,不太常用

        3.3 ConcurrentHashMap

                线程安全,比HashTable性能高

        3.4 TreeMap

                key值按一定的顺序排序

                添加或获取元素时性能较HashMap慢,因为需求维护内部的红黑树,用于保证key值的顺序

        3.5 LinkedHashMap

                继承HashMap

                LinkedHashMap是有序的,且默认为插入顺序,当我们希望有顺序地去存储key-value时,就需要使用LinkedHashMap了

                示例:

 Map linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("name1", "josan1");
        linkedHashMap.put("name2", "josan2");
        linkedHashMap.put("name3", "josan3");
        Set> set = linkedHashMap.entrySet();
        Iterator> iterator = set.iterator();
        while(iterator.hasNext()) {
            Entry entry = iterator.next();
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            System.out.println("key:" + key + ",value:" + value);
        }

总结:所以,在不同的情况下需要使用不同的集合框架,要看当时功能需求,这样才能把功能写的更好

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/999036.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号