Java笔记2:Java集合之Collection

作者:陆金龙    发表时间:2018-05-25 00:17   

关键词:Collection  Set集合  List  Queue集合  

本文主要内容根据李刚《疯狂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 iterator();

 default void forEach(Consumeraction) {

        Objects.requireNonNull(action);

        for (T t : this) {

            action.accept(t);

        }

    }

  default Spliterator spliterator() {

        return Spliterators.spliteratorUnknownSize(iterator(), 0);

    }

}

public interface Collection extends Iterable{

Iterator iterator();

@Override

    default Spliterator spliterator() {

        return Spliterators.spliterator(this, 0);

    }

int size();

boolean isEmpty();

    boolean contains(Object o);

Object[] toArray();

T[] toArray(T[] a);

    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 each = 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 stream() {

        return StreamSupport.stream(spliterator(), false);

    }

default Stream parallelStream() {

        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