Java入门6.1---Collection集合:Set、List、Queue

一、Collection介绍

Java集合可分为Collection和Map两种体系:

Collection接口:

  1. Set:元素无序、不可重复的集合;
  2. List:元素有序,可重复的集合;

Map接口:具有映射关系“key-value”的集合

1. 为什么需要Collection?

  1. Java是⼀⻔⾯向对象的语⾔,就免不了处理对象
  2. 为了⽅便操作多个对象,那么我们就得把这多个对象存储起来
  3. 想要存储多个对象(变量),很容易就能想到⼀个容器
  4. 常⽤的容器我们知道有-->StringBuffered,数组(虽然有对象数组,但是数组的⻓度是不可变的!)
  5. 所以,Java就为我们提供了集合(Collection)~

存储对象可以考虑:数组、集合

  长度 元素的数据类型
数组 固定 基本数据类型+引用数据类型
集合 可变 引用数据类型(存储的是简单的int,会自动装箱成Integer)

数组存储对象的弊端:

  1. 一旦创建,其长度不可变;
  2. 真实的数组存放的对象的个数是不可知。

2. Collection总览

1) 线程安全的集合:

Vector、HashTable、StringBuffer、ConcurrentHashMap、Stack

2)非线程安全的集合:

ArrayList、LinkedList、HashMap、HashSet、TreeMap、TreeSet、StringBulider、LinkedHashSet、LinkedHashMap

3) DEFAULT_INITIAL_CAPACITY,比如ArrayList(默认10)、Vector(默认10)、HashTable(默认为11)、StringBuilder(默认16)、StringBuffer(默认16)、HashMap(默认16)、HashSet(默认16)、XxxBlockingQueue(array的要手工指定,linked默认Integer.MAX_VALUE)等等。

3. Collection的功能

添加功能:

  1. boolean add(Object obj):添加一个元素;
  2. boolean addAll(Collection c):添加一个集合的元素;

删除功能:

  1. void Clear():移除所有的元素;
  2. boolean remove(Object obj):移除一个元素;
  3. boolean removeAll(Collection c):移除一个集合的元素,只要一个元素被移除了,就返回true;

判断功能:

  1. boolean contains(Object obj):判断集合是否包含该元素;
  2. boolean containsAll(Collection c):判断集合中是否包含指定的集合元素,只有包含所有的元素,才叫包含;
  3. boolean isEmpty():判断集合是否为空;

获取功能:

  1. Interator<E> iterator():迭代器

长度功能:

  1. int size():元素的个数;

交集功能:

  1. boolean retainAll(Collection c):移除此collection中未包含在指定collection中的所有元素。集合A和集合B做交集,最终的结果保存在集合A,返回值表示的是A是否发生过变化。

举例:

package 集合;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * ArrayList的常用方法
 *
 * @author nxf
 * @since 2020-07-06
 */
public class TestCollection {
    @Test
    public void testCollection1(){
        Collection col1 = new ArrayList();
        // 1.size:返回集合中元素的个数
        System.out.println(col1.size());
        // 2.add(Object obj):向集合中添加一个元素
        col1.add(123);
        col1.add("AA");
        col1.add(new Date());
        col1.add("BB");
        System.out.println(col1.size());
        // 3.addAll(Collection col1);
        Collection col2 = Arrays.asList(1,2,3);
        col1.addAll(col2);
        System.out.println(col1);
        System.out.println(col1.size());
        // 4.isEmpty():判断集合是否为空
        System.out.println(col1.isEmpty());
        // 5.clear():清空集合元素
        col1.clear();
        System.out.println(col1);
        System.out.println(col1.isEmpty());
    }

