Java集合

集合分为三大接口:
Collection,Map,Iterator

Collection

用于存储单个对象的集合,其下有两个子接口:
List,Set

List接口

  1. 有序接口
  2. 允许有多个null元素,元素可以重复
  3. 具体的实现有常用的:ArrayList,Vector,LinkedList

ArrayList

采用动态数组实现,默认构造方法是创建一个空数组
添加时每次都扩充一个单位
不适合进行删除和插入
为了防止数组动态扩充次数过多,可以在创建时给定初始容量
线程不安全,适合在单线程访问时使用,效率较高

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


public class ListDemo {

    public static void main(String[] args) {
        arrayList();
    }
    private static void arrayList() {
        
        List list = new ArrayList();//可以使用集合来存储多个不同类型的元素(对象)
        List<String> list1 = new ArrayList<String>();////可以使用泛型指定对象类型
        
        //数组添加
        list.add("笑一笑,十年少");
        list.add(123);
        
        list1.add("飞驰人生");
        list1.add("流浪地球");
        //list1.add(123);//报错
        
        int size = list1.size();
        for(int i=0;i<size;i++) {
            System.out.println(list.get(i));
            System.out.println(list1.get(i));
        }
        
        //判断是否包含
        System.out.println(list1.contains("飞驰人生"));
        //删除
        list1.remove("飞驰人生");
        System.out.println(list1.size());
        //转成数组
        String[] array = list1.toArray(new String[] {});
        for(String s: array) {
            System.out.println(s);
        }
    }
}

Vector

采用动态数组实现,默认构造方法是创建一个容量为10数组
添加时每次都扩充一个单位
不适合进行删除和插入
为了防止数组动态扩充次数过多,可以在创建时给定初始容量
线程安全,适合在多线程访问时使用,单效率较低 (区别)

private static void vector() {
        Vector<String> v = new Vector<String>();
        v.add("昨夜风疏雨");
        v.add("浓睡不消残酒");
        
        for(int i=0;i<v.size();i++) {
            System.out.println(v.get(i));
        }
    }

LinkedList

采用双向链表的数据结构
适合插入,删除操作,性能高

private static void linkedList() {
        LinkedList<String> ll = new LinkedList<String>();
        ll.add("试问卷帘人");
        ll.add("却道海棠花依旧");
        for(int i=0;i<ll.size();i++) {
            System.out.println(ll.get(i));
        }
    }

Set接口

  1. 无序的
  2. 不允许元素重复
  3. 具体的实现类有:HashSet,TreeSet,LinkedHashSet

HashSet

实现原理:哈希表
不允许重复(通过equals检查是否相同),可以有一个null元素
不保证顺序恒久不变
添加元素把元素作为HashMap的key存储,HashMap的value使用一个固定的Object对象

private static void hashSet() {
        Set<String> set = new HashSet<String>();
        set.add("知否,");
        set.add("知否,");//相同会自动替换掉上面的
        set.add("知否 ");
        set.add("应是绿肥红瘦");
        
        String[] names = set.toArray(new String[] {});
        for(String s:names) {
            System.out.print(s);
        }
    }

hashCode
是一个定义在object类中的本地方法,它的实现与本地机器相关
判断两个对象是否相等,首先是判断两个对象的hashCode是否相等,如果相等,进一步进行equals比较,都相同则是同一个对象
若要求自定义对象的属性值一致时认为是同一个对象,则可以重写所在类的hashCodeequals方法

@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((sex == null) ? 0 : sex.hashCode());
        return result;
    }

@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Dog other = (Dog) obj;
        if (age != other.age)
            return false;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (sex == null) {
            if (other.sex != null)
                return false;
        } else if (!sex.equals(other.sex))
            return false;
        return true;
    }

TreeSet

TreeSet是有序的,基于二叉树数据结构,对象需要有比较器
对象比较器还可以用来去除重复元素

private static void treeSet() {
        Set<String> tree = new TreeSet<String>();
        tree.add("任侠平生愿");
        tree.add("一叶边舟莲波滟");
        
        //若是自定义的类,需要实现comparator接口(对象比较器),因为treeSet实现二叉树有序排列
        Set<Cat> catTree = new TreeSet<Cat>(new CatComparator());

        Cat c1 = new Cat(1,"喵喵");
        Cat c2 = new Cat(2,"旺旺");
        Cat c3 = new Cat(0,"嘟嘟");
        //Cat c4 = new Cat(1,"Tom");//因为age属性相同,执行add方法不会被加入(去除重复元素)
        catTree.add(c1);
        catTree.add(c2);
        catTree.add(c3);
        System.out.println(catTree.size());
    }

