JAVA集合

一、集合简介

1.什么是集合

集合collection,是一个数据容器,类似于数组,但是集合是可变的,数组是不可变的,集合可随时对里面的内容进行增添和删除操作,另外集合中也提供了若干的方法来操作集合中的数组。

集合里的数据,我们称之为元素(elements);集合只能用来存储引用数据类型数据,不能存数八大基本数据类型的数据。

2.泛型的引入

Java SE 5.0以前,集合的元素只要是Object类型就行,那个时候任何对象都可以存放在集合内,但是从集合中获取对象后,需要进行正确的强制类型转换。但是,Java SE 5.0 以后,可以使用新特性”泛型”,用来指定要存放在集合中的对象类型。避免了强制转换的麻烦。

public static void main(String[] args){
   ArrayList  list = new ArrayList();
   list.add(new Person());
   list.add(5);
   list.add("helloworld");
   
   Object obj = list.get(0);
   Person  p = (Person)obj;
   String name = p.getName();
   System.out.println("name:"+name);
}

3.集合结构

       在JAVA中每一个不同的集合,底层都对应着不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构中,不同的数据结构,数据存储方式不同。

3.1单列集合Collection

        List中的数据元素可以重复 :ArrayList/LinkedList

        Set中的元素不可重复:HashSet/TreeSet

List特点:此处的顺序并不是大小顺序,而是存入数据的先后顺序,有序是因为List集合都有下标,下表从0 开始递增。

Set:取出顺序和存入顺序不一致,Set集合没有下标

ArrayList是非线程安全的。

HashSet集合在new的时候,底层实际上new了一个hashMap集合。向HashSet集合中存储元素,实际上存储到了HashMap的key中。HashMap集合是一个Hash表数据结构。

SortedSet集合存储元素的特点:由于继承了Set集合,所以他的特点也是无序不可重复,但是放在SortedSet集合中的元素可以自动排序。放到该集合中的元素是自动按照大小顺序排序的。

TreeSet集合底层实际上是TreeMap。TreeSet集合在new的时候,底层实际上new了一个TreeMap集合。向TreeSet集合中存储元素,实际上是存储到了TreeMap的key中了。TreeMap集合是一个二叉树数据结构

3.2双列集合Map:HashMap、TreeMap

二、Collection接口

Collection接口是List,Set 和 Queue 接口的父接口,改接口定义了他们三个子接口的共同方法。即可以用于操作Set集合,也可以用于操作 List 和 Queue 集合。

1.常用方法

Collection集合的常用方法
boolean add(E e)添加元素到集合的末尾(追加)
boolean remove(Object o)删除指定元素,成功则返回true(底层调用equles)
void clear()清空集合
boolean contains(Object o)判断元素是否在集合中,存在返回true(底层调用equles)
boolean isEmpty()判断集合是否为空,空则返回true
int size()返回集合中的元素个数
iterator()获取集合的迭代器
toArray()获取集合数组
addAll(Collention c)将一个集合添加到当前集合后面(并集)
removeAll(Collection c)获得集合的差集
containsAll(Collection c)获取当前集合的指定元素
boolean retainAll(Collection<?> c)仅保留此Collection里的集合
        Collection collection = new ArrayList();
        collection.add("tom");
        collection.add("lucy");
        collection.add("hello");
        collection.add("hello");
        collection.add("lisa");
        collection.add("john");

        //判断tom是否在集合中
        System.out.println(collection.contains("tom"));//true
        //删除集合中的元素hello,删除的是从前往后数的第一个匹配到的
        System.out.println(collection.remove("hello"));
        System.out.println(collection);

        //返回集合中的元素个数
        System.out.println(collection.size());
        //创建一个新集合,并添加元素
        Collection collection1 = new ArrayList<>();
        collection1.add("lihua");
        collection1.add("xiaohui");
        collection1.add("tim");

        //判断两个集合是否相同(比较的是集合中的元素)
        System.out.println(collection.equals(collection1));

        //将collection1集合中的所有元素添加到collection集合后面
        System.out.println(collection.addAll(collection1));

        //获取集合数组,数组元素用Arrays.toString()输出
        System.out.println(Arrays.toString(collection.toArray()));

        //获取集合的迭代器
        System.out.println(collection.iterator());
        System.out.println(collection1.iterator());

        //判断集合是否为空
        System.out.println(collection.isEmpty());

        //判断参数集合Collection1中的元素是否都在当前集合Collection中包含
        System.out.println(collection.retainAll(collection1));

        //将Collection1中的元素从Collection中删除,将所有元素删除后集合为Null,
        System.out.println(collection.removeAll(collection1));
        System.out.println(collection);