    @Test
    public void testCollection2(){
        Collection col1 = new ArrayList();
        col1.add(123);
        col1.add("AA");
        col1.add(new Date());
        col1.add(new Person(25,"nxf"));
        col1.add(new String("nxf"));
        System.out.println(col1); // [123, AA, Mon Jul 06 15:10:37 CST 2020, 集合.TestCollection$Person@1b2a4, nxf]
        // 6.contains(Object obj):判断集合中是否包含指定的obj元素,如果包含,返回true,否则,返回false
        // 判断的依据:根据元素所在的类的equals()方法进行判断
        // 明确:如果存入集合中的元素是自定义类的对象,要求:自定义类要重写equals()方法
        System.out.println(col1.contains("Aa")); // false
        boolean b1 = col1.contains(new Person(25,"nxf")); // Person类重写了equals方法
        System.out.println(b1); // true
        boolean b2 = col1.contains(new String("nxf")); // String重写了equals方法
        System.out.println(b2); // true
        // 7. containsAll(Collection col1)
        Collection col2 = new ArrayList();
        col2.add(123);
        col2.add(new String("AA"));
        boolean b3 = col1.containsAll(col2);
        System.out.println(b3); // true
        // 8.retainAll(Collection col1):求当前集合与col1的共有的元素,返回给当前集合
        col1.retainAll(col2);
        System.out.println(col1); // [123, AA]
        // 9.remove(Object obj):删除集合中的obj元素,若删除成功,返回true,否则,返回false
        boolean b4 = col2.remove("AA");
        System.out.println(col2); // [123]
        // 10.removeAll(Collection clo1):从当前集合中删除包含在col1中的元素
        col1.removeAll(col2);
        System.out.println(col1); // [AA]
        // 11.equals(Object obj):判断集合中的所有元素是否完全相同
        Collection coll2 = new ArrayList();
        coll2.add(123);
        coll2.add(new String("AA1"));
        System.out.println(col2.equals(coll2)); // false
        // 12.hashCode():
        System.out.println(col1.hashCode()); // 2111
        // 13.toArray():集合转化为数组
        Object[] obj = col1.toArray();
        for (int i=0;i<obj.length;i++){
            System.out.println(obj[i]); // AA
        }
        // 14.asList():数组转化为集合
        Collection col3 = Arrays.asList(1,2,3,4);
        System.out.println(col3); // [1, 2, 3, 4]
        // 15.iterator():返回一个Iterator接口实现类的对象,进而实现集合的遍历
        Iterator iterator = col1.iterator();
        // 方式1:不用
        for (int i=0;i<col1.size();i++){
            System.out.println(iterator.next());
        }
        // 方式2:使用
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
} public class Person { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(age, name); } public Person(int age, String name) { this.age = age; this.name = name; } } }

举例:集合的遍历

初始化:

1、Arrays.asList
ArrayList<Type> obj = new ArrayList<Type>(Arrays.asList(Object o1, Object o2, Object o3, ....so on));

2、生成匿名内部内进行初始化
ArrayList<T> obj = new ArrayList<T>() {{
    add(Object o1);
    add(Object o2);
    ...
    ...
}};

3、常规方式
ArrayList<T> obj = new ArrayList<T>();
obj.add("o1");
obj.add("o2");
...

或者

ArrayList<T> obj = new ArrayList<T>();
List list = Arrays.asList("o1","o2",...);
obj.addAll(list);

4、Collections.ncopies
ArrayList<T> obj = new ArrayList<T>(Collections.nCopies(count,element));//把element复制count次填入ArrayList中

4. 迭代器(Iterator)

Collection的源码中继承了Iterable,Iterable是⼀个接口,它有iterator()这个方法,返回的是Iterator也是⼀个接口,它只有三个方法:

  1. hasNext()
  2. next()
  3. remove()

可是,我们没能找到对应的实现方法,只能往Collection的子类下找找了,于是我们找到了--->ArrayList,于是,我们在ArrayList下找到了iterator实现的身影:它是在ArrayList以内部类的方式实现的!并且,从源码可知:Iterator实际上就是在遍历集合。

 

所以说:我们遍历集合(Collection)的元素都可以使用Iterator,至于它的具体实现是以内部类的方式实现的!

二、List集合

注意:添加进List集合中的元素(或对象)所在的类一定要重写equals()方法。

