Java集合

集合概念

数组 ——>容器 存储一组数据

数组就是容器:同一类型,创建时指定容量,长度不变,在内存空间连续存储

不足:长度固定,不能不变

需求:程序在运行时,数据数量随时会发生变化,需要的存储结构也会有特殊需求

(增删多链表结构,查询多数组结构)

集合API

集合体系概述

Java的集合框架是由很多接口、抽象类、具体类组成的,都位于java.util包中
在这里插入图片描述

Collection接口

接口中常用的方法(单列集合共有的方法),定义了存取一组对象的方法,其子接口Set和List分别定义了存储方式。

●Set中的数据对象没有顺序且不可以重复。

●List中的数据对象有顺序且可以重复。

Java中集合类默认使用泛型,如果没有定义集合中存储的数据类型,默认数据类型为Object。建议使用泛型语法为集合指明数据类型,类型统一就不会出现转型问题(把类型当作参数传递)

import java.util.ArrayList;
import java.util.Collection;
public class CollectDemo1 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<String>();
        c.add("a");//添加元素
        c.add("b");
//        c.contains("a"); 是否包含此元素,若包含此元素输出返回true,反之为false
        System.out.println(c.contains("a"));
//        c.isEmpty();是否为空
        System.out.println(c.isEmpty());
//        c.size();集合长度
        System.out.println(c.size());
//        c.remove("a");删除指定元素(返回为boolean类型,true或false)
        System.out.println(c.remove("a"));
        c.clear();//删除集合中所有元素
        System.out.println(c);
    }
}

集合转数组

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class CollectionDemo2 {
    public static void main(String[] args) {
        Collection<String> c1 = new ArrayList<String>();
        c1.add("a");
        c1.add("b");
        Collection<String> c2 = new ArrayList<String>();
        c2.add("c");
        c2.add("d");
        c1.addAll(c2);
        System.out.println(c1);//把c2集合添加到c1集合
        System.out.println(c1.containsAll(c2));//c1里面是否包含c2元素,包含返回true
        System.out.println(c1.retainAll(c2));
        System.out.println(c1.retainAll(c2));//保留两个集合中的交集部分,内容改变返回true,不变返回false
        System.out.println(c1);
        //集合转数组
        Object[] o = c1.toArray();//默认Object
        System.out.println(Arrays.toString(o));
        String[] array = c1.toArray(new String[(c1.size())]);//明确类型
        System.out.println(Arrays.toString(array));
    }
}

List 接口及实现类

List接口继承自Collection接口,并且继承了Collection接口中的全部方法。
有三个实现类:

  • ArrayList

    数组列表,数据采用数组方式存储

    查询快;中间添加,删除 效率低

  • LinkedList

    链表 查询慢从头或者尾结点开始查询;中间删除,添加效率高

  • Vector

    数组列表,添加同步锁,线程安全的

ArrayList

实现了长度可变的数组,在内存中分配连续的空间

  • ArrayList c = new ArrayList();
    创建对象时不会创建数组,第一次添加时创建一个容量为10的数组
  • ArrayList c = new ArrayList(10);
    创建对象时,创建一个指定容量的数组
  • add方法(底层源码实现方法)及接口方法 具体方法(个别):
import java.util.ArrayList;

public class ArrayListDemo1 {
    public static void main(String[] args) {
        /*
        ArrayList<String> c = new ArrayList<String>();创建对象时不会创建数组,第一次添加时创建一个容量为10的数组
          ArrayList<String> c = new ArrayList<String>(10);创建对象时,创建一个指定容量的数组
          transient Object[] elementData; 底层存储数据的数组
          add方法(底层源码实现方法)
          add(E e)  向末尾添加元素进来;添加前判断元素添加进去后,数组能否放的下,如果可以,直接添加进去,如果不可以会创建一个数组(扩容)
        public boolean add(E e) {
        ensureCapacityInternal(size + 1);  //检测容量
        elementData[size++] = e;
        return true;
    }

       if (minCapacity - elementData.length > 0) //添加后的长度-数组长度>0
            grow(minCapacity);//底层数组扩容方法

             private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容为原来的1.5倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)//最大上限为int最大值-8( private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
       */
        ArrayList<String> c = new ArrayList<String>();
        c.add("e");
        c.add("b");
        c.add("c");
        c.add("d");
        c.add("a");
        c.add("a");
        c.add("a");
        c.add("a");
        c.add("a");
        c.add("a");
        c.add("a");
        System.out.println(c.indexOf("a"));//返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1
        System.out.println(c);
        System.out.println(c.get(3));
        System.out.println(c.contains("r"));//如果此列表包含指定的元素,则返回 true
        System.out.println(c.remove(1));//根据索引删除
        System.out.println(c);
        System.out.println(c.clone());//返回此 ArrayList实例的浅拷贝。
    }
}

