java学习笔记之--集合

集合:

我们在编程时通常需要存放多个数据,而数组必须是固定的长度满足不了我们的需求,而且数组没办法存放具有映射关系的数据也就是两个对象,因此我们引入集合来解决这个问题。

  • 集合中分为三大接口:
    Collection、Map、Iterator
  • 集合框架的接口和类在java.util包中
    这里写图片描述

Collection接口:用于存储单个对象的集合

  • Collection层次结构中的根接口。Collection表示一组对象,这些对象也叫collection的元素。一些collection允许有重复的元素,而另一些则不允许。一些collection是有序的,而另一些则是无序的。JDK不提供此接口的任何直接实现:它提供更具体的子接口(如Set和List)实现。此接口通常用来传递collection,并在需要最大普遍性的地方操作这些collection
    接口定义:
    public interface Collection
    extends Iterable

List接口

  • public interface List extends Collection
    有序的collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制,用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
  • 允许多个null元素
  • 具体的实现类常用的:ArrayList,Vector,LinkedList

ArrayList

  • public class ArrayList extends AbstractList
  • 实现原理:采用动态对象数组实现,默认构造方法创建了一个空数组
  • 第一次添加元素,扩展容量为10,之后的扩充算法:原来数组大小+原来数组一半
  • 不适合进行删除或插入操作
  • 为了防止数组动态扩充次数过多,建议创建ArrayList时,给定初始容量
  • 线程不安全,适合在单线程访问时使用效率较高
  • 我们应该在一个集合中存储相同的类型对象 List<数据类型>
  • List接口的大小是用可变数组来实现的,实现了所有可选列表操作,并允许包括null在内的所有元素,除了实现List接口外,此类还提供一些方法来操作内部用来存储列表的数组大小

Vector

  • 实现原理:采用动态对象数组实现,默认构造方法创建了一个大小为10的对象数组
  • 扩充的算法:当增量为0时,扩充为原来大小的2倍,当增量大于0时,扩充为原来大小+增量
  • 不适合删除或插入操作
  • 为了防止数组动态扩充次数过多,建议创建Vector时,给定初始容量
  • 线程安全,适合在多线程访问时使用,在单线程下使用效率低

LinkedList

  • public class LinkedList extends AbstractSequentialList
    List接口的链接列表实现,实现了所有可选列表操作,并允许包括null在内的所有元素,除了实现List接口外,此类还为在列表的开头及结尾get、remove和insert元素提供了统一的命名方法
  • 实现原理:采用双向链表结构实现
  • 适合插入、删除操作,性能高
  • 在实际开发中,我们如何选择list的具体实现?
    (1)安全性问题
    (2)是否频繁插入,删除操作(LinkedList)
    (3)是否是存储后遍

大概展示一下list接口的代码(这几个实现类的代码都大同小异,包含的方法也差不多,所以这里就演示一下ArrayList和Vector):

public class ListDemo {
    private static void arrayList(){
        List<String> list=new ArrayList<>();
        list.add("苍老师");
        list.add("李老师");
        list.add("王老师");
        list.add("毕老师");
        list.add("毕老师");
        int size=list.size();
        for(int i=0;i<size;i++){
            System.out.println(list.get(i));
        }

        String[] array=list.toArray(new String[]{});
        for(String s:array){
            System.out.println(s);
        }
    }
    private static void vector(){
        Vector<String> v=new Vector<>();
        v.add("苍老师");
        v.add("李老师");
        v.add("王老师");
        v.add("毕老师");
        v.add("苍老师");
        int size=v.size();
        for(int i=0;i<size;i++){
            System.out.println(v.get(i));
        }

       String[] array=v .toArray(new String[]{});
       for(String s:array){
          System.out.println(s);
      }

    }
    public static void main(String[] args){
        arrayList();
        vector();//linkedList
    }
}

Set接口

  • public interface Set extends Collection
    一个不包含重复元素的collection,更确切地说,set不包含满足e1.equals(e2)的元素对e1和e2,并且最多包含一个null元素。正如其名称所暗示的,此接口模仿了数学上的set抽象
  • 它是无序的
  • 不允许重复元素