1. 特点

  1. 实现了Collection接口;
  2. List接口特性:是有序的,元素是可重复的;
  3. 允许元素为null。

2. 方法

List集合里添加了一些根据索引来操作集合元素的方法:

List常用方法:

  1. 增:add(Object obj)
  2. 删:remove
  3. 改:set(int index,Object obj)
  4. 查:get(int index)
  5. 插:add(int index,Object ele)
  6. 长度:size()

3. ListIterator

Collection返回的是Iterator迭代器接口,而List中又有它自己对应的实现-->ListIterator该接口比普通的Iterator接口多了几个方法:

从方法名就可以知道:ListIterator可以往前遍历,添加元素,设置元素。

  底层数据结构 是否线程安全 是否允许元素为null值 使用场景
ArrayList 数组 主要的实现类
LinkedList 链表 频繁的插入、删除
Vector 数组 效率慢,古老的实现类,插入删除慢, 线程安全

4. 实现List接口的类

4.1 ArrayList(主要实现类)

  1. 底层结构是数组,初始容量为10,每次增长1.5倍;
  2. 在增删时候,需要数据的拷贝复制(navite方法由C/C++实现);
  3. 线程非同步

  

ArrayList底层其实就是一个数组,ArrayList中有扩容这么一个概念,正因为它扩容,所以它能够实现“动态”增长。

(1)构造方法

(2)Add()方法

(3)add(E e)

步骤:

  1. 检查是否需要扩容;
  2. 插入元素;
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

(4)为什么ArrayList不是线程安全的,怎么解决?

为什么说ArrayList是线程不安全的?

解决ArrayList线程不安全_ 木木不-CSDN博客

4.2 Vector

Vector是jdk1.2的类了,比较老旧的⼀个集合类。

  1. 底层结构是数组,初始容量为10,每次增长2倍;
  2. 它是线程同步的,已被ArrayList替代。

用ArrayList代替Vector:

  1. 在要求⾮同步的情况下,我们⼀般都是使用ArrayList来替代Vector的。
  2. 如果想要ArrayList实现同步,可以使⽤Collections的方法: List list =Collections.synchronizedList(new ArrayList(...)); // 就可以实现同步了

4.3 LinkedList

  1. 底层结构是双向链表
  2. 实现了Deque接口,一次我们可以像操作栈和队列一样操作它;
  3. 线程非同步

从结构上,我们还看到了LinkedList实现了Deque接⼝,因此,我们可以操作LinkedList像操作队列和栈⼀样。

(1) 构造方法

有两个构造方法

4.4 CopyOnWriterArrayList

  1. 原理:在修改时,复制出一个新数组,修改的操作在新数组中完成,最后将新数组交由array变量指向;
  2. 写加锁,读不加锁
  3. 缺点:CopyOnWriter容器只能保证数据的最终一致性不能保证数据的实时一致性
  4. 适合在读多写少的场景下使用。

三、Set集合

注意:要求添加进Set中元素所在的类,一定要重写equals()和hashCode()方法,进而保证Set中元素的不可重复性。

1. 特点

  1. 实现了Collection接口
  2. Set接口特性:无序的,元素不可重复
    1. 无序性 != 随机性,真正的无序性指的是底层存储的位置是无序的。
    2. 不可重复性:当向set中添加进相同的元素的时候,后面的这个不能添加进去。
  3. 底层大多数是Map结构的实现;
  4. 常用的三个子类都是非同步的。
  5. Set使用的方法基本上都是Collection接口下定义的。

Set中的元素是如何存储的呢?使用了哈希算法。(hashCode()==>equals())

  当向set中添加对象时,首先调用该对象所在类的hashCode()方法。计算此对象的哈希值,此哈希值决定了此对象在set中的存储位置,若此位置之前没有对象存储,则这个对象直接存到此位置;若此位置已有对象存储,再通过equals()方法来比较这两个对象是否相同。如果相同,后一个对象就不能再加进来。万一返回false,都存储(不建议如此)。要求:hashCode()方法要与equals()方法一致。

2. Set的常用方法

