[笔记] 疯狂JAVA讲义(第3版)第8章 Java集合(一)

简介: [笔记] 疯狂JAVA讲义(第3版)第8章 Java集合(一)

第8章 Java集合


Java集合类是一种特别有用的工具类,用于存储数量不等的对象,并可以实现常用的数据结构。


8.1 Java集合概述


在编程时,常常需要集中存放多个数据,使用数组可以存放多个对象,但数组的长度不可变。而且数组无法报错具有映射关系的数据,比如成绩表:语文-80。


为了保存数量不确定,以及具有映射关系的数据,Java提供了集合类。集合类主要保存、盛装其他数据,因此也称为容器类。


集合类和数组不同,数组元素可以是基本类型,也可以是对象。


。集合里只能保存对象(实际保存的是对象的引用变量)。


Java集合类主要由两个接口派生而出:Collection和Map。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4bESKdnq-1588248416657)(media/image1.png)]{width=“7.074179790026247in” height=“4.385655074365705in”}


8.2 Collection接口和Iterator接口


Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法可用于操作List、Set和Queue。


Collection定义的方法:


小结:


添加: add,addAll,


清除 clear,


包含元素: contains, containsAll


是否为空:isEmpty


返回Iterator: iterator


删除:remove, removeAll, retainAll(Collection c)//从集合中删除集合c不包含的元素


返回元素个数:size


转换数组:toArray()


//Collection测试
package ch8;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class CollectionTest {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(“孙悟空”);
c.add(6);
System.out.println(“元素个数”+c.size());
c.remove(6);
System.out.println(“元素个数”+c.size());
System.out.println(“c集合包含字符串(孙悟空)?”+c.contains(“孙悟空”));
c.add(“javaEE”);
System.out.println(“c集合中的元素”+c);
Collection books = new HashSet();
books.add(“JavaEE”);
books.add(“疯狂java”);
System.out.println(“c集合完全包含books集合?”+c.containsAll(books));
c.removeAll(books);
System.out.println(“c集合元素:”+c);
c.clear();
System.out.println(“c集合元素:”+c);
books.retainAll©;
System.out.println(“books集合元素:”+books);
}
}


8.2.1 使用Lambda表达式


Java8为 Iteratable接口新增了一个forEach(Consumer Action)默认方法,参数为一个函数式接口。


而Iteratable接口是Collection接口的父接口,因此Collection集合也可以用调用此方法。


调用forEach方法时,程序会依次将集合元素传给Consumer的accept(T t)方法。


可以使用Lambda表达式来实现。:


package ch8;
import java.util.Collection;
import java.util.HashSet;
public class CollectionEach {
public static void main(String[] args) {
Collection books = new HashSet();
books.add(“javaEE”);
books.add(“疯狂java”);
books.add(“疯狂Android”);
books.forEach(obj->System.out.println(“迭代元素”+obj)); //使用forEach
}
}


8.2.2 使用Java 8 增强的Iterator遍历集合元素


Iterator接口主要用来遍历(迭代访问)Collection集合中的元素,Iterator对象也被称为迭代器。


Iterator定义的4个方法:


Boolean hasNext(); //还没遍历完
Object next(); //返回下一个元素
void remove(); //删除上一次next方法返回的元素
void forEachRemaining(Consumer action); //Java8新增的默认方法,该方法可用Lambda表达式遍历集合元素。
package ch8;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class IteratorTest {
public static void main(String[] args) {
Collection books = new HashSet();
books.add(“javaEE”);
books.add(“疯狂java”);
books.add(“疯狂Android”);
books.forEach(obj->System.out.println(“迭代元素”+obj));
Iterator it = books.iterator();
while(it.hasNext()) {
String book = (String)it.next();
System.out.println(book);
if(“疯狂java”.equals(book)) {
it.remove();
}
book = “测试字符串”;//对book赋值,不会改变元素本身
}
System.out.println(books);
}
}


对迭代变量book进行赋值,并没有改变集合元素:iterator并没有把元素本身传递给迭代变量,而是传递值。


使用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只能通过Iterator的remove()方法删除上一次next()返回的集合元素。


8.2.3 使用Lambda表达式遍历Iterator


package ch8;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class IteratorEachTest {
public static void main(String[] args) {
Collection books = new HashSet();
books.add(“javaEE”);
books.add(“疯狂java”);
books.add(“疯狂Android”);
Iterator it = books.iterator();
it.forEachRemaining(obj->System.out.println(“迭代元素”+obj));
}
}


8.2.4 使用foreach循环遍历集合元素


使用foreach循环访问集合元素:


