简介
- 为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),
Java提供了集合类,集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所有的集合类都位于java.util包下,后来为了处理多线程环境下的并发安全问题,Java 5还在java.util.concurrent包下提供了一些多线程支持的集合类- 集合里只能保存对象,这个跟数组的区别
- 集合类主要由两个接口派生而出
Collection和Map
- 类的继承树图
Collection接口、子接口及其实现
Map体系的继承树
-
四种集合
SetListQueueMap
-
常用的实现类:
HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList、HashMap、TreeMap等实现类 -
访问方式
List:根据元素的索引来访问Map:根据key来访问valueSet:只能根据元素本身来访问
-
简单使用
-
lambda表达式遍历集合java 8为Iterable接口新增了一个forEach(Consumer action)默认方法,该方法所需参数的类型是一个函数式接口,而Iterable接口是Collection接口的父接口,因此Collection集合也可直接调用该方法//创建集合 var books = new HashSet(); books.add("java"); books.add("c++"); //使用foreach()遍历集合 books.foreach(book -> System.out.println(book));
-
Iterator遍历集合元素Iterator接口也是Java集合框架的成员,但它与Collection系列、Map系列的集合不一样:Collection系列集合、Map系列集合主要用于盛装其他对象,而Iterator则主要用于遍历(即迭代访问)Collection集合中的元素,Iterator对象也被称为迭代器,Iterator仅用于遍历集合,Iterator本身并不提供盛装对象的能力//遍历并打印 var list = books.iterator(); While(list.hasNext()){ var book = list.next(); System.out.println(book) }注意:
Iterator必须依附于Collection对象,若有一个Iterator对象,则必然有一个与之关联的Collection对象
-
Lambda表达式遍历IteratorJava 8为Iterator新增了一个forEachRemaining(Consumer action)方法var list = books.iterator(); //使用lambda表达式(目标类型Comsumer)来遍历集合元素 books.forEachRemaining(book -> System.out.println(book));
-
foreach循环遍历集合元素Java 5提供foreach循环迭代访问集合元素更加便捷for(var book : book){ System.out.println(book); }
-
Predicate操作集合Java 8为Collection集合新增了一个removeIf(Predicate filter)方法,该方法将会批量删除符合filter条件的所有元素。该方法需要一个Predicate(谓词)对象作为参数,Predicate也是函数式接口,因此可使用Lambda表达式作为参数List<String> list = new ArrayList<>(); list.add("java"); list.removeIf(book -> book.equals("java"));
-
Stream操作集合- Java 8还新增了
Stream、IntStream、LongStream、DoubleStream等流式API,这些API代表多个支持串行和并行聚集操作的元素IntStream is = IntStream.builder() .add(1) .add(2) .build(); System.out.println(is.max().getAsInt()); System.out.println(is.min().getAsInt()); Stream提供了大量的方法进行聚集操作,这些方法既可以是“中间的”(intermediate),也可以是“末端的”(terminal),具体可以参见API源码
- Java 8还新增了
-
Set集合HashSet类- 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化
HashSet不是同步的,如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合时,则必须通过代码来保证其同步,即是非线程安全- 集合元素值可以是
null
当向
HashSet中添加可变对象时,必须十分小心。如果修改HashSet集合中的对象,有可能导致该对象与集合中的其他对象相等,从而导致HashSet无法准确访问该对象LinkedHashSet类LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。也就是说,当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素
虽然
LinkedHashSet使用了链表记录集合元素的添加顺序,但LinkedHashSet依然是HashSet,因此它依然不允许集合元素重复TreeSet类TreeSet是SortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合元素处于排序状态TreeSet有自然排序跟定制排序,自然排序调用自带的排序方法即可,定制排序需要通过Comparator接口的帮助,自行实现排序规则
@Data @ToString @AllArgsConstructor @NoArgsConstructor class Clazz{ int age; } //使用 var tree = new TreeSet((o1,o2->{ var c1 = (Clazz) o1; var c2 = (Clazz) o2; //根据age的属性决定,age越大,Clazz对象反而越小 return c1.age > c2.age ? -1 : c1.age < c2.age ? 1 : 0; }));
Java 9改进了TreeSet实现,如果采用自然排序的Set集合的元素没有实现Comparable接口,程序就会立即引发ClassCastException异常,如果希望TreeSet能正常运作,TreeSet只能添加同一种类型的对象EnumSet类EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小,而且运行效率很好。尤其是进行批量操作(如调用containsAll()和retainAll()方法)时,如果其参数也是EnumSet集合,则该批量操作的执行速度也非常快EnumSet集合不允许加入null元素,如果试图插入null元素,EnumSet将抛出NullPointerException异常。如果只是想判断EnumSet是否包含null元素或试图删除null元素都不会抛出异常,只是删除操作将返回false,因为没有任何null元素被删除
-
各
Set实现类的性能分析HashSet的性能总是比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序- 对于普通的插入、删除操作,
LinkedHashSet比HashSet要略微慢一点,这是由维护链表所带来的额外开销造成的,但由于有了链表,遍历LinkedHashSet会更快 EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素
-