Java集合体系总结

来源:互联网 发布:淘宝店铺用什么软件好 编辑:程序博客网 时间:2024/05/22 14:31

一、集合框架

集合是容纳数据的容器,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----12Jack----11Mike----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 大小:5null李四张三abctim

添加自定义对象,仍然添加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张三李四timnull

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---1Hadoop---2JS---1Lucene---1ES---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集合框架

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 六个月宝宝粘人爱哭怎么办 摔伤结巴里面灌脓了怎么办 两周半的宝宝说话结巴怎么办 孩子两周说话结巴怎么办 名字取了生僻字考大学怎么办 淘宝客服一直不说话怎么办 淘宝客服不说话也不发货怎么办 面对不说话的客人怎么办? 卖家客服不回复怎么办 2周小孩说话结巴怎么办 微信群里只领红包不说话的人怎么办 躺在微信不说话的客户怎么办 9岁儿童说话结巴怎么办 5岁儿童说话结巴怎么办 京东客服不说话怎么办 两岁宝宝说话有点结巴怎么办 孩子不敢跟外人说话怎么办? 孩子不爱跟外人说话怎么办 别人说我不说话怎么办 衣服屁股那块变亮怎么办 小孩家里说话外面不说话怎么办 2个月小宝宝便秘怎么办 3个月小宝宝便秘怎么办 微信客户不说话怎么办 一岁半宝宝便秘肛裂怎么办 网恋现在都不说话了怎么办 宝宝便秘四天了怎么办 月子里小孩吐奶怎么办 月子里的小孩吐奶怎么办 婴儿吃多了吐奶怎么办 20个月孩子便秘怎么办 一岁宝宝肛裂怎么办 婴儿吃饱了吐奶怎么办 23天新生儿吐奶怎么办 婴儿吐奶舌苔白怎么办 宝宝吐奶酸臭味怎么办? 1周岁吐奶有酸味怎么办 十多天的宝宝吐奶怎么办 未满月婴儿吐奶怎么办 2个月宝宝溢奶怎么办 四岁宝宝说话结巴怎么办