Java笔记2:Java集合之Collection
本文主要内容根据李刚《疯狂Java讲义》第8章源码整理而来。
Java集合与Collection概念
Java集合
所有的集合都是通过array实现的。array是固定的,不能动态改变,且一个array只能存放同一种数据类型。
java集合可以存储和操作不固定的一组数据,只能存放引用类型的数据,不能放基本数据类型。
java集合在java.util 程序包中实现。
Java集合框架:从Collection和Map两大根接口延伸出来的子接口和实现类。
Collection:Set(HashSet、TreeSet、LinkedHashSet)、List(Vector、ArrayList、LinkedList)、Queue
Map体系:HashMap(线程不安全的)、HashTable(线程安全的)、SortedMap(TreeMap)、EnumMap
下面程序创建了四个线程安全的集合对象,可以避免使用Vector和HashTable:
Collection c = Collections.synchronizedCollection(new ArrayList()); List list = Collections.synchronizedList(new ArrayList()); Set s = Collections.synchronizedSet(new HashSet()); Map m = Collections.synchronizedMap(new HashMap());
Collection概念
Collection:对象的集合。没有get方法来获取某个元素,只能通过Iterator遍历元素。
Iterable和Collection接口定义
public interface Iterable
Iterator
default void forEach(Consumeraction) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
public interface Collection
Iterator
@Override
default Spliterator
return Spliterators.spliterator(this, 0);
}
int size();
boolean isEmpty();
boolean contains(Object o);
Object[] toArray();
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collectionc);
boolean addAll(Collectionc);
boolean removeAll(Collectionc);
default boolean removeIf(Predicatefilter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
boolean retainAll(Collectionc);
void clear();
boolean equals(Object o);
int hashCode();
default Stream
return StreamSupport.stream(spliterator(), false);
}
default Stream
return StreamSupport.stream(spliterator(), true);
}
}
Collection的基本操作
· add(Object o):增加元素
· addAll(Collection c):...
· clear():...
· contains(Object o):是否包含指定元素
· containsAll(Collection c):是否包含集合c中的所有元素
· iterator():返回Iterator对象,用于遍历集合中的元素
· remove(Object o):移除元素
· removeAll(Collection c):相当于减集合c
· retainAll(Collection c):相当于求与c的交集
· size():返回元素个数
· toArray():把集合转换为一个数组
Collection的用法
Collection c = new ArrayList();
c.add("孙悟空");// 集合里不能放基本类型的值,但Java支持自动装箱
c.add(6);
c.remove(6); // 删除指定元素
c.contains("孙悟空")); // true
Collection books = new HashSet();
books.add(6);
books.add("疯狂Java讲义");
c.containsAll(books) // false
c.removeAll(books); // 用c集合减去books集合里的元素
c.clear();
books.retainAll(c); // 控制books集合里只剩下c集合里包含的元素
books.forEach(obj -> System.out.println("迭代集合元素:" + obj));
// 增强的for循环,性能表现好
for (Object obj : books)
{
String book = (String)obj;// 此处的book变量也不是集合元素本身
System.out.println(book);
}
// java8的foreach,每次耗时高达100毫秒以上,数据量大时不建议使用
list.forEach(str->{
System.out.println(str);
});
// Java8增强的Iterator遍历
Iterator it = books.iterator();
while(it.hasNext())
{
String book = (String)it.next();
if (book.equals("疯狂Java讲义"))//删除 "疯狂Java讲义"
{
//Iterator迭代过程中,不可修改集合元素。books.remove(book)会引发异常
it.remove();
}
}
Iterator it = books.iterator();
it.forEachRemaining(obj -> System.out.println("迭代集合元素:" + obj));
// Java8增强的Iterator遍历 Lambda表达式(目标类型是Predicate)过滤集合
books.removeIf(ele -> ((String)ele).length() < 10);
// Java8新增的Stream操作集合
IntStream is = IntStream.builder()
.add(20)
.add(13)
.add(-2)
.add(18)
.build();
// 下面调用聚集方法的代码每次只能执行一个
System.out.println("is所有元素的最大值:" + is.max().getAsInt());
System.out.println("is所有元素的总和:" + is.sum());
System.out.println("is所有元素的总数:" + is.count());
System.out.println("is所有元素的平均值:" + 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,每个元素是原Stream元素的2倍+1
IntStream newIs = is.map(ele -> ele * 2 + 1);
// 使用方法引用的方式来遍历集合元素
newIs.forEach(System.out::println); // 输出41 27 -3 37
// 调用Collection的stream方法即可返回集合对应的stream
books.stream().filter(ele->((String)ele).contains("疯狂")).count()
books.stream().mapToInt(ele -> ((String)ele).length()).forEach(System.out::println);
ArrayList nums = new ArrayList();
nums.add(2);
nums.add(-5);
nums.add(3);
Collections.reverse(nums); // 将List集合元素的次序反转
Collections.sort(nums); // 将List集合元素的按自然顺序排序
Collections.shuffle(nums); // 将List集合元素的按随机顺序排序 每次输出的次序不固定
Collections.frequency(nums , -5) // 判断-5在List集合中出现的次数,返回1
Collections.sort(nums); // 对nums集合排序
Collections.binarySearch(nums , 3) //只有排序后的List集合才可用二分法查询,输出3
List unmodifiableList = Collections.emptyList(); //空的、不可改变的List对象
Set unmodifiableSet = Collections.singleton("疯狂Java讲义");// 创建只有一个元素,且不可改变的Set对象
Map scores = new HashMap();
scores.put("语文" , 80);
scores.put("Java" , 82);
Map unmodifiableMap = Collections.unmodifiableMap(scores);// 返回Map对象的不可变版本
1.Set集合(不重复)
Set是不包含重复元素的集合,根据equals方法判读两个对象是否相同。
HashSet 无序的,加入的元素要注意hashCode()方法的实现,元素可以是null。
LinkedHashset 继承自HashSet,使用链表维护元素次序,根据元素的hashCode值决定元素的存储位置。性能略低于HashSet(要维护插入顺序),迭代访问元素时会有好性能(采用链表维护了顺序)。
TreeSet 自动排序,实现SortedSet 接口。要排序,性能比HashSet差。
EnumSet 有序的,EnumSet中的所有元素都必须是指定枚举类型的枚举值。
1.1 HasSet (无序)
不包含多个相等的元素。
判断两个对象相等的标准:equals方法和hashCode方法返回值都相同
HashSet hs = new HashSet();
hs.add(new R(5));
hs.add(new R(-3));
hs.add(new R(9));
hs.add(new R(-2));
System.out.println(hs); // 打印HashSet集合,集合元素没有重复
Iterator it = hs.iterator();
R first = (R)it.next(); // 取出第一个元素
first.count = -3; // 为第一个元素的count实例变量赋值
hs.remove(new R(-3)); //删除count为-3的R对象
1.2 LinkedHashSet(使用链表维护元素的插入顺序)
LinkedHashSet books = new LinkedHashSet();
books.add("疯狂Java讲义");
books.add("轻量级Java EE企业应用实战");
books.remove("疯狂Java讲义"); // 删除 疯狂Java讲义
books.add("疯狂Java讲义");// 重新添加 疯狂Java讲义
1.3 TreeSet(自动排序的)
Treeset操作
· first():返回第一个元素
· last():返回最后一个元素
· lower(Object o):返回指定元素之前的元素
· higher(Obect o):返回指定元素之后的元素
· subSet(fromElement, toElement):返回子集合
Treeset操作示例
TreeSet nums = new TreeSet();
// 向TreeSet中添加四个Integer对象
nums.add(5);
nums.add(2);
nums.add(10);
nums.add(-9);
nums.first() // -9
nums.last() // 10
nums.headSet(4) //返回小于4的子集,不包含4 [-9, 2]
nums.tailSet(5) //返回大于等于5的子集 [5, 10]
nums.subSet(-3 , 4)//返回大于等于-3,小于4的子集 [2]
向TreeSet中添加的应该是同一类型的对象:
TreeSet ts = new TreeSet();
ts.add(new R(5));
ts.add(new R(-3));
ts.add(new R(9));
ts.add(new R(-2));
R first = (R)ts.first();
first.count = 20; // 对第一个元素的count赋值
R last = (R)ts.last();
last.count = -2; // 对最后一个元素的count赋值
System.out.println(ts); // 20 -2 5 -2
ts.remove(new R(-2)) // 删除实例变量被改变的元素,删除失败
System.out.println(ts.remove(new R(5))); // 删除实例变量没有被改变的元素,成功
//定制排序 此处Lambda表达式的目标类型是Comparator
TreeSet ts = new TreeSet((o1 , o2) ->
{
M m1 = (M)o1;
M m2 = (M)o2;
// 根据M对象的age属性来决定大小,age越大,M对象反而越小
return m1.age > m2.age ? -1: m1.age < m2.age ? 1 : 0;
});
1.4 EnumSet
// 创建一个EnumSet集合,集合元素就是Season枚举类的全部枚举值
EnumSet es1 = EnumSet.allOf(Season.class);
System.out.println(es1); // 输出[SPRING,SUMMER,FALL,WINTER]
// 创建一个EnumSet空集合,指定其集合元素是Season类的枚举值。
EnumSet es2 = EnumSet.noneOf(Season.class);
System.out.println(es2); // 输出[]
// 手动添加两个元素
es2.add(Season.WINTER);
es2.add(Season.SPRING);
System.out.println(es2); // 输出[SPRING,WINTER]
// 以指定枚举值创建EnumSet集合
EnumSet es3 = EnumSet.of(Season.SUMMER , Season.WINTER);
EnumSet es4 = EnumSet.range(Season.SUMMER , Season.WINTER);
// es5的集合元素 + es4集合元素 = Season枚举类的全部枚举值
EnumSet es5 = EnumSet.complementOf(es4);// es4的补集
Collection c = new HashSet();
c.clear();
c.add(Season.FALL);
c.add(Season.SPRING);
EnumSet enumSet = EnumSet.copyOf(c); //复制c所有元素来创建 [SPRING,FALL]
c.add("疯狂Java讲义");
enumSet = EnumSet.copyOf(c); // 异常:因为c集合里的元素不是全部都为枚举值
2.List(可重复、有序)
List是可包含重复元素的集合。与Set相比,增加了与索引位置相关的操作。
· add(int index, Object o):在指定位置插入元素
· addAll(int index, Collection c):...
· get(int index):取得指定位置元素
· indexOf(Obejct o):返回对象o在集合中第一次出现的位置
· lastIndexOf(Object o):...
· remove(int index):删除并返回指定位置的元素
· set(int index, Object o):替换指定位置元素
· subList(int fromIndex, int endIndex):返回子集合
ArrayList类 基于数组实现的List类。
LinkedList类 有序的基于链表实现的List类。
2.1 ArrayList 线程不安全的
2.2 Vector 线程安全的,耗时一些
books.remove(2); // 删除第三个元素
books.indexOf(new String("疯狂Ajax讲义"))
books.set(1, new String("疯狂Java讲义"));//将第二个元素替换成新的字符串对象
//将books集合的第二个元素(包括)到第三个元素(不包括)截取成子集合
System.out.println(books.subList(1 , 2));
// 使用目标类型为Comparator的Lambda表达式对List集合排序
books.sort((o1, o2)->((String)o1).length() - ((String)o2).length());
// 使用目标类型为UnaryOperator的Lambda表达式来替换集合中所有元素
// 该Lambda表达式控制使用每个字符串的长度作为新的集合元素
books.replaceAll(ele->((String)ele).length());
// 反向迭代
while(lit.hasPrevious())
{
System.out.println(lit.previous());
}
2.3 LinkedList实现类
内部以链表方式保存元素,随机访问集合元素的性能较差,但插入、删除元素时性能较出色。
可以List、双端队列、栈的方式使用。
LinkedList books = new LinkedList();
books.offer("疯狂Java讲义");//加入队列的尾部
books.push("轻量级Java EE企业应用实战");//加入栈的顶部
books.offerFirst("疯狂Android讲义");//添加到队列头部(相当于栈的顶部)
// 以List的方式(按索引访问的方式)来遍历集合元素
for (int i = 0; i < books.size() ; i++ )
{
System.out.println("遍历中:" + books.get(i));
}
books.peekFirst() // 访问、并不删除栈顶的元素
books.peekLast() // 访问、并不删除队列的最后一个元素
books.pop() // 将栈顶的元素弹出“栈”
books.pollLast() // 访问、并删除队列的最后一个元素
// 固定长度的List 不可删除、增加元素
Arrays.asList(Object... a);
3.Queue集合
Queue即队列。队列先进先出,出栈机制既保证了顺序,又不需要单独管理元素的删除。
队列不允许随机访问队列中的元素。
应用场景:耗时任务在主线程逐个处理容易产生阻塞,可在主线程中将要处理的任务暂存到一个队列里,由一个新的线程负责取出执行。
PriorityQueue类 已排序。
ArrayDeque类 双端队列。
Queue增加了以下操作:
· boolean offer(E e): 将指定的元素插入此队列,当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。推荐使用此方法取代add
· E poll(): 返回队列首个元素。将首个元素从队列中弹出,如果队列是空的,就返回null;推荐使用此方法取代remove
· E element(): 返回队列首个元素。查看首个元素,不会移除首个元素,如果队列是空的就抛出异常NoSuchElementException
· E peek(): 返回队列首个元素。查看首个元素,不会移除首个元素,如果队列是空的就返回null
3.1 PriorityQueue
按队列元素的大小进行重新排序(实际已违反队列先进先出的原则)
PriorityQueue pq = new PriorityQueue();
// 下面代码依次向pq中加入四个元素
pq.offer(6);
pq.offer(-3);
pq.offer(20);
pq.offer(18);
// 输出pq队列,并不是按元素的加入顺序排列
System.out.println(pq); // 输出[-3, 6, 20, 18]
// 访问队列第一个元素,其实就是队列中最小的元素:-3
System.out.println(pq.poll());
3.2 Deque的实现类: ArrayDeque 双端队列
栈的行为(性能较差):
ArrayDeque stack = new ArrayDeque();
// 依次将三个元素push入"栈"
stack.push("疯狂Java讲义");
stack.push("轻量级Java EE企业应用实战");
stack.push("疯狂Android讲义");
stack.peek() // 访问第一个元素,不将其pop出"栈"
stack.pop() // pop出第一个元素
队列的行为:
ArrayDeque queue = new ArrayDeque();
// 依次将三个元素加入队列
queue.offer("疯狂Java讲义");
queue.offer("轻量级Java EE企业应用实战");
queue.offer("疯狂Android讲义");
queue.peek() // 访问但不将其poll出队列"栈"
queue.poll() //将其poll出队列"栈"
本文参考和使用了以下文章的部分内容:
https://www.cnblogs.com/nayitian/p/3266090.html#LinkedHashSet
https://www.cnblogs.com/liyiran/p/4607817.html