LinkedHashSet

根据添加顺序排序,相同元素添加会改变原有顺序

private static void linkedHashSet() {
        Set<Cat> linkHash = new LinkedHashSet()<Cat>;
        Cat c1 = new Cat(1,"喵喵");
        Cat c2 = new Cat(2,"旺旺");
        Cat c3 = new Cat(0,"嘟嘟");
        linkHash.add(c1);
        linkHash.add(c2);
        linkHash.add(c3);
        System.out.println(catTree.size());
    }

Iterator(迭代器)

用于遍历集合

三种迭代方式

//foreach迭代
    private static void foreach(Collection<Cat> c) {
        for(Cat cat :c) {
            System.out.println(cat);
        }
    }
    
    //Iterator
    private static void iterator(Collection<Cat> c) {
        Iterator<Cat> iter = c.iterator();
        while(iter.hasNext()) {
            System.out.println(iter.next());
        }
    }
    
    private static void enumration() {
        Vector<String> vs = new Vector<String>();
        vs.add("tom");
        vs.add("jack");
        vs.add("job");
        vs.add("lily");
        
        Enumeration<String> es = vs.elements();
        while(es.hasMoreElements()) {
            System.out.println(es.nextElement());
        }
    }

foreach接口

JDK1.8实现了forEach方法

private static void foreach() {
        List<String> list = new ArrayList<String>();
        list.add("tom");
        list.add("jack");
        list.add("job");
        list.add("lily");
        
        //多种输出方式
        list.forEach(s->System.out.println(s));
        list.forEach((String s)->{System.out.println(s);});
        list.forEach(s->{System.out.println(s);});
        list.forEach(System.out::print);
        
    }

四大核心接口

Consumer<T> 消费者接口
Function<T,R> 表示接受一个参数并产生结果的函数
Supplier<T> 代表结果供应商
Predicater<T> 断言接口

private static void functionTest() {
    String s = strToUpp("abcdefg",(str)->str.toUpperCase());
    System.out.println(s);
}
    
private static String strToUpp(String str,Function<String, String> f) {
    return f.apply(str);
}
    
public static void supplierTest() {
    List<Integer> list = getNums(10,()->(int)(Math.random()*100));
    list.forEach(System.out::println);
}
    
public static List<Integer> getNums(int num,Supplier<Integer> sup){
    List<Integer> list  = new ArrayList<Integer>();
        
    for(int i=0;i<num;i++) {
        list.add(sup.get());
    }
    
    return list;
}
    
public static void predicateTest() {
    List<String> list = Arrays.asList("AEG","MAC","Landom","eesg");
    List<String> result = filter(list,(s)->s.contains("a"));
    result.forEach(System.out::println);
}
    
public static List<String> filter(List<String> list,Predicate<String> p){
    List<String> results = new ArrayList<String>();
        
    for(String s: list) {
        if(p.test(s)) {
            //测试是否包含
            results.add(s);
        }
    }
    
    return results;
}

Stream接口

Stream接口的数据源是一个集合,为了函数式编程创造,惰式执行,数据只能被消费一次(使用一次)

两种操作类型:

  1. 中间操作(生成一个Stream),可以再调用别的Stream接口的方法
  2. 结束操作(执行计算操作)
package Collection;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamDemo {

    public static void main(String[] args) {
        
        //of方法用于生成Stream
        Stream<String> stream = Stream.of("good","good","study","day","day","up");
        
        //foreach方法(结束操作)
        stream.forEach((str)->System.out.println(str));
        stream.forEach(System.out::println);
        
        //过滤filter方法(中间操作)
        stream.filter((s)->s.length()>3).forEach(System.out::println);
    
        //去除重复值
        stream.distinct().forEach(System.out::println);
        
        //映射map(把流全部映射后再进行操作)
        stream.map(s->s.toUpperCase()).forEach(System.out::println);
        
        //平摊flatMap(对于一个流中有不同的元素,每个元素有不同数量的集合,通过该方法把所有集合整合在一个流中)
        Stream<List<Integer>> ss = Stream.of(Arrays.asList(1,2,3),Arrays.asList(4,5));
        ss.flatMap(list->list.stream()).forEach(s->System.out.println(s));

        //reduce
        Optional<String> opt = stream.reduce((s1,s2)->s1.length()>=s2.length()?s1:s2);
        System.out.println(opt.get());
        
        //collect 
        List<String> list = stream.collect(Collectors.toList());
        list.forEach(s->System.err.println(s));
    }
}