Set中常用的方法都是Collection下定义的

  底层数据结构 是否线程安全 是否允许有null值 适用场景
HashSet 哈希表(一个元素为链表的数组)+红黑树    
TreeSet 红黑树(一个自平衡的二叉树)   保证元素的排序方式
LinkedHashSet 哈希表+双向链表   频繁的遍历,较少的添加、插入操作

3. 实现Set接口的

3.1 HashSet(主要实现类)

  1. 底层数据结构是哈希表(是一个元素为链表的数组)+红黑树
  2. 实际上就是封装了HashMap
  3. 元素无序,可以为null

3.2 LinkedHashSet

  1. 底层数据结构由哈希表(是一个元素为链表的数组)和双向链表组成,使用链表维护了一个添加进集合中的顺序,导致当我们遍历LinkedHashSet集合元素时,是按照添加进去的顺序遍历的。
  2. 父类是HashSet
  3. 实际上就是LinkHashMap
  4. 元素可以为null
  5. LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。 

3.3 TreeSet

  1. 底层实际上是一个TreeMap实例(红黑树);
  2. 可以实现排序的功能;
  3. 元素不能为null
  4. 向TreeSet中添加的元素必须是同一个类的可以按照添加进集合中的元素的指定的顺序遍历。像String、包装类等默认按照从小到大的顺序遍历。
  5. 当自定义类没有实现Comparable接口时,当向TreeSet中添加自定义对象时,报ClassCastException异常。
  6. 当向TreeSet中添加自定义类的对象时,依据此方法,确定按照哪个属性排列。

(1)自然排序

  1. 要求添加进Set集合中的元素所在的类一定要实现java.lang.Comparable接口;
  2. 重写其CompareTo(Object o)方法,在此方法中,指明按照自定义类的哪个属性进行排序;
  3. 向TreeSet中添加元素即可,若不实现此接口,则会报运行时异常;
  4. 首先按照compareTo()进行比较,一旦返回0,虽然仅是两个对象的此属性值相同,但是程序会认为这两个对象是相同的,进而后一个对象就不能添加进来。
  5. 要求重写的compareTo()与hashCode()和equals()三者保持一致。
  改进后

 (2)定制排序 

  1. 创建一个实现Comparetor接口的实现类的对象,在实现类中重写Comparator的compare(Object o1,Object o2)方法;
  2. 在此compare()方法中指明按照元素所在类的哪个属性进行排序;
  3. 将此实现Comparetor接口的实现类的对象作为形参传递给TreeSet的构造器中;
  4. 向TreeSet中添加元素即可,若不实现此接口,则会报运行时异常。
  5. 要求重写的compare()与hashCode()和equals()三者保持一致。
  改进后

TreeSet中添加元素即可,若不实现此接口,则会报运行时异常;

四、Queue

五、易错点

1.Arrays.asList(arr)

1. 以下代码正确的是:(B)
A.
String[] arr = new String[]{"1", "2", "3", "4", "5"};
List<String> list = Arrays.asList(arr);
list.remove("5");
list.add("6");
B.
Map<String, String> map = new HashMap<>();
map.put("1", "a");
map.put("2", "b");
List<String> list = new ArrayList<>();
list.addAll(map.values());
list.addAll(Collections.singleton("c"));
C.
List<String> list = Collections.emptyList();
list.add("1");
D.
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
ArrayList<String> subList = (ArrayList<String>) list.subList(0, 0);

解析:
A:Exception in thread "main" java.lang.UnsupportedOperationException
C:Exception in thread "main" java.lang.UnsupportedOperationException
D:Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList

  

 

 

 

 

参考链接:

【1】Java ArrayList 不为人知的陷阱,及add(int index, E element)和set(int index, E element)两个方法的说明_请叫我大师兄-CSDN博客

【2】Java Collections.nCopies(int n, T o) 的作用 -- 创建一个包含n个重复元素o的集合_请叫我大师兄-CSDN博客

posted @ 2020-07-02 16:46  nxf_rabbit75  阅读(362)  评论(0编辑  收藏  举报