LinkedList的常用方法

import java.util.LinkedList;

public class LinkedListDemo {

    public static void main(String[] args) {

        /*
        * LinkedList 存储重复元素,按照添加顺序排放
        *        E item; 数据
              Node<E> next;  后
              Node<E> prev;  前
        * */

        LinkedList<String> list = new LinkedList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.addFirst("x");
        list.addLast("x");
        list.add(2, "q");//在索引为2的位置上添加q
        list.removeFirst();//删除首位
        list.removeLast();//删除末尾
        list.getFirst();
        list.getLast();
        System.out.println(list.get(3));
        System.out.println(list);
    }
}

Vector

import java.util.Vector;

public class VectorDemo {

    public static void main(String[] args) {

        /*
         Vector 底层是数组实现
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
        */

        Vector<String> vector = new Vector<>();
        vector.add("a");
        vector.add("b");

    }
}

List接口集合迭代

  • for循环遍历
  • 增强for循环的遍历
  • 迭代器遍历(Iterator)
import java.util.ArrayList;
import java.util.Iterator;

public class ListDemo1 {

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>(10);
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("c");
        list.add("c");
        /*
          for循环遍历:支持在遍历的过程中删除集合中的元素,注意索引的变化
        */
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("c")){
                list.remove("c");
            }
            System.out.println(list.get(i));
        }

        /*
          增强for循环的遍历:不支持在遍历时删除元素,如果删除会抛出java.util.ConcurrentModificationException(并发修改异常)
           list.remove(item);  删除元素时会报错,break结束循环,可以继续向后执行
                break;
        */
        for (String item : list){
            if (item.equals("c")){
                list.remove(item);
                break;
            }
            System.out.println(item);
        }

        /*
           迭代器遍历(Iterator)
           调用Iterator(),返回一个迭代器对象
        */
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String s = iterator.next();
            if (s.equals("c")){
                iterator.remove();//删除当前指针指向的那个元素
            }
        }
        System.out.println(list);
    }
}

Set接口

Set接口和List接口同样继承自Collection接口,与Collection接口中的方法基本一致;Set中所存储的元素是不重复的,但是是无序的, Set中的元素是没有索引的

Set接口主要有两个实现类:HashSet和TreeSet

HashSet集合

HashSet类中的元素不能重复,即彼此调用equals方法比较,都返回false,并且元素都是无序的

底层数据结构是哈希表+链表

哈希表依赖于哈希值存储

import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo1 {

    public static void main(String[] args) {
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add("a");
        hashSet.add("b");
        hashSet.add("c");
        hashSet.add("d");
        hashSet.add("a");
//        hashSet.clear();//删除
        System.out.println(hashSet.contains("a"));

        /*
          set集合只能用增强for循环和迭代器遍历
           for (String s : hashSet){
            System.out.println(s);
        }
        */

        Iterator<String> iterator = hashSet.iterator();
        while (iterator.hasNext()){
            String string = iterator.next();
            System.out.println(iterator.next());
        }
    }
}

在向HashSet集合中添加元素时注意的问题(代码中体现):

import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo2 {

    public static void main(String[] args) {

        /*
          HashSet添加元素时,如何判断元素是否存在(也就是判断HashSet的键是如何判断重复)
          add(E) 在添加元素时,在里面要判断元素是否存在,解决两个问题:1.效率要高  2.安全
          add添加时,先用内容调用hashCode()方法计算出一个hash值(int类型),用哈希值比较是否相同
          但是只用hash值比较是不安全的,这时会调用equals方法对每个字符进行比较
        */
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add("a");
        hashSet.add("b");
        hashSet.add("c");
        hashSet.add("d");
        hashSet.add("a");
        System.out.println(hashSet);
    }
}