2.集合的迭代

* 1.就是指对集合中的元素进行遍历。
* 2.几乎所有的集合子类型都实现了Iterator接口,所以可以使用迭代器来遍历集合。
* 3.Iterator接口中定义了三个方法:
*  -boolean hasNext():判断是否有下一个元素,可以形象的认为有指针指向下一个元素
*  -E next():获取下一个元素,返回指针指向的元素
*  -void remove():删除当前元素。删除的是指针指向的元素。
*
*  注意:使用迭代器遍历集合时,不能使用集合中(自己)的方法来添加元素,否则会抛出异常。ConcurrentModificationException

2.1增强for循环

在进行集合的遍历的时候,基本都与Iterable有关。此接口提供了对集合进行迭代的方法,只有实现了Iterable接口,在能使用增强for循环进行遍历。

//使用增强for循环实现集合的遍历
for (Object ele : collection){
    System.out.println(ele);
}

2.2迭代器

迭代器Iterator,是一个接口, Collection集合中有一个方法 iterator() 可以获取这个接口的实现类 对象。在这个迭代器中,维护了一个引用,指向集合中的某一个元素。默认指向一个集合前不存在的元 素,可以认为是下标为-1的元素。

迭代器的工作原理:循环调用 next() 方法进行向后的元素指向,并返回新的指向的元素。同时,在向后进行遍历的过程中,使用 hasNext() 判断是否还有下一个元素可以迭代。

在迭代器使用的过程中,需要注意:

  • 不允许对集合中的元素进行修改

  • 不允许对集合的长度进行修改

 //获取迭代器对象,输出集合中的元素
Iterator<String> iterator = collection.iterator();
String element = null;
while (iterator.hasNext()){
    //让迭代器向后指向一位,并返回
    element = iterator.next();
    System.out.println(element);
}

3.Queue子接口

简介: 

  • 队列Queue也是Collection的一个子接口,它也是常用的数据结构,可以将队列看成特殊的线性表,队列限制对线性表的访问方式:只能从一端添加(offer)元素,从另一端取出(poll)元素

  • 队列遵循先进先出(FIFO first Input First Output)的原则

  • 实现类LinkedList也实现了该接口,选择此类实现Queue的原因在于Queue经常要进行添加和删除操作,而LinkedList在这方面效率比较高。

  • 其主要方法如下:

    方法解析
    boolean offer(E e)作用:将一个对象添加到队尾,如果添加成功返回true
    E poll()作用:从队首删除并返回这个元素
    E peek()作用:查看队首的元素
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();
        //add 和 offer都能添加元素
        queue.add("qin");
        queue.offer("a");
        queue.offer("b");
        queue.offer("c");
        queue.offer("d");
        queue.add("e");
        System.out.println(queue);
        String string = null;
        
        while (!queue.isEmpty()){
            //从队首删除并返回元素
            string = queue.poll();
            System.out.print(string);//遵循先进先出
            //查看队首元素
            System.out.println(queue.peek());
            /*
                qina
                ab
                bc
                cd
                de
                enull
             */
        }
        System.out.println(queue);
    }    

Deque接口 

 Deque是Queue的子接口,定义了所谓的双端队列,即从队列的两端分别可以入队(offer、add)和出队(poll)。同样,LinkedList实现了该接口。

 如果将Deque限制为只能一段入队和出队,则可以实现“栈”(Stack)的数据结构,对于栈而言,入栈被称为push,出栈被称为pop。遵循先进后出原则。

 * 提供的方法:

 *     offerFirst        :从头部插入元素

 *     offerLast         :从尾部插入元素

 *     pollFirst        :从头部移除元素 

 *     pollLast        :从尾部移除元素

    public static void main(String[] args) {
        Deque<String> deque = new LinkedList<>();
        deque.add("q");
        deque.offer("w");
        deque.offer("e");
        deque.offer("r");
        deque.add("t");
        deque.offer("y");
        System.out.println(deque);

        //在集合尾部插入元素
        deque.offerLast("he");
        System.out.println(deque);

        //在集合头部插入元素
        deque.offerFirst("oh");
        System.out.println(deque);

        //删除集合头部元素并输出
        deque.poll();
        System.out.println(deque);

        //删除集合尾部元素并输出
        deque.pollLast();
        System.out.println(deque);

        //从头部出栈
        deque.pop();
        System.out.println(deque);
    }

2.List子接口