关于::的用处

  • 引用静态方法
  • 引用对象的方法
  • 引用构造方法

Map接口

  1. 键值对存储一组对象
  2. key要不能重复(唯一)
  3. value可以重复
    具体的实现类有:HashMap,TreeMap,LinkedHashMap

HashMap

package Collection;

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

public class MapDemo {

    public static void main(String[] args) {
        hashMap();
    }
    private static void hashMap() {
        Map<Integer, String> map = new HashMap<>();
        //复制
        map.put(1,"Tom");
        map.put(2,"Jack");
        map.put(3,"Zip");
        
        //从map中取值
        System.out.println(map.get(1));//通过key取value
        
        //map遍历
        Set<Entry<Integer, String>> entrySet = map.entrySet();
        for(Entry e:entrySet) {
            System.out.println(e.getKey()+"->"+e.getValue());
        }
        
        //第二种遍历方式:通过遍历键拿值
        Set<Integer> keys = map.keySet();
        for(Integer i:keys) {
            String value = map.get(i);
            System.out.println(i+"->"+value);
        }
        
        //第三种遍历方式(遍历值)
        Collection<String> values = map.values();
        for(String value:values) {
            System.out.println(value);
        }
        
        //第四种:foreach
        map.forEach((key,value)->System.out.println(key+"->"+value));

        //判断是否包含
        System.out.println(map.containsKey(6));
    }
}

HashMap实现原理

  1. 基于哈希表(数组+链表+二叉树(红黑树))
  2. 默认加载因子为0.75,表示当数组的数据利用率达到75%就进行空间扩充,默认数组大小是16
  3. 把key对象通过hash()方法计算hash值,然后用hash值对数组长度取余数(默认16),来决定该key对象在数组中的存储位置,当该位置有多个对象时,以链表方式存储,当链表长度大于8时,链表转换为红黑树结构存储(提高性能)
  4. 当数组容量超过75%时,数据扩充一倍(左移1),扩充次数过多会影响性能(每次扩充哈希表会重新计算每个对象的存储位置),在开发中尽量减少扩充
  5. 线程不安全,适合单线程中使用

hashTable

  1. 基于哈希表实现
  2. 默认数组大小为11,加载因子为0.75
  3. 扩充方法为原数组左移1,再+1
  4. 线程安全,适合多线程使用
private static void hashtable() {
    Map<String, String> table = new Hashtable<String, String>();
    table.put("one", "Chinese");
    table.put("two","English");
    table.put("three","American");
        
    table.forEach((key,value)->System.out.println(key+"->"+value));
}

LinkedHashMap

此类是HashMap的子类,由于HashMap不能保证顺序恒久不变,此类使用一个双重链表来维护元素的添加顺序

private static void linkedHashMap() {
    Map<String, String> link = new LinkedHashMap<String, String>();
    link.put("one", "Chinese");
    link.put("two","English");
    link.put("three","American");
        
    link.forEach((key,value)->System.out.println(key+"->"+value));
}

TreeMap

基于红黑树实现,该映射根据键的顺序进行排序,对于自定义对象,根据Comparator进行排序,具体用法同上

Map接口jdk1.8新特性

Map接口中新增加了一些默认方法

//如果没有该值返回指定值
String name = map.getOrDefault(4, null);
        
//如果空才添加,put方法放回老的value,putIfAbsent返回新的或者已存在的
map.putIfAbsent(3, "xixi");
        
//删除,key和value都匹配时才能删除
map.remove(1, "vince");
        
//替换
map.replace(1, "aaa");//如果key不为空,替换为指定的value
map.replace(1, "jack", "youoyou");//键值对匹配才替换新的
        
//执行函数操作
map.compute(1,(k,v)->v+"1");//jack1
map.computeIfAbsent(2,(val)->val+"test");//为空就执行后面的函数
        
//合并
map.merge(1, "888", (oldVal,newVal)->oldVal.concat(newVal));//jack888

Collections工具类

该工具类提供了大量针对Collection/Map的操作,总体可以分为四类,都是静态方法

  1. 排序操作(主要针对List接口)
//反转
Collections.reverse(list);
//随机排序
Collections.shuffle(list);
//根据自然升序排序
Collections.sort(list);
//自定义比较器排序
Collections.sort(list,c);
//交换位置
Collections.swap(list, 0, 2);
//所有元素向右移动指定长度
Collections.rotate(list, 2);
  1. 查找和替换(针对Collection相关的接口)
