Java集合体系总结

简介: 一、集合框架集合是容纳数据的容器,java常用的集合体系图如下。以集合中是否运行重复元素来分,主要有List和Set接口,List集合中可以有重复元素,Set集合集合中的元素不可重复,Iterator和List Iterator是遍历集合的2个迭代器,Map是存储Key/Value键值对的容器。

一、集合框架

集合是容纳数据的容器,java常用的集合体系图如下。以集合中是否运行重复元素来分,主要有List和Set接口,List集合中可以有重复元素,Set集合集合中的元素不可重复,Iterator和List Iterator是遍历集合的2个迭代器,Map是存储Key/Value键值对的容器。

这里写图片描述

java集合体系图

二、迭代器

迭代器的用法写在后面,这里说明Iterator和ListIterator的区别:

  1. Iterator在遍历一个集合的过程中不能修改集合中的对象,ListIterator可以修改对象
  2. ListIterator从后往前遍历,有hasPrevious()方法,Iterator没有
  3. ListIterator可以在遍历过程中进行添加元素操作,Iterator不能,否则会报java.util.ConcurrentModificationException异常。

三、List集合

List集合存储不唯一、有序的对象,可以操作角标。

3.1 ArrayList

3.1.1内部实现

ArrayList也叫变长数组,数组的长度是固定的,ArrayList可以随着元素的增多长度增加,内部实现为数组。ArrayList在添加元素时首先会判断长度,长度不够时,长度扩展到原来到1.5倍,原数组复制到新数组。

3.1.2关键属性

属性 取值或结论
是否允许重复 允许
是否有序 有序
是否允许为空 允许
是否线程安全
使用场景 多查询、少增删

3.1.3 测试示例

创建一个ArrayList对象,添加5个元素,其中一个为null:

        arrayList.add("a1");
        arrayList.add("a2");
        arrayList.add(null);
        arrayList.add("a3");
        arrayList.add("a4");
        System.out.println(arrayList);

打印结果:

[a1, a2, null, a3, a4]

使用迭代器for循环方式遍历:

        for (Iterator iter=arrayList.iterator();iter.hasNext();){
            System.out.println(iter.next());
        }

使用迭代器while循环方式遍历:

        Iterator iter=arrayList.iterator();
        while (iter.hasNext()){
            System.out.println(iter.next());
        }

存储自定义对象:

public class Boy {

    private String name;
    private int age;
    //省略setter、getter、构造器和toString()方法
}

添加Boy对象到ArrayList中并打印:

 ArrayList<Boy> boys=new ArrayList<>();

        Boy b1=new Boy("Tom",12);
        Boy b2=new Boy("Jack",11);
        Boy b3=new Boy("Mike",15);

        boys.add(b1);
        boys.add(b2);
        boys.add(b3);

        System.out.println(boys);
        Iterator<Boy> iter=boys.iterator();
        while (iter.hasNext()){
            Boy b=iter.next();
            System.out.println(b.getName()+"----"+b.getAge());
        }

结果:

[Boy{name='Tom', age=12}, Boy{name='Jack', age=11}, Boy{name='Mike', age=15}]
Tom----12
Jack----11
Mike----15

3.1.4 转成线程安全

非线程安全的ArrayList

ArrayList<String> arrayList = new ArrayList();

线程安全的ArrayList

List arrayList =Collections.synchronizedList(new ArrayList<String>()) ;

3.1.5 常用方法

  • clear():移除列表中的所有元素
  • contains(Object o): 包含指定元素
  • get(int index):返回列表中指定位置上的元素
  • indexOf(Object o): 返回列表中首次出现指定元素的索引,如果列表不包含,则返回-1
  • isEmpty():列表为空返回true
  • lastIndexOf(Object o):返回列表中最后一次出现指定元素的索引,如果列表不包含元素则返回-1
  • remove(int index):移除列表中指定位置的元素
  • remove(Object o):移除列表中首次出现的指定元素
  • removeRange(int fromIndex,int toIndex):移除列表中索引在fromIndex(包括)和toIndex(不包括)之间的所有元素。
  • size():返回集合的元素个数
  • set(int index,E element):修改指定位置上的元素
  • toArray():按顺序返回包含此列表中所有元素的数组