package ch8;
import java.util.Collection;
import java.util.HashSet;
public class ForeachTest {
public static void main(String[] args) {
Collection books = new HashSet();
books.add(“javaEE”);
books.add(“疯狂java”);
books.add(“疯狂Android”);
for(Object obj : books) {
String book = (String)obj;
System.out.println(book);
//这里不能修改集合:books.remove(book);该语句会引起异常
}
}
}

8.2.5 使用Java8 新增的Predicate操作集合


Java8为Collection集合新增了一个removeIf(Predicate filter)方法,该方法会批量删除符合filter条件的元素。


Pridicate 也是函数式接口,可以用Lambda表达式来代替。


package ch8;
import java.util.Collection;
import java.util.HashSet;
public class PredicateTest {
public static void main(String[] args) {
Collection books = new HashSet();
books.add(new String(“轻量级jave EE企业应用实战”));
books.add(new String(“疯狂Java讲义”));
books.add(new String(“疯狂IOS讲义”));
books.removeIf(ele->((String)ele).length()<10); //使用removeIf
System.out.println(books);
}
}


使用Predicate可以简化集合的运算,假设有3个统计需求:1、统计出现疯狂的图书数量;2、统计包含Java的图书数量;3、统计名字长度大于10的图书数量


使用Predicate只需要一个方法:


package ch8;
import java.util.Collection;
import java.util.HashSet;
import java.util.function.Predicate;
public class PredicateTest2 {
public static void main(String[] args) {
Collection books = new HashSet();
books.add(new String(“轻量级jave EE企业应用实战”));
books.add(new String(“疯狂Java讲义”));
books.add(new String(“疯狂IOS讲义”));
System.out.println(calAll(books, ele->((String)ele).contains(“疯狂”)));
System.out.println(calAll(books, ele->((String)ele).contains(“Java”)));
System.out.println(calAll(books, ele->((String)ele).length()>10 ));
}
public static int calAll(Collection books,Predicate p) {
int total = 0;
for(Object obj : books) {
if(p.test(obj)) { //调用Predicate的test方法,判断对象是否满足条件p。
total++;
}
}
return total;
}
}

8.2.6 使用Java8新增的Stream操作集合


Java8 新增了Stream、IntStream、LongStream、DoubleStream等流式API,代表多个支持串型和并行聚集操作的元素。


Stream是通用的流接口,而剩下3个则是对应元素的流。


Java8 还为流式API提供了对应的Builder,如Stream.Builder,可以通过调用Builder来创建对应的流。


创建Stream的步骤:


1、使用Stream类的builder()类方法,创建Builder。


2、重复调用Builder的add()方法向流中添加元素


3、调用Builder的build()方法获取对应的流


4、调用Stream的聚集方法。


//对于大部分聚集办法而言,每个Stream只能执行一次聚集方法。


package ch8;
import java.util.stream.IntStream;
public class IntStreamTest {
public static void main(String[] args) {
IntStream is = IntStream.builder().add(20).add(13).add(-2).add(18).build();
//大部分聚集方法只能用一次,下面的语句每次只能执行一行
//System.out.println(“最大值:”+is.max().getAsInt());
//System.out.println("最小值: "+is.min().getAsInt());
//System.out.println(“总和:”+is.sum());
//System.out.println(“元素数”+is.count());
//System.out.println(“平均值”+is.average());
//System.out.println(“is所有元素平方>20?”+is.allMatch(ele->ele*ele>20));
//System.out.println(“is包含平方>20的元素?”+is.anyMatch(ele->ele*ele>20));
//将is映射成一个新Stream。
IntStream newIs = is.map(ele->ele*2+1);
newIs.forEach(System.out::println);
}
}

聚集方法可以是中间的,也可以是末端的。


中间方法:允许流保持打开,并调用后续方法。如上述的map()方法。中间方法返回值是另一个流。


末端方法:对流的最终操作,将流结束,使流不可再用。 如上述的sum(),count()方法。


常用中间方法小结:


过滤,映射,逐个操作测试,排序所有重复元素,排序,限制最大访问元素个数。


常用末端方法:


遍历执行,转换数组,合并,最大/小值,数量,Match,返回元素


Java8运行使用流式API操作集合。Collection接口提供一个stream()默认方法,返回对应的流。


package ch8;
import java.util.Collection;
import java.util.HashSet;
public class CollectionStream {
public static void main(String[] args) {
Collection books = new HashSet();
books.add(new String(“轻量级jave EE企业应用实战”));
books.add(new String(“疯狂Java讲义”));
books.add(new String(“疯狂IOS讲义”));
System.out.println(books.stream().filter(ele->((String)ele).contains(“疯狂”)).count());
System.out.println(books.stream().filter(ele->((String)ele).contains(“Java”)).count());
System.out.println(books.stream().filter(ele->((String)ele).length()>10).count());
books.stream().mapToInt(ele->((String)ele).length()).forEach(System.out::println);
}
}