用一个案列进行演示如何将Person存入HashSet:

new Person() 在内存中创建一个对象,有一个内存地址:

public native int hashCode();
若Person类中没有重写Object类中hashCode(), 则默认调用的是Object中的,取出的是内存地址 ;

一般的类中都会重写Object类中的hashCode(), 重写后的计算方式都是根据对象中包含的内容的值来计算的,重写后添加时,调用自己类中的hashCode();

因此在向集合中存入元素时,为了保证HashSet正常工作,要在存入对象时重写Object类中的hashCode()和equals()方法。

import java.util.Objects;

public class Person {

    private int id;
    private String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        Person person = (Person) o;
        return id == person.id && name.equals(person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}
import java.util.HashSet;

public class HashSetDemo3 {

    public static void main(String[] args) {
        HashSet<Person> hashSet = new HashSet<Person>();
        Person p1 = new Person(1,"张三1");
        Person p2 = new Person(2,"张三2");
        Person p3 = new Person(3,"张三3");
        Person p4 = new Person(1,"张三1");
        Person p5 = new Person(5,"张三5");

        hashSet.add(p1);
        hashSet.add(p2);
        hashSet.add(p3);
        hashSet.add(p4);
        hashSet.add(p5);
        System.out.println(hashSet);
    }
}

TreeSet集合

内部采用平衡二叉树来存储元素,其结构可以保证TreeSet集合中没有重复的元素,且可以对元素进行排序。

import java.util.TreeSet;

public class TreeSetDemo1 {