//采用二分查找法,搜索返回键值(前提是已排序)
Collections.binarySearch(list, "tom");
//求最大最小值
Collections.max(list);
Collections.min(list);
//使用指定对象填充(全部变成指定对象)
Collections.fill(list, "duola");
//返回指定值出现次数
Collections.frequency(list, "lily");
//替换(替换所有跟指定值相同的值)
Collections.replaceAll(list, "old", "new");
  1. 同步控制

提供多个synchronizedXXX方法,该方法返回指定集合对应的同步对象,针对HashSet,ArrayList,HashMap等线程不安全的同步问题
在使用迭代方法遍历集合时需要手工同步返回的集合

  1. 设置不可变的集合
  • Collections.emptyXXX():返回一个空的不可变的xxx
  • Collections.singletonXXX():返回一个只包含指定对象的,不可变的集合对象
  • unmodifiableXXX():返回指定集合对象的不可变视图

Optional容器类

一个可以为null的容器对象,如果值存在isPresent方法会返回true,调用get方法会返回该对象

package collection;

import java.util.Optional;

public class OptionalDemo {
    public static void main(String[] args){

        Optional<String> optional1 = Optional.of("family");//创建一个非null的Optional
        Optional<String> optional2 = Optional.ofNullable("house");//为指定的值创建一个Optional,如果指定值为空返回一个空的Optional
        System.out.println(optional1.isPresent());//如果值存在返回true,否则返回false
        System.out.println(optional2.get());//如果值存在返回该值,否则抛出NoSuchElementException
        optional1.ifPresent((value)->System.out.println(value.toUpperCase()));//如果Optional实例有值则为其调用consumer,否则不作处理
        System.out.println(optional2.orElse("无值"));//如果有值就返回,否则返回指定的其他值
        optional2.orElseGet(()->"default");//与orElse方法相似,区别在于该方法可以接受Supplier接口的实现用来生成默认值
        Optional<String> optional3 = Optional.ofNullable(null);
        try {
            optional3.orElseThrow(Exception::new);//如果有值将其返回,否则抛出supplier异常
        } catch (Exception e) {
            e.printStackTrace();
        }
        Optional<String> optional4 = optional1.map((value)->value.toUpperCase());//如果有值,执行mapping函数返回值,如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional
        System.out.println(optional4.orElse("没有值"));
        Optional<String> optional5 = optional1.flatMap((value)->Optional.of(value.toUpperCase()));
        System.out.println(optional5.orElse("没有值"));//与map类似,但该方法不会将返回值封装成Optional封装
        optional4 = optional4.filter((value)->value.length()>3);//如果有值并且满足断言条件就返回该值的Optional,否则返回空的Optional
        System.out.println(optional4.orElse("该值长度不大于三"));
    }
}

堆和栈

package collection;

import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class QueueAndStackDemo {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();//队列,先进先出
        Deque<String> deque = new LinkedList<>();//双端队列
        Stack<String> stack = new Stack<>();

        queue.add("小白");
        queue.add("小黑");
        queue.add("小花");
        queue.add("小红");

        System.out.println(queue.size());
        System.out.println(queue.peek());//取值但不删除
        System.out.println(queue.poll());//取值且移除
        queue.remove();//删除

    }

}

对象的一对多关系


import java.util.HashSet;

public class Teacher {

    private String name;
    private int age;
    private HashSet<Student> students = new HashSet<>();

    public Teacher() {
    }

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

    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 HashSet<Student> getStudents() {
        return students;
    }

    public void setStudents(HashSet<Student> students) {
        this.students = students;
    }

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

public class Student {

    private String name;
    private int age;
    private Teacher teacher;

    public Student() {

    }

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

    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 Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

package collection;

public class HasManyDemo {
    public static void main(String[] args) {
        Teacher teacher = new Teacher("邢老师",8);

        Student student1 = new Student("小a",10);
        Student student2 = new Student("小b",12);
        Student student3 = new Student("小c",4);
        Student student4 = new Student("小d",35);

        teacher.getStudents().add(student1);
        teacher.getStudents().add(student2);
        teacher.getStudents().add(student3);
        teacher.getStudents().add(student4);

        student1.setTeacher(teacher);
        student2.setTeacher(teacher);
        student3.setTeacher(teacher);
        student4.setTeacher(teacher);

        print(teacher);
    }

    private static void print(Teacher t1){
        System.out.println(t1.getName());
        System.out.println(t1.getAge());
        for(Student s:t1.getStudents()){
            System.out.println(s);
        }
    }
}