3.2 LinkedList

3.2.1内部实现

内部实现为双向链表,删除和增加速度快,查找速度慢。

3.2.2关键属性

属性 取值或结论
是否允许重复 允许
是否有序 有序
是否允许为空 允许
是否线程安全
使用场景 多增删、少查询

3.2.3 测试示例

LinkedList当作FIFO的队列使用,也就是常用的add方法添加元素:

        LinkedList quene=new LinkedList();
        quene.add("a");
        quene.add("b");
        quene.add("c");
        quene.add("d");

        System.out.println("打印队列:"+quene);
        System.out.println("获取队头:"+quene.getFirst());
        System.out.println("获取队尾:"+quene.getLast());
        System.out.println("移除队头:"+quene.pop());
        System.out.println("移除队头之后的队列:"+quene);

打印结果:

打印队列:[a, b, c, d]
获取队头:a
获取队尾:d
移除队头:a
移除队头之后的队列:[b, c, d]

LinkedList当作FILO的栈使用:

        LinkedList stack = new LinkedList();

        stack.push("1");
        stack.push("2");
        stack.push("3");
        stack.push("4");

        System.out.println("打印栈:"+stack);

        System.out.println("获取栈顶元素:"+stack.peek());
        System.out.println("打印栈:"+stack);
        System.out.println("取出栈顶元素:"+stack.pop());
        System.out.println("打印栈:"+stack);

打印结果:

打印栈:[4, 3, 2, 1]
获取栈顶元素:4
打印栈:[4, 3, 2, 1]
取出栈顶元素:4
打印栈:[3, 2, 1]

除了ArrayList中包含的基本方法以为,LinkedList中多了getFirst()、getLast()、addFirst()、addLast()、peek()、peekFirst()、peekLast()、removeFirst()、removeLast()等方法。

3.2.4 转成线程安全

 List<Integer> linkedList=Collections.synchronizedList(new LinkedList<Integer>());

3.3 Vector

Vector内部是数组结构,线程安全,速度较慢,几乎不用。

四、Set集合

Set集合中的元素唯一、无序,没有角标。

4.1HashSet

4.1.1内部实现

内部结构是哈希表,对象存入HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,确保set中没有存储相同的对象。判断两个元素是否相同,首先通过hashCode()方法判断的是2个元素的哈希值是否相同,再根据equals()方法判断值是否相同,只有2者都相同才是统一元素。

4.1.2 基本属性

属性 取值或结论
是否允许重复 不允许
是否有序 无序
是否允许为空 允许(只有一个null)
是否线程安全
使用场景 对象不重复和需要快速查找的场景

4.1.3 测试示例

        HashSet<String> hashSet=new HashSet();
        hashSet.add("abc");
        hashSet.add("张三");
        hashSet.add("李四");
        hashSet.add("tim");
        hashSet.add(null);
        hashSet.add(null);

        System.out.println("HashSet 大小:"+hashSet.size());
        Iterator iter=hashSet.iterator();
        while (iter.hasNext()){
            System.out.println(iter.next());
        }

打印结果:

HashSet 大小:5
null
李四
张三
abc
tim

添加自定义对象,仍然添加Boy类中的对象

      Boy b1=new Boy("神乐",12);
      Boy b2=new Boy("神乐",12);
      HashSet<Boy> boys=new HashSet<>();
      boys.add(b1);
      boys.add(b2);
      System.out.println(boys);

结果:

[Boy{name='神乐', age=12}, Boy{name='神乐', age=12}]

这时候b1和b2其实是一个对象,在Boy类中重写hashCode()和equals()方法:

public class Boy {