8.3 Set集合


Set集合类似于一个罐子,可以把对象丢进Set集合中,而Set不会记住元素的顺序。


Set集合和Collection基本相似。只是Set集合不允许包含重复元素。


8.3.1 HashSet类


HashSet是Set类接口的典型实现,大多数使用Set集合时就是使用这个实现类。


HashSet按Hash算法存储集合中的元素,具有很好的存取和查找性能。


HashSet具有如下特点:


1、不保证元素的排列顺序


2、不是同步的。多个线程访问一个HashSet时,需要程序员保证其同步。


3、集合元素值可以是null。


HashSet集合判断元素相等的标准是 两个对象equals()相等,且hashCode()相等。


@如果需要将某个类的对象保存到HashSet中,重写equals()方法和hashCode()方法时,尽量保证两个对象通过equals()返回true时,hashCode()返回值也相等。


HashSet中每个能存储元素的"槽位"(slot)通常被称为"桶"(bucket),如果多个元素hashCode相等,而equals返回false,就需要在一个桶中放多个元素,会导致性能下降。


@重写hashCode()的原则:同一个对象hashCode()返回值始终相等; 两个对象equals()返回true时,hashCode()返回相等的值。


对象中equals()方法用来比较的实例变量,应该用于计算hashCode值。


重写hashCode()的一般步骤:


1、把对象内每个有意义的实例变量都计算出一个int类型的hashCode值。计算方法见表8.1。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QHd8pjVb-1588248416665)(media/image2.png)]{width=“8.300698818897638in” height=“1.710492125984252in”}


2、用第1步计算出的多个hashCode组合计算出一个hashCode返回:


return f1.hashCode()*19 +(int) f2 *31 //19,31是质数,用来避免直接相加偶然相等。


如果向HashSet添加一个可变对象后,后面程序修改了该可变对象的实例变量,则可能导致它与集合中其它元素相同。


这就可能导致HashSet中包含两个相同的对象。这会导致该集合行为混乱。


8.3.2 LinkedHashSet类


HashSet子类。它会使用链表维护元素的次序,使得元素看起来是以插入顺序保存的。


输出LinkedHashSet集合的元素时,元素的顺序总是和添加顺序一致。


8.3.3 TreeSet类


TreeSet类是Sorted接口的实现类。可以确保元素处于排序状态(默认升序)。


TreeSet类的额外方法:


访问第一个、最后一个、前一个、后一个。以及3个获取子集的方法。


package ch8;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet nums = new TreeSet();
nums.add(5);
nums.add(2);
nums.add(1);
nums.add(4);
System.out.println(nums); //输出1,2,4,5
System.out.println(nums.first());
System.out.println(nums.last());
System.out.println(nums.headSet(4));//小于4的子集,不含4
System.out.println(nums.tailSet(4));//大于等于4的子集,含4
System.out.println(nums.subSet(2, 4));//大于等于2,小于4
}
}


TreeSet默认采用自然排序(升序排序)。调用元素的compareTo(Object obj)方法比较大小,然后升序排序。


TreeSet内的对象必须实现Compareable接口,(实现compareTo()方法)。


TreeSet内的对象应该是一个类的对象,否则无法调用compareTo()方法。//大部分compareTo()方法都要进行强制类型转换。即使用户自定义类实现Compareable接口,compareTo()不使用强制类型转换,但取出元素时,仍然会发生ClassCastException。总之, TreeSet里只能添加一种类型的对象。


@compareTo()和equals()方法:compareTo()返回0时,equals()应该返回true;


@ 与HashSet类似,建议不要修改放入TreeSet集合中元素的关键实例变量。


2.定制顺序


如果需要定制顺序,则需要在创建TreeSet集合对象时,提供一个Comparator对象与该集合关联。Comparator对象负责对集合元素排序。


Comparator接口包含一个int compare(T o1,T o2)方法。返回正整数则o1大于o2,返回0,相等,返回负数,o1小于o2.


package ch8;
import java.util.TreeSet;
class M
{
int age;
public M(int age) {
this.age = age;
}
public String toString() {
return “M[age:”+age+"]";
}
}
public class TreeSetTest4 {
public static void main(String[] args) {
TreeSet ts = new TreeSet( (o1,o2)->{ //Comparator ,使用Lambda表达式简写。
M m1 = (M)o1;
M m2 = (M)o2;
return m1.age>m2.age ? -1
m1.age<m2.age?1 :0;
});
ts.add(new M(5));
ts.add(new M(9));
ts.add(new M(3));
System.out.println(ts);
}
}

8.3.4 EnumSet类


专门为枚举可设计的集合类。


元素必须是指定枚举类型。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KORJELFO-1588248416667)(media/image3.png)]{width=“8.586475284339457in” height=“3.3018438320209973in”}