    public static void main(String[] args) {

        /*
          TreeSet:不重复,可以根据元素进行排序
        */
        TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(3);
        treeSet.add(1);
        treeSet.add(2);
        treeSet.add(5);
        treeSet.add(5);
        System.out.println(treeSet);

当向TreeSet集合中存储我们自定义的类型数据时,如下面例子中的Car(),因为我们自己定义的类型数据没有实现Comparable接口,因此不能够直接在 TreeSet集合中进行排序操作。

public class Car implements Comparable<Car>{

    private int num;
    private String name;

    public Car(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Car{" +
                "num=" + num +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public int compareTo(Car o) {
//        return this.num - o.num;//使用num比较排序,
        return this.name.compareTo(o.name);//使用name比较排序
    }
}
import java.util.TreeSet;

public class TreeSetDemo {

    public static void main(String[] args) {

        TreeSet<Car> set = new TreeSet<>();
        Car car1 = new Car(1,"红旗H1");
        Car car2 = new Car(3,"红旗H3");
        Car car3 = new Car(2,"红旗H2");
        Car car4 = new Car(8,"红旗H8");
        Car car5 = new Car(1,"红旗H9");

        set.add(car1);
        set.add(car2);
        set.add(car3);
        set.add(car4);
        set.add(car5);
        System.out.println(set);

    }
}

Map接口

Map接口是一种双列集合,它的每一个元素都包含一个key(键对象)和Value(值对象)。Map中的映射关系是一对一的,即一个键对象(key)只能对应一个值对象(Value),键和值对象可以是任意数据类型。

Map接口中定义的一些方法:

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapDemo1 {
    public static void main(String[] args) {
        Map<String,String> map = new HashMap<String, String>();
        map.put("a", "aa");
        map.put("b", "bb");
        map.put("c", "cc");
        map.put("d", "dd");
        map.put("s", "ss");
        map.put("a", "aaa");
        System.out.println(map.containsKey("a"));//是否包含指定的键值
        System.out.println(map.containsValue("bb"));//是否包含指定的值
        System.out.println(map.size());
        System.out.println(map.get("cc"));//根据指定的键找到对应的值
        System.out.println(map.remove("s"));//删除指定的键,返回对应的值
        System.out.println(map.getOrDefault("a", "aa"));
        System.out.println(map.entrySet());
        System.out.println(map);

        Set<String> set = map.keySet();//获取map中键的那一列,存放到一个set中
        Collection<String> list = map.values();
        System.out.println(list);
    }
}

HashMap集合

HashMap集合是Map接口中的一个实现类,用于存储键值映射关系,在集合体系概述中的图解中有体现,HashMap中元素的key值不能重复,排列顺序是不固定的,可以存储一个为null的键;底层由哈希表结构组成,即“数组+链表”的组合体,数组为HashMap的主体结构,链表则是为解决哈希值冲突而存在的分支结构。

import java.util.HashMap;

public class HashMapDemo {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<String, String>();
        map.put("a", "aa");
        map.put("b", "bb");
        map.put("c", "cc");
        map.put("a", "dd");
        System.out.println(map);//输出:{a=dd, b=bb, c=cc}
        /*
          Map集合中的键具有唯一性,在向集合中添加已存在的键值时,会覆盖之前已存在的键值
        */
    }
}

HashMap集合的简单存储示意图:
在这里插入图片描述

TreeMap集合

TreeMap集合中不允许出现重复的键,内部是通过二叉树原理保证键的唯一性;

TreeMap中所有的元素按照某种顺序排列,如果需要得到一个有序 的Map就应该使用TreeMap,key值所在类必须实现Comparable接口。

import java.util.*;

public class TreeMapDemo {
    public static void main(String[] args) {
        TreeMap<String,String> map = new TreeMap<String, String>();
        map.put("a", "aa");
        map.put("c", "cc");
        map.put("b", "bb");
        System.out.println(map);//输出:{a=aa, b=bb, c=cc}
    }
}

HashTable集合

HashTable是Map接口的一个实现类,不能存储为null的键,HashTable是线程安全的,效率不及HashMap。

import java.util.*;

public class HashTableDemo {
    public static void main(String[] args) {
        Hashtable<String,String> map = new Hashtable<String,String>();
        map.put("a", "aa");
//        map.put(null, "bb");//不允许为键为空
        map.put("c", "cc");
        map.put("d", "dd");
        System.out.println(map);
    }
}

Map集合遍历

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapDemo2 {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<String, String>();
        map.put("a", "aa");
        map.put("b", "bb");
        map.put("c", "cc");
        map.put("d", "dd");
        map.put("s", "ss");
        map.put("a", "aaa");

        /*
          遍历方式一:
        */
        Set<String> keyset = map.keySet();
        for (String key : keyset){
            System.out.println(map.get(key));
        }

        /*
          遍历方式二:
          entrySet(); 把map中的键值对封装在一个Entry对象中,将多个Entry对象装进Set集合中.
          Entry对象包含键值
        */
        Set<Map.Entry<String,String>> entrySet = map.entrySet();
        for (Map.Entry entry : entrySet){
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
    }
}

Collections类

Collections是集合类的工具类,与数组的工具类Arrays类似.如将集合中的元素排序,从集合中查找某个元素等。
在这里插入图片描述

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsDemo {
    /*
	   addAll(Collection<? super T> c, T... elements)
	 */

    public static void main(String[] args) {
		  /* Collections.sort(list,new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				return o2-o1;
			}
		  });*/

        //System.out.println(Collections.binarySearch(list,4));

        //Collections.swap(list, 0, 3);


        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        List<Integer>  list1 = new ArrayList<>();
        list1.add(5);
        list1.add(6);
        list1.add(7);

        Collections.copy(list, list1);//将list1复制到list中去,从头开始复制,
        System.out.println(list);//输出为:[5, 6, 7, 4]

        List<Integer>  list2 = Collections.emptyList();//返回一个空集合,集合不能使用 ,避免在判断时候出现空指针

        System.out.println(Collections.max(list));
        System.out.println(Collections.min(list));

        //Collections.replaceAll(list, 2,6);//将集合中2替换为6,有几个2替换几个6
       /*
       Collections.fill(list, 5);//将list集合填充为5
        System.out.println(list);//输出:[5, 5, 5, 5]
        */

        Collections.reverse(list);//集合逆序操作
        System.out.println(list);

    }


    public static void test1(){
        test2(1,2,3,4,5);
    }
    //int...a  定义可变长度参数,本质是数组,一个参数列表中只能有一个,并且 放在参数列表的最后一位
    public static void test2(int b,int...a){

    }

}

泛型

  • 什么是泛型?为什么用泛型?

泛型即“参数化类型”。

参数化类型即指将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式,在调用或使用时传入具体的类型。

早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。

  • 泛型类

泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法。

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值