    private String name;
    private int age;
    //省略setter、getter、构造器和toString()方法
        @Override
    public int hashCode() {

        return name.hashCode()+age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this==obj) return  true;
        if (!(obj instanceof Boy))  throw new ClassCastException("类型错误");
        Boy boy=(Boy) obj;

        return this.name.equals(boy.name)&&(this.age==boy.age);
    }
}

重写equals和hashCode方法以后,上述集合中就会只添加一个对象:

[Boy{name='神乐', age=12}]

4.1.4 转成线程安全

Set<String> hSet=Collections.synchronizedSet(new HashSet<String>());

4.1.5 唯一且有序

LinkedHashSet集合中都元素唯一且有序,这里都有序是指添加顺序。

        LinkedHashSet<String> lHashSet= new LinkedHashSet<String>();
        lHashSet.add("abc");
        lHashSet.add("张三");
        lHashSet.add("李四");
        lHashSet.add("tim");
        lHashSet.add(null);
        iter=lHashSet.iterator();
        while (iter.hasNext()){
            System.out.println(iter.next());
        }

打印结果:

abc
张三
李四
tim
null

4.2 TreeSet

4.2.1 内部实现

TreeSet内部实现为二叉树,可以对元素进行排序

4.2.2 基本属性

属性 取值或结论
是否允许重复 不允许
是否有序 无序
是否允许为空 允许(只有一个null)
是否线程安全
使用场景 去重且排序

4.2.3 测试示例


        Boy b1=new Boy("定春",16);
        Boy b2=new Boy("神乐",12);
        Boy b3=new Boy("桑巴",13);

        TreeSet<Boy> treeSet=new TreeSet<>(new Comparator<Boy>() {
            @Override
            public int compare(Boy o1, Boy o2) {
                return o1.getAge()-o2.getAge();
            }
        });

        treeSet.add(b1);
        treeSet.add(b2);
        treeSet.add(b3);
        System.out.println(treeSet);

打印结果:

[Boy{name='神乐', age=12}, Boy{name='桑巴', age=13}, Boy{name='定春', age=16}]

4.2.4 转成线程安全

        TreeSet<Boy> treeSet=new TreeSet<>(new Comparator<Boy>() {
            @Override
            public int compare(Boy o1, Boy o2) {
                return o1.getAge()-o2.getAge();
            }
        });

        Set<Boy> treeSet1=Collections.synchronizedSet(treeSet);

五、Map集合

Map只存储的是键值对,Key必须唯一且不为空,key允许为null。

5.1HashMap

5.1.1基本属性

属性 取值或结论
是否允许重复 key重复会被覆盖,value可重复
是否有序 无序
是否允许为空 key和value都允许为空
是否线程安全

5.1.2 测试示例

词频统计:

        String[] arr={"Hadoop","Lucene","ES","ES","ES","Hadoop","Java","JS"};
        //TreeMap<String,Integer> map=new TreeMap<>();
        HashMap<String,Integer> map=new HashMap<>();
        for (String str:arr){
            if (map.containsKey(str)){
                map.put(str,map.get(str)+1);
            }else{
                map.put(str,1);
            }
        }

        Set<String> keys=map.keySet();
        Iterator<String> iter=keys.iterator();
        while (iter.hasNext()){
            String str=iter.next();
            System.out.println(str+"---"+map.get(str));
        }

打印结果:

Java---1
Hadoop---2
JS---1
Lucene---1
ES---3

如果想按value排序,可以改成TreeMap();

六、性能对比

ArrayList遍历性能对比

ArrayList的遍历可以用一般for循环、foreach循环、Iterator迭代器三种方式实现,为了测试它们的性能,先创建一个ArrayList对象,添加5万个字符串:

ArrayList<String > a = new ArrayList();
for (int i = 0; i < 60000; i++) {
    a.add(""+i);
}

for 循环打印并记录耗时:

long start = System.currentTimeMillis();

for (int i = 0; i < a.size(); i++) {
     System.out.print(a.get(i));
}

long end = System.currentTimeMillis();

System.out.println("\n下标for循环:" + (end - start));

结果:

150