package ch8;
import java.util.EnumSet;
enum Season{
SPRING,SUMMER,FAIL,WINTERL
}
public class EnumSetTest {
public static void main(String[] args) {
EnumSet es1 = EnumSet.allOf(Season.class);
System.out.println(es1);
EnumSet es2 = EnumSet.noneOf(Season.class);
System.out.println(es2);
es2.add(Season.WINTERL);
System.out.println(es2);
EnumSet es3 = EnumSet.of(Season.FAIL,Season.WINTERL);
System.out.println(es3);
EnumSet es4 = EnumSet.range(Season.SUMMER, Season.WINTERL);
System.out.println(es4);
EnumSet es5 = EnumSet.complementOf(es4);
System.out.println(es5);
}
}
/可以使用EnumSet.copyOf© 复制一个Collection集合中的元素,前提是c中元素必须是同一个枚举类的枚举值。


8.3.5各Set实现类的性能分析



HashSet性能好


TreeSet 可以排序。


LinkedHashSet 遍历快。


EnumSet专为枚举设计。


8.4 List集合


List集合代表代表一个元素有序、可重复的集合。


List默认按索引的添加顺序设置元素索引(从0开始)。


8.4.1 Java8改进的List接口和ListIterator接口


List中增加了根据索引操作集合的方法。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iqts9foy-1588248416669)(media/image4.png)]{width=“7.973155074365704in” height=“3.7639337270341207in”}


package ch8;
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args) {
List books = new ArrayList();
books.add(new String(“轻量级jave EE企业应用实战”));
books.add(new String(“疯狂Java讲义”));
books.add(new String(“疯狂IOS讲义”));
System.out.println(books);
books.add(1,new String(“疯狂Ajax讲义”));
for(int i = 0 ; i < books.size() ; i++ ) {
System.out.println(books.get(i));
}
books.remove(2);
System.out.println(books);
System.out.println(books.indexOf(new String(“疯狂Ajax讲义”)));
books.set(1, new String(“疯狂java讲义”));
System.out.println(books);
System.out.println(books.subList(1, 2));
}
}


List 判断对象相等的标准是equals()方法返回true。


//sort和replace方法
package ch8;
import java.util.ArrayList;
import java.util.List;
public class ListTest3 {
public static void main(String[] args) {
List books = new ArrayList();
books.add(new String(“轻量级jave EE企业应用实战”));
books.add(new String(“疯狂Java讲义”));
books.add(new String(“疯狂IOS讲义”));
books.add(new String(“疯狂Ajax讲义”));
//sort
books.sort((o1,o2)->((String)o1).length()-((String)o2).length());
System.out.println(books);
//replaceAll
books.replaceAll(ele->((String)ele).length());
System.out.println(books);
}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ioufkGkk-1588248416673)(media/image5.png)]{width=“7.838729221347331in” height=“1.4366797900262467in”}


ListIterator增加了向前迭代和添加元素。


package ch8;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorTest {
public static void main(String[] args) {
String[] books = {“疯狂java讲义”,“疯狂IOS讲义”,“疯狂Java EE 企业应用实战”};
List bookList = new ArrayList();
for(int i = 0 ; i < books.length ; i++ ) {
bookList.add(books[i]);
}
ListIterator lit = bookList.listIterator();
while(lit.hasNext()) {
System.out.println(lit.next());
lit.add("----------------");
}
System.out.println(“现在开始反向迭代”);
while(lit.hasPrevious()) {
System.out.println(lit.previous());
}
}
}

[笔记] 疯狂JAVA讲义(第3版)第8章 Java集合(二):https://developer.aliyun.com/article/1537905

相关文章
|
15天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
25 2
|
14天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
19天前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
19天前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
19天前
|
Java 开发者
|
22天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
22天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
22天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
|
8天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
10 0
|
1月前
|
Java 编译器 Android开发
Kotlin语法笔记(28) -Kotlin 与 Java 混编
本系列教程详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。对于希望快速学习Kotlin的用户,推荐查看“简洁”系列教程。本文档重点介绍了Kotlin与Java混编的技巧,包括代码转换、类调用、ProGuard问题、Android library开发建议以及在Kotlin和Java之间互相调用的方法。
24 1
下一篇
无影云桌面