前言

最近刷题,看到别人说可以采用HashSet 处理,一脸懵,好像在哪里遇见过,就是想不起来,只能重新学习。

1.集合介绍

集合: 若干个确定的元素所构成的整体。

为什么需要集合类???

  • 数组有如下限制
    - 数组初始化后大小不可变
    - 只能按索引顺序存取
  • 需要不同类型的集合类处理不同的数据
  • 可变大小的顺序链表
  • 保证无重复元素的集合

Java的java.util包提供了集合类:Collection ,主要提供了以下三种类型的集合:

  • List:有序列表的集合
  • Set:无序没有重复元素的集合
  • Map:键值对的映射表集合

注意:Java访问集合总是通过统一的方式——迭代器(Iterator)来实现,它最明显的好处在于无需知道集合内部元素是按什么方式存储的。

2. List介绍

2.1 基本介绍

List 是一个长度可变的有序数表
List的行为和数组几乎完全相同

  • ArrayList :内部使用了数组来存储所有元素
  • LinkedList : 通过“链表”也实现了List接口

二者之间的比较:

Java list集合算出某个属性的和 java集合类list_Java list集合算出某个属性的和


通常情况下,我们总是优先使用 ArrayList。

2.2 List 的特点

  • List 接口允许我们添加重复的元素,即List内部的元素可以重复
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple"); // size=1
        list.add("pear"); // size=2
        list.add("apple"); // 允许重复添加元素,size=3
        System.out.println(list.size());
    }
}
  • List还允许添加null
public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple"); // size=1
        list.add(null); // size=2
        list.add("pear"); // size=3
        String second = list.get(1); // null
        System.out.println(second);
    }
}
  • 创建List
    除了使用ArrayList和LinkedList,我们还可以通过List接口提供的of()方法,根据给定元素快速创建List:
List<Integer> list = List.of(1, 2, 5);

但是List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常。

  • 遍历List
  • 用for循环根据索引配合get(int)方法遍历
public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("apple", "pear", "banana");
        for (int i=0; i<list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }
    }
}

但这种方式并不推荐,一是代码复杂,二是因为get(int)方法只有ArrayList的实现是高效的,换成LinkedList后,索引越大,访问速度越慢。

  • Iterator对象有两个方法:boolean hasNext()判断是否有下一个元素,E next()返回下一个元素。
public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("apple", "pear", "banana");
        for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            System.out.println(s);
        }
    }
}
  • Java的for each循环本身就可以帮我们使用Iterator遍历
public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("apple", "pear", "banana");
        for (String s : list) {
            System.out.println(s);
        }
    }
}

2.3 List和Array转换

1. 把List变为Array有三种方法
  • 第一种是调用toArray()方法直接返回一个Object[]数组:
public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("apple", "pear", "banana");
        Object[] array = list.toArray();
        for (Object s : array) {
            System.out.println(s);
        }
    }
}
  • 第二种方式是给toArray(T[])传入一个类型相同的Array,List内部自动把元素复制到传入的Array中
public class Main {
    public static void main(String[] args) {
        List<Integer> list = List.of(12, 34, 56);
        Integer[] array = list.toArray(new Integer[3]);
        for (Integer n : array) {
            System.out.println(n);
        }
    }
}
  • 最后一种更简洁的写法是通过List接口定义的T[] toArray(IntFunction<T[]> generator)方法:
Integer[] array = list.toArray(Integer[]::new);
2. 把Array变为List
  • 通过List.of(T…)方法最简单:
Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);

2.4 List的contains 和 index of 方法

List 内部按照放入元素的先后顺序存放,并且每个元素都可以通过索引确定自己的位置。
List还提供了boolean contains(Object o)方法来判断List是否包含某个指定元素。此外,int indexOf(Object o)方法可以返回某个元素的索引,如果元素不存在,就返回-1。

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("A", "B", "C");
        System.out.println(list.contains("C")); // true
        System.out.println(list.contains("X")); // false
        System.out.println(list.indexOf("C")); // 2
        System.out.println(list.indexOf("X")); // -1
    }
}

3 Map

3.1 基本介绍

Map这种键值(key-value)映射表的数据结构,作用就是能高效通过key快速查找value(元素)。

用Map来实现根据name查询某个Student的代码如下:

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Xiao Ming", 99);
        Map<String, Student> map = new HashMap<>();
        map.put("Xiao Ming", s); // 将"Xiao Ming"和Student实例映射并关联
        Student target = map.get("Xiao Ming"); // 通过key查找并返回映射的Student实例
        System.out.println(target == s); // true,同一个实例
        System.out.println(target.score); // 99
        Student another = map.get("Bob"); // 通过另一个key查找
        System.out.println(another); // 未找到返回null
    }
}

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

当我们调用put(K key, V value)方法时,就把key和value做了映射并放入Map。
当我们调用 get(K key)时,就可以通过key获取到对应的value。如果key不存在,则返回null。

我们在存储Map映射关系的时候,对同一个key调用两次put()方法,分别放入不同的value

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 123);
        map.put("pear", 456);
        System.out.println(map.get("apple")); // 123
        map.put("apple", 789); // 再次放入apple作为key,但value变为789
        System.out.println(map.get("apple")); // 789
    }
}

Map中不存在重复的key,因为放入相同的key,只会把原有的key-value对应的value给替换掉。

3.2 遍历map

对Map来说,要遍历key可以使用for each循环遍历Map实例的keySet()方法返回的Set集合,它包含不重复的key的集合:

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("apple", 123);
        map.put("pear", 456);
        map.put("banana", 789);
        for (String key : map.keySet()) {
            Integer value = map.get(key);
            System.out.println(key + " = " + value);
        }
    }
}

Map存储的是key-value的映射关系,并且,它不保证顺序.
最常用的一种Map实现是HashMap。

3.3 Map 的类型

  • HashMap是一种通过对key计算hashCode(),通过空间换时间的方式,直接定位到value所在的内部数组的索引,因此,查找效率非常高。
  • 如果Map的key是enum类型,推荐使用EnumMap,既保证速度,也不浪费空间。
  • 使用EnumMap的时候,根据面向抽象编程的原则,应持有Map接口。
  • SortedMap在遍历时严格按照Key的顺序遍历,最常用的实现类是TreeMap;
  • 作为SortedMap的Key必须实现Comparable接口,或者传入Comparator;
  • 要严格按照compare()规范实现比较逻辑,否则,TreeMap将不能正常工作。

4. Set

4.1 基本介绍

如果我们只需要存储不重复的key,并不需要存储映射的value,那么就可以使用Set。
Set用于存储不重复的元素集合,它主要提供以下几个方法:

  • 将元素添加进Set:boolean add(E e)
  • 将元素从Set删除:boolean remove(Object e)
  • 判断是否包含元素:boolean contains(Object e)
  • 我们来看几个简单的例子:
public class Main {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        System.out.println(set.add("abc")); // true
        System.out.println(set.add("xyz")); // true
        System.out.println(set.add("xyz")); // false,添加失败,因为元素已存在
        System.out.println(set.contains("xyz")); // true,元素存在
        System.out.println(set.contains("XYZ")); // false,元素不存在
        System.out.println(set.remove("hello")); // false,删除失败,因为元素不存在
        System.out.println(set.size()); // 2,一共两个元素
    }
}

经常用Set用于去除重复元素。

Java list集合算出某个属性的和 java集合类list_List_02

  • HashSet是无序的,因为它实现了Set接口,并没有实现SortedSet接口;
  • TreeSet是有序的,因为它实现了SortedSet接口。