foreach循环打印并记录耗时:

 start = System.currentTimeMillis();

 for (String i : a) {
      System.out.print(i);
 }
 end = System.currentTimeMillis();

 System.out.println("\nforeach:" + (end - start));

耗时:

95

Iterator循环打印:

start=System.currentTimeMillis();
Iterator iter=a.iterator();
while (iter.hasNext()){
     System.out.print(iter.next());
}
end=System.currentTimeMillis();
System.out.println("\nIter:"+(end-start));

耗时:

60

结论:一般for 循环最慢、foreach次之、Iterator最快。

另外,Iterator遍历还有另外一种变形方式,效率和while形式一样。

for(Iterator it=a.iterator();it.hasNext();){
    System.out.print(it.next());
}

七、参考文章

有些文章总结的很好,列出来供参考学习:
Java集合框架

目录
相关文章
|
28天前
|
Java
【Java集合类面试二十八】、说一说TreeSet和HashSet的区别
HashSet基于哈希表实现,无序且可以有一个null元素;TreeSet基于红黑树实现,支持排序,不允许null元素。
|
4天前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
19天前
|
Java
用JAVA架建List集合为树形结构的代码方法
这段代码定义了一个表示树形结构的 `Node` 类和一个用于构建树形结构的 `TreeController`。`Node` 类包含基本属性如 `id`、`pid`、`name` 和 `type`,以及子节点列表 `children`。`TreeController` 包含初始化节点列表并将其转换为树形结构的方法。通过过滤和分组操作实现树形结构的构建。详情可见:[代码示例链接1](http://www.zidongmutanji.com/zsjx/43551.html),[代码效果参考链接2](https://www.257342.com/sitemap/post.html)。
28 5
|
19天前
|
存储 Java 程序员
Java中的集合框架:从入门到精通
【8月更文挑战第30天】在Java的世界里,集合框架是一块基石,它不仅承载着数据的存储和操作,还体现了面向对象编程的精髓。本篇文章将带你遨游Java集合框架的海洋,从基础概念到高级应用,一步步揭示它的奥秘。你将学会如何选择合适的集合类型,掌握集合的遍历技巧,以及理解集合框架背后的设计哲学。让我们一起探索这个强大工具,解锁数据结构的新视角。
|
20天前
|
存储 算法 Java
Java中的集合框架深度解析云上守护:云计算与网络安全的协同进化
【8月更文挑战第29天】在Java的世界中,集合框架是数据结构的代言人。它不仅让数据存储变得优雅而高效,还为程序员提供了一套丰富的工具箱。本文将带你深入理解集合框架的设计哲学,探索其背后的原理,并分享一些实用的使用技巧。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往高效编程的大门。
|
27天前
|
存储 算法 Java
Java 中的同步集合和并发集合
【8月更文挑战第22天】
21 5
|
28天前
|
存储 Java
【Java集合类面试二十九】、说一说HashSet的底层结构
HashSet的底层结构是基于HashMap实现的,使用一个初始容量为16和负载因子为0.75的HashMap,其中HashSet元素作为HashMap的key,而value是一个静态的PRESENT对象。
|
28天前
|
Java
【Java集合类面试三十】、BlockingQueue中有哪些方法,为什么这样设计?
BlockingQueue设计了四组不同行为方式的方法用于插入、移除和检查元素,以适应不同的业务场景,包括抛异常、返回特定值、阻塞等待和超时等待,以实现高效的线程间通信。
|
18天前
|
存储 算法 Java
Java中的集合框架深度解析与实践
【8月更文挑战第31天】在Java编程的海洋中,集合框架扮演着不可或缺的角色。本文将带你领略Java集合框架的魅力,从理论到实践,深入浅出地探索List、Set和Map等核心接口的使用技巧。我们将通过具体代码示例,展示如何在日常开发中高效运用这些工具,让你的代码更加优雅和高效。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往Java集合世界的大门。
|
18天前
|
存储 人工智能 Java
JAVA集合
【8月更文挑战第31天】