HashSet

  • 类实现Set接口,由哈希表(实际上是一个HashMap实例)支持,它不保证set的迭代顺序;特别是它不保证该顺序恒久不变,此类允许使用null元素
  • 实现原理:基于哈希表(HashMap)实现
  • 不允许重复,可以有一个NULL元素,重复的元素不会添加
  • 不保证顺序恒久不变
  • 添加元素时把元素作为HashMap的key存储,HashMap的value使用一个固定的object对象
  • 排除重复元素是通过equals来检查对象是否相同
  • HashSet判断重复值(判断两个对象是否相等)的原理:
    (1)判断两个对象的hashCode是否相等
    如果不相等,认为两个对象也不相等,结束;如果相等,转入2
    (2)判断两个对象用equals运算是否相等
    如果不相等,认为两个对象也不相等;如果相等,认为两个对象相等
    (equals()是判断两个对象是否相等的关键)
    注:哈希表–数组+链表+二叉树(红黑树)的实现(相同位置以链表方式存储)

TreeSet

  • 基于TreeMap的NavigableSet实现。使用元素的自然顺序对元素进行排序,或者根据创建set时提供的Comparator进行排序,具体取决于使用的构造方法
  • 有序的,基于TreeMap(二叉树数据结构),对象需要比较大小,通过对象比较器来实现,对象比较器还可以去除重复元素,如果自定义的数据类,没有实现比较器接口,将无法添加到TreeSet集合中