2.1List接口特点

 * 1. 元素有序,并且可以重复。

 * 2. 两个实现类:ArrayList和LinkedList

 *      - ArrayList:动态数组+各种方法

 *      - LinkedList: 双向链表+各种方法

 * 3. 两个实现类的效率:(元素特别多的情况下)

 *      -  对于随机访问来说, ArrayList要高于LinkedList

 *      -  对于频繁的插入和删除来说, LinkedList要高于ArrayList.

 *

 *  4. 基于List接口,两个实现类的方法基本一致。

  • Stack:是一个古老的集合, JDK1.0版本时候出现,模拟栈结构存储数据。

  • Vector:是一个古老的集合, JDK1.0版本时候出现,现在已经被ArrayList和LinkedList替代

2.2Vector

1、底层也是一个数组。
2、初始化容量:10
3、怎么扩容的?
    扩容之后是原容量的2倍。
    10--> 20 --> 40 --> 80
4、Vector中所有的方法都是线程同步的,都带有synchronized关键字,
是线程安全的。效率比较低,使用较少了。
5、怎么将一个线程不安全的ArrayList集合转换成线程安全的呢?
    使用集合工具类:
        java.util.Collections;
        java.util.Collection 是集合接口。
        java.util.Collections 是集合工具类。
Collections.synchronizedList();//将及格转换为线程安全的。

 2.3常用方法

LinkedList\ArrayList构造和添加方法
public ArrayList<E>()创造一个空集合
public boolean add(E e)将指定的参数元素追加到集合的末尾
public void add(int index ,E e)在集合的指定位置添加元素(插入元素)
public void addAll(E object)将指定集合中的所有元素添加到当前集合中
piblic addAll(int index ,Collection c)将集合中的所有元素添加到当前集合的指定位置
LinkedList\ArrayList集合常用方法
public boolean remove(Object o)删除指定元素,成功则返回true
public E remove(int index)删除指定索引位置上的元素,返回被删除的元素
public set(int index,E e)修改指定位置上的元素,返回修改前的元素
public E get(int index)获取指定索引位置上的元素
public int size()获取集合中元素个数
int indexOf(Object obj)返回指定索引元素第一次出现的位置,没有返回-1
int lastIndexOf(Object obj)返回指定索引元素最后一次出现的位置,没有返回-1
List subList(int fromIndex,int toIndex)截取元素,返回指定区间内的元素,[fromIndex,toIndex)
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        //添加元素
        list.add("w");
        list.add("e");
        list.add("l");
        list.add("c");
        list.add("o");
        list.add("m");
        list.add("e");
        System.out.println(list);
        List<String> list1 = new ArrayList<>();
        list1.add("来");
        list1.add("到");
        list1.add("中国");
        System.out.println(list1);

        //将list1中的元素添加到list中
        list.addAll(list1);
        System.out.println(list);

        //删除元素-按下标删除
        list1.remove(1);
        System.out.println(list1);
        //删除元素-按元素删除,删除的是从头到尾查询到的第一个元素
        //要想全部删除可以遍历集合
        list.remove("e");
        System.out.println(list);

        //修改元素,修改之前的元素可以返回
        System.out.println(list.set(1, "o"));
        System.out.println(list);

        //获取元素
        System.out.println(list1.get(0));

        //截取元素
        System.out.println(list.subList(1,5));

        //返回指定索引第一次出现的位置
        System.out.println(list.indexOf("o"));

        //返回指定索引最后一次出现的位置
        System.out.println(list.lastIndexOf("o"));
    }

 2.4 ListIterator

ListIterator是Iterator接口的子接口,继承到了Iterator中的所有方法,同时自己也添加了若干个方法。允许使用ListIterator在进行元素迭代的时候,对集合中的数据进行修改,或者对集合的长度进行修改。同时,使用ListIterator还可以进行倒序的迭代。

注意:

在进行迭代的过程中,允许修改集合。但是要注意的是,这个修改集合,只能通过接口中的方法进行,并不能通过集合中的方法进行修改。

// 3、在迭代的过程中,修改集合
while (iterator.hasNext()) {
   // 向后指向一位,并返回当前指向的元素
   String ele = iterator.next();
   if (ele.equals("Vertu")) {
      // 在迭代的过程中,删除这个元素
      iterator.remove();
      // 在迭代的过程中,添加一个元素(加在当前迭代的这个元素的后面)
      iterator.add("HTC");
      // 在迭代的过程中,对当前迭代的元素进行修改
      iterator.set("Nokia");
   }
}

3.Set子接口

3.1简介

1.Set集合中的元素是无序的(取出顺序与存入顺序无关)

2.Set集合中的元素不能重复

3.2实现类