LinkedHashSet

  • 具有可预知迭代顺序的Set接口的哈希表和链接列表实现,与HashSet的不同之处在于后者维护一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到set中的顺序(插入顺序)进行迭代。注意:插入顺序不受在set中重新插入的元素的影响(如果在s.contains(e)返回true后立即调用s.add(e),则元素e会被重新插入到set s中

Iterator接口:集合迭代器

  • 遍历集合的方式有:Iterator、ListIterator(List的迭代器)、Enumeration(Vector)、foreach
  • 四大核心函数式接口(通常用lambda表达式) JDK1.8新特性:
    Consumer、Function

Stream

  • 高级版本的Iterator,不是存储数据结构,数据源可以是一个集合,为了函数式编程创造,惰式执行,数据只能被消费一次
    两种类型的操作方法:
    (1)中间操作(生成一个Stream)(2)结束操作(执行计算操作)

set接口的代码展示:

这里是自定义了一个类,所以需要重写方法:

public class Cat implements Comparable<Cat> {
    private String name;
    private int age;
    private int id;

    @Override
    public int compareTo(Cat o) {
        return this.id-o.id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Cat cat = (Cat) o;
        return age == cat.age &&
                id == cat.id &&
                Objects.equals(name, cat.name);
    }
   @Override
   public int hashCode() {

      return Objects.hash(name, age, id);
    }

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

public class CatComparator implements Comparator<Cat> {
    @Override
    public int compare(Cat o1, Cat o2) {
        return o1.getAge()-o2.getAge();
    }
}


然后这是具体使用:

public class SetDemo {
    public static void main(String[] args) {
        hashSet();
        treeSet();
    }
    public static void hashSet() {
        Cat c1 = new Cat("miaomiao", 5, 1);
        Cat c2 = new Cat("huahua", 4, 3);
        Cat c3 = new Cat("shasha", 3, 2);
        Cat c4 = new Cat("miaomiao", 5, 1);
        Set<Cat> cats = new HashSet<>();//自定义对象
        cats.add(c1);
        cats.add(c2);
        cats.add(c3);
        cats.add(c4);
        System.out.println(cats.size());
        for (Cat c : cats) {
            System.out.println(c);//重写了hashCode和equals方法 没有重写hashCode的话,哈希值不同;重写了hashCode没重写equals的话还是可以添加成功
        }
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4.hashCode());//c1和c4的哈希值相同
        System.out.println(c1 == c4);

        String v1 = "苍老师";
        String v2 = "白老师";
        String v3 = "薛老师";
        String v4 = "苍老师";

        Set<String> v = new HashSet<>();
        v.add(v1);
        v.add(v2);
        v.add(v3);
        v.add(v4);
        //  v.add("苍老师");
        //   for (String s : v) {
        //      System.out.println(s.hashCode());
        //   }
        System.out.println(v1.hashCode());
        System.out.println(v2.hashCode());
        System.out.println(v3.hashCode());
        System.out.println(v4.hashCode());//哈希值相同
        System.out.println(v1 == v4);//地址值相同
    }
    private static void treeSet(){
        TreeSet<Cat> tree=new TreeSet<>(new CatComparator());//括号里是Comparator<Cat> comparator
        Cat c1=new Cat("miaomiao",5,1);
        Cat c2=new Cat("huahua",7,2);
        Cat c3=new Cat("xiaoxiao",7,8);
        Cat c4=new Cat("miaomiao",5,1);
        tree.add(c1);
        tree.add(c2);
        tree.add(c3);
        tree.add(c4);
        for(Cat cats:tree){
            System.out.println(cats);
        }
    }

Map接口

  • 将键映射到值的对象,一个映射不能包含重复的键;每个键最多只能映射到一个值 Map

HashMap

  • 基于哈希表的Map接口的实现,提供所有可选的映射操作,并允许使用null值和null键(除了非同步和允许使用null之外,HashMap类与Hashtable大致相同)此类不保证映射的顺序,特别是不保证该顺序恒久不变
  • 默认加载因子为0.75(存储的元素所占数组百分比)默认数组大小为16
  • 把对象存储到哈希表中,如何存储?
  • 把key对象通过hash()方法计算hash值,然后用这个hash值对数组长度取余数来决定该对key对象在数组中存储的位置,当这个位置有多个对象时,以链表结构存储,1.8后,当链表长度大于8时,链表将转换为红黑树结构存储(为了取值更快)
  • 扩充算法:当前数组容量<<1(扩大一倍),扩充次数过多,会影响性能,每次扩充表示哈希表重新散列(重新计算每个对象的存储位置)
  • 线程不安全,适合单线程

Hashtable

  • 任何非null对象都可以用作键或值,为了成功地在哈希表中存储和获取对象,用作键的对象必须实现hashCode方法和equals方法
  • 基于哈希表默认数组大小为11,加载因子为0.75
  • 扩充方式:原数组大小<<1(*2)+1
  • 线程是安全的,用在多线程访问时

LinkedHashMap

  • 是HashMap的子类,它具有可预知的迭代顺序,使用一个双重链表

TreeMap

  • 基于红黑树实现,根据其键的自然顺序进行排序,或根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法
  • 若是自定义对象时,要实现Comparable接口重写compareTo方法(和TreeSet一样,不能添加重复元素)
    注:put会覆盖掉原来的value,可以使用putIfAbsent方法只会添加不存在相同key的值;remove是根据键和值都匹配时删除

Set是基于Map实现的,所以方法也差不多,我们来看一下代码:

public class MapDemo {
    private static void hashMap(){
        Map<Integer ,String> map=new HashMap<>();
        map.put(1,"Tom");
        map.put(2,"Jack");
        map.put(3,"Lisa");
        map.put(4,"Bin");
        System.out.println("size="+map.size());
        map.get(1);//通过key取value

        String value=map.getOrDefault(5,"null");//看key值存不存在
        System.out.println(value);

        //map的遍历
        Set<Map.Entry<Integer,String>> entrySet=map.entrySet();
        for(Map.Entry e:entrySet){
            System.out.println(e.getKey()+"->"+e.getValue());
        }
       //遍历键
        Set<Integer> keys=map.keySet();
        for(Integer i:keys){
            String value1=map.get(i);
            System.out.println(i+"->"+value1);
        }
        //遍历值
        Collection<String> values=map.values();
        for(String value2:values){
            System.out.println(value2);
        }
        map.forEach((key,value3)->System.out.println(key+"->"+value3));
    }
    private static void treeMap(){
        Map<Cat,String> cats=new TreeMap<>();
        cats.put(new Cat("2ha",1,1),"cat1");
        cats.put(new Cat("hhh",3,3),"cat2");
        cats.put(new Cat("www",2,2),"cat3");
        cats.forEach((key,value)->System.out.println(key+"->"+value));
    }
    public static void main(String[] args){
        hashMap();
       // treeMap();
    }
}

Collections工具类

  • 提供了大量针对Collection/Map的操作,总体可分为四类,都为静态方法
  • 排序操作(主要针对List接口相关)
  • 查找和替换(主要针对Collection接口相关)
  • 同步控制–该方法返回指定集合对象对应的同步对象,从而解决多线程并发访问集合时线程的安全问题(注意:在使用迭代方法遍历集合时需要手工同步返回的集合)
  • 设置不可变集合
    Optionalr容器类:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象(不用判断返回值是否为空)

猜你喜欢

转载自blog.csdn.net/wangaiji/article/details/81257727
今日推荐