1).HashSet
  • HashSet是Set接口的典型实现,大多数时候使用Set集合都会使用这个类。
  • HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。

HashSet 具有以下特点:

  •         不能保证元素的排列顺序
  •         HashSet不是线程安全的
  •         集合元素可以是null

当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。

如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。

2).LinkedHashSet
  • LinkedHashSet是HashSet的子类
  • LinkedHashSet集合根据元素的HashCode值来决定元素的存储位置,旦它同时使用链表维护元素的次序,这使得元素看起来是插入顺序保存的。
  • LinkedHashSet性能插入性能略低于HashSet,但在迭代访问Set 里的全部元素有很好的性能
  • LinkedHashSet 不允许集合元素重复
3).TreeSet

TreeSet是SortedSet 接口的实现类,TreeSet集合是用来对元素进行排序的,同样也可以保证元素的唯一性。TreeSet可以确保集合元素处于排序状态。

TreeSet支持两种排序方法 : 自然排序和定制排序。默认情况,采用自然排序。

4.List排序

4.1Comparable接口 

如果集合里的元素想要排序,那么元素对象之间一定要有大小之分。

如果界定大小???

此时元素必须要是comparable 接口的实现类,该接口提供了方法

        int comparaTo(T t). 规范了其子集是可以进行比较的,因此子类必须重写该抽象方法

比较规则:(默认升序)

  • 如当前对象大于给定对象,那么返回值应为>0的整数

  • 若小于给定对象,那么返回值应为<0的整数

  • 若两个对象相等,则应返回0

4.2 工具类提供的排序方法

Collections是集合的工具类,其中排序的方法有sort方法。

  •         static void sort(List<T> list) : 对指定集合的元素进行自然排序。前提是元素类型必须实现Comparable接口

4.3 Comparator比较器接口 

java类一旦定义好,就不要轻易再去修改它。因此当java类实现了Comparable接口,也就代表比较规则已经确定.

但是,有的时候我们想临时改变一下比较规则,怎么办呢?

此时我们可以采用Comparator接口回调的方式。它提供了一个抽象方法:

  • int compare(T o1 , T o2)

比较规则如下

  • 若o1>02, 则返回值应该>0

  • 若o1<o2,则返回值应该<0

  • 若o1==o2, 则返回值应该为0

工具类中提供了sort方法的重载方法

  • static void sort(List<T> list , Comparator c)

作用:使用比较器c,指定临时排序规则

4.4 Collections 

Collections 是一个操作 Set、List 和 Map 等集合的工具类,提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

Collections工具类提供的方法
排序操作
reverse(List)反转List中元素的顺序
shuffle(List)对List集合元素进行随机排序
sort(List,Comparator)根据指定的Comparator产生顺序对List集合元素排序
sort(List)根据元素的自然顺序对指定几何元素按升序排序
swap(List ,i ,j)将指定List集合中的 i 出元素和 j 处元素进行交换
查找替换
Object max(Collection)根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator)根据comparator指定的顺序,返回集合中的最大元素
Object min(Collection)
Object max(Collection,Comparator)
int frequency(Collection,Object)返回指定集合中指定元素的出现次数
boolean replaceAll(List list,Object old,Object new)替换list中的元素,new 换 old
    public static void main(String[] args) {
        List<String> list = new LinkedList<>();
        list.add("a");
        list.add("e");
        list.add("r");
        list.add("t");
        list.add("d");
        list.add("y");
        list.add("k");
        list.add("m");
        System.out.println(list);
        //对集合中的元素进行排序

        // 使用匿名内部类创建 Comparator, 重写比较器
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s2.compareTo(s1);
            }
        });
        System.out.println("排序后: " + list);

        //对集合进行随机排序
        Collections.shuffle(list);
        System.out.println("随机排序后: " + list);

        //对集合进行排序
        Collections.sort(list);
        System.out.println("排序后: " + list);

        //反转List中的元素的顺序
        Collections.reverse(list);
        System.out.println("反转后: " + list);

        //对集合中的指定元素进行交换
        Collections.swap(list,1,4);
        System.out.println("交换后: " + list);

        //返回集合中最大的元素
        System.out.println("最大的为:" + Collections.max(list));

        //返回集合中最小的元素
        System.out.println("最小的为:" + Collections.min(list));

        //替换集合中的元素
        Collections.replaceAll(list,"a","A");
        System.out.println("替换后: " + list);

 三、Map接口

1.Map简介

Map是集合框架中的另一个父接口,它用来保存具有映射(一对一)关系的数据,这样的数据称之为键值对(Key-Value-Pair)。key可以看成是value的索引。特点如下:

key和value必须是引用数据类型

作为key,在Map集合中不允许重复

key可以为null

key和value之间存在一对一关系,通过指定的key总能找到唯一确定的value

根据内部数据结构的不同,Map接口有多种实现类,其中常用的有内部为hash表实现的

HashMap和内部为排序二叉树实现的TreeMap 。 

2.常用方法

Map的基本方法
V put(K key,V value)设置键值对
V remove(Object key)删除元素
void clear()清空集合
boolean containsKey(Object key)判断键是否存在,存在返回true
boolean containsValue(Object value)判断值是否存在,存在返回true
boolean isEmpty()判断简直关系是否为空,空则返回true
boolean equals(Object o)比较指定的对象与此映射是否相等
int hashCode()返回此映射的哈希值
int size()返回映射中的键-值映射关系数
putIfAbsent("age","30")当键值对中不存在此关系时才会增加,否则没有任何效果
replace()修改键值对中的值

enterSet()

返回此映射中包含的键-值对的 Set 视图

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        //设置键值对,添加元素
        map.put("name", "tom");
        map.put("age", "18");
        map.put("gender", "male");

        System.out.println(map);
        //删除元素,当键存在时,返回true,错误返回false
        System.out.println(map.remove("name", "lucy"));

        //返回此映射的哈希值,哈希值用于存储和查询元素
        System.out.println(map.hashCode());
//        System.out.println(map);

        //返回键值对的数量
        System.out.println(map.size());

        //判断键是否存在
        System.out.println(map.containsKey("age"));

        //判断值是否存在
        System.out.println(map.containsValue("18"));

        //判断键值对关系是否为空
        System.out.println(map.isEmpty());

        //清空映射关系
        map.clear();
        System.out.println(map);

3.Map的遍历

3.1 遍历所有的key

- Set<K> keySet();

- 该方法会将当前Map中的所有key存入一个Set集合后返回

3.2 遍历所有的key-value

- Set<Entry<K,V>> entrySet()

- 该方法会将当前Map中的每一组key-value封装成Entry对象存入Set集合后返回

3.3 遍历所有的value(不常用)

- Collection<V> values

4.HashMap的实现原理

4.1原理

HashMap的底层主要是基于数组和链表来实现的,他之所以有相当快的查询速度,主要是因为它是通过计算散列码来决定存储位置。HashMap中主要是通过key的HashCode来计算Hash值,只要HashCode相同,计算出来的Hash值就是一样的。如果存储的对象太多,就有可能不同的对象算出来的Hash值是相同的,这就是所谓的Hash冲突。

图中,紫色部分即代表哈希表,也称为哈希数组,数组的每个元素都是一个单链表的头节点,链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中

4.2装载因子及其HashMap优化

capacity:容量,hash表里bucket(桶)的数量,也就是散列数组大小

initial capacity:初始容量,创建hash表时,初始bucket的数量,默认构建容量为16,也可以使用特定容量。

size:大小,当前散列表中存储数据的数量

load factor:加载银子,默认值0.75也就是75%,当向散列表增加数据时,如果size/capacity的值大于loadfactor,则发生扩容并且重新散列(rebash)

性能优化:加载因子较小时,散列查找性能会提高,但是也浪费了散列桶空间容量。0.75是性能和空间相对平衡结果。在创建散列表时指定合理容量,减少rehash能提供性能

 5. HashMap 与 HashTable

HashMap特点:

1、无序,不可重复。

2、放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
    所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。
3、HashMap集合的默认初始化容量是16,默认加载因子是0.75
    这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组以二叉树开始扩容。

HashMap 和 Hashtable 是 Map 接口的两个典型实现类

区别:

  • Hashtable 是一个古老的 Map 实现类,不建议使用

  • Hashtable 是一个线程安全的 Map 实现,但 HashMap 是线程不安全的。

  • Hashtable 不允许使用 null 作为 key 和 value,而 HashMap 可以

与 HashSet 集合不能保证元素的顺序的顺序一样,Hashtable 、HashMap 也不能保证其中 key-value 对的顺序

6.LinkedHashMap

LinkedHashMap 是 HashMap 的子类

LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致

7.TreeMap

TreeMap 存储 Key-Value对时,需要根据 Key 对 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于有序状态。

TreeMap 的 Key 的排序:

  • 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException

  • 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口

8.Properties 

Properties 类是 Hashtable 的子类,该对象用于处理属性文件

由于属性文件里的 key、value 都是字符串类型,所以 properties 里的 Key 和 Value 都是字符串类型的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值