> Java > java지도 시간 > 본문

Java 컬렉션(2) - 컬렉션 프레임워크 및 알고리즘에 대한 자세한 설명

黄舟
풀어 주다: 2017-03-01 11:56:28
원래의
1531명이 탐색했습니다.

java 컬렉션(1) - 데이터 구조에 대한 자세한 설명: http://www.php.cn/java-article-354226.html

프레임워크는 클래스 집합을 의미합니다. 이 집합에는 많은 고급 메커니즘, 기능 및 전략이 구현되어 있습니다. 프레임워크 사용자는 이러한 기본 메커니즘을 다시 생성하지 않고도 하위 클래스를 생성하여 슈퍼클래스를 구현하고 확장할 수 있습니다. 일상 업무에서 우리가 사용하는 기술은 기본적으로 프레임워크입니다. 해당 패키지를 사용하고 해당 기능을 호출할 때 이 프레임워크의 아이디어를 사용하게 됩니다. 컬렉션(1)에서 컬렉션의 데이터 구조를 분석한 후, 오늘은 컬렉션의 프레임워크에 대해 계속 논의하겠습니다.

(1) 수집 데이터 구조 검토

基本 类型 实现接口 说明
List 链表LinkedList Deque,List,Queue 通过存放前后结点的引用,实现双向链表
数组列表ArrayList List,RandomAccess 数据传入动态数组中,自动扩充数组大小
Set 散列集HashSet Set 哈希法存储数据,无序但查找时效率高
树集TreeSet NavigableSet,Set,SortedSet 按照一定方法排序,输出有顺序的集合
Queue 优先级队列PriorityQueue Queue 按照堆排序的方法排序的队列树
双端队列ArrayDeque Deque, Queue 可以在两端添加和删除,不能操作中间的队列
Map 散列表HashMap Map 用哈希法存放的,键值映射的表
树表TreeMap Map,NavigableMap,SortedMap 将键按照一定方法排序的表
注:前六种(不包括Map)都实现了Collection和Iterator接口,因为篇幅限制没有写出。

(2) 뷰

컬렉션 클래스 라이브러리에서 구현 클래스에 대한 인터페이스를 구축하는 데 매우 많은 부분을 사용했는데 이러한 인터페이스의 용도는 무엇입니까? 구현 클래스의 인터페이스에 메소드를 직접 작성해도 같은 효과가 있지 않을까요? 실제 상황은 우리가 생각하는 것만큼 간단하지 않습니다. 이러한 인터페이스를 통해 작동하려면 복잡한 것들이 많이 있습니다. 예를 들어, 특정 클래스 뷰 객체 의 공용 인터페이스 유형의 객체 를 호출합니다. 이러한 객체의 유형은 특정 구현 클래스가 아니라 일부 구현 클래스의 공용 인터페이스입니다. 뷰에는 많은 기능이 있으며 주요 기능은 다음과 같습니다.

1. 뷰는 경량 세트 래퍼입니다.

메서드를 사용하여 일반 배열을 뷰 객체로 변환할 수 있습니다. 목록 유형입니다. 이 뷰 객체의 유형은 List입니다. 이상하게 들릴 수도 있습니다. List는 인터페이스이고 인스턴스화될 수 없기 때문입니다. 그러나 이것이 뷰 객체의 장점입니다. 이 뷰 객체는 일반적인 ArrayList, LinkedList 등과 같은 특정 메서드를 통해 List 인터페이스를 구현하는 모든 컬렉션 클래스에 할당될 수 있습니다. 컬렉션 클래스.

package SetViews;import java.util.Arrays;import java.util.Collections;import java.util.LinkedList;/**
 * 
 * @author QuinnNorris
 * 视图是一个轻量级的集包装器,下面有两个例子
 */public class Views {

    /**
     * @param args
     */
    public static void main(String[] args) {        // TODO Auto-generated method stub
        //-----------------------例1----------------------
        int[] arr = new int[10];        //创建一个普通的int数组
        Arrays.asList(arr);        //通过asList方法,我们将一个普通的数组转换成List类型的视图对象。

        //-----------------------例2-----------------------
        List<String> ll = new LinkedList<>(); 
        //创建一个LinkedList对象ll。
        ll.addAll(Collections.nCopies(10, "ok"));        
        //nCopies是Collections类的静态方法,可以将“ok”复制10次,并返回一个List类型的视图对象。
        //在这里我们不可以直接把Collections.nCopies的类型强转成LinkedList,因为语法不能上转下
        //我们通过addAll方法将这个内容填充到LinkedList对象ll中。

        ll.set(2, "no");        //我们看看它是否能正常工作,我们试着将第三个链表中的String换成no。
        //请注意,链表最好不要使用get和set方法,这里只是为了实验。
        for(String s : ll){
            System.out.println(s);
        }        //输出结果第三个是no,其他全是ok,没有任何问题。
    }

}
로그인 후 복사

이것은 뷰의 큰 장점 중 하나입니다. 배열을 컬렉션 클래스 라이브러리의 클래스 객체로 래핑하는 경량 배열 래퍼입니다. 마찬가지로, 키-값 쌍과 Set에 저장해야 하는 데이터를 뷰를 통해 래핑하여 해당 클래스에 전달할 수 있습니다. 따라서 우리는 이 뷰의 기능을 경량 세트 래퍼라고 부릅니다.

2. 뷰를 통해 클래스의 하위 범위를 분리합니다.

이러한 하위 범위는 원본에 대한 참조입니다. 컬렉션입니다. 이러한 하위 범위를 수정하면 원본 컬렉션에 영향을 미칩니다. 영향을 주지 않으려면 addAll과 같은 다른 방법을 사용하여 복사해야 합니다. 목록에서 예시 범위를 나누는 방법은 간단합니다.

package SetViews;import java.util.ArrayList;import java.util.Collections;import java.util.List;/**
 * 
 * @author QuinnNorris
 * 在列表中分例子范围,并进行操作
 */public class SubViews {

    /**
     * @param args
     */
    public static void main(String[] args) {        // TODO Auto-generated method stub

        List<Integer> al = new ArrayList<>();        //创建ArrayList对象al
        al.addAll(Collections.nCopies(15, 1));        //在al中设置15个1
        List subal = al.subList(5, 10);        
        //subList取出从第6个元素到第10个元素作为一个子范围,返回一个视图对象,我们将它存放到subal中。
        subal.set(0, 0);        //第6个数字改成0

        for(Integer i :al)
            System.out.println(i);        //除了第6个数字为0其他全为1

        subal.clear();//清除子范围

        for(Integer i :al)
            System.out.print(i);        //al列表中只有10个1,中间的五个被清除了
    }

}
로그인 후 복사

그 외에도 다른 정렬 세트 및 매핑 테이블에서는 요소 위치 대신 정렬 순서를 사용하여 하위 범위를 생성할 수 있습니다. 이러한 메서드는 SortedSet 및 SortedMap에 선언되어 있습니다.

SortedSet headSet(E toElement)
                   요소가 toElement보다 엄격하게 작은 이 세트의 부분 보기를 반환합니다.

SortedSet subSet(E fromElement, E toElement)
fromElement(포함)부터 toElement(제외)까지의 요소가 포함된 이 세트의 부분 보기를 반환합니다.

SortedSet tailSet(E fromElement)
                    요소가 fromElement보다 크거나 같은 이 집합의 부분 보기를 반환합니다.

위 세 가지 메소드는 SortedSet에 선언된 메소드로, TreeSet에서 사용할 수 있습니다. 마찬가지로 메서드의 모든 Set가 Map으로 대체되면 TreeMap에서 이 세 가지 유사한 메서드를 사용할 수 있습니다. Map의 모든 작업은 값이 아닌 키를 사용하여 수행된다는 점에 유의해야 합니다.

3. 수정 불가능한 안전 뷰

실생활에서 동료의 메소드에 컬렉션 객체를 전달하면 그 사람이 잘못된 것으로 판명됩니다. , 그것은 확실히 당신을 짜증나게 만들 것입니다. 그렇다면 이러한 문제를 어떻게 해결하고 데이터 구조를 더욱 안전하게 만들 수 있을까요? 뷰에서는 수정 불가능한 뷰를 사용하여 객체를 잠글 수 있습니다. 컬렉션을 수정하려고 하면 예외가 발생하고 컬렉션은 수정되지 않은 상태로 복원됩니다.

Collections 클래스에서는 수정 불가능한 뷰를 얻기 위한 여러 메서드를 제공합니다.

unmodifiableCollection   : 返回指定 collection 的不可修改视图(下同)。  

   unmodifiableList  

   unmodifiableMap  

   unmodifiableSet 

   unmodifiableSortedMap  

   unmodifiableSortedSet
로그인 후 복사


위 메서드는 매우 간단하고 명확합니다. 이러한 방법이 어떻게 작동하는지 구체적인 예를 살펴보겠습니다.

package SetViews;import java.util.ArrayList;import java.util.Collections;import java.util.List;/**
 * 
 * @author QuinnNorris
 * 不可修改的安全视图举例
 */public class UnmodifaiableViews {

    //这个是调用列表的方法
    private static void changeList(List<String> al) {
        al.set(0, "change");        //error Exception in thread "main" java.lang.UnsupportedOperationException
    }    public static void main(String[] args) {        // TODO Auto-generated method stub

        List<String> al = new ArrayList<>();        //创建ArrayList对象al
        al.addAll(Collections.nCopies(10, "final"));        //前十个元素填充“final”字符串
        changeList(al);        //先直接调用changeList方法,没问题
        changeList(Collections.unmodifiableList(al));        
        //报错,说明在changeList中对al进行了修改,被阻止了,而且抛出了异常
    }

}
로그인 후 복사

수정 불가능하다는 것은 컬렉션 개체를 더 이상 수정할 수 없다는 의미가 아니라 컬렉션의 정적 메서드를 적용할 때 수정할 수 없다는 의미입니다. 그러나 여기에는 암시적인 문제가 있습니다. 왜냐하면 정적 메서드를 사용한 후에 해당 형식이 뷰 형식이 되어 더 자세한 메서드를 사용할 수 없게 되기 때문입니다. 예를 들어 ArrrayList 객체가 Collections.unmodifyingList()로 패키징된 후 해당 유형이 List로 변경되고 일부 ArrayList 관련 메서드를 사용할 수 없다는 점에 유의하세요.

4. 동기화된 뷰

멀티 스레딩은 일상 작업에서 매우 일반적입니다. 실수로 접근하지 않도록 파괴하세요. 여기서는 멀티스레딩 문제에 대해 너무 많이 논의하고 싶지 않습니다. 컬렉션 클래스 라이브러리에서 디자이너는 뷰 메커니즘을 사용하여 일반 컬렉션의 스레드 안전성을 보장합니다. 예를 들어, Collecions 클래스의 정적 메소드synchronousMap은 매개변수로 전달된 Map 구현 클래스의 객체를 스레드로부터 안전한 Map으로 변환할 수 있습니다.

Map<String,Integer> map = Collections.synchronizedMap(new HashMap<String,Integer>());
로그인 후 복사

세 번째 지점의 수정 불가능 뷰와 유사한 유사한 방법이 많이 있으므로 여기서는 논의하지 않겠습니다.

5.检查视图

有的时候,我们在使用类似ArrayList等数据结构时并不使用泛型,从而会导致一些难以发现的类型错误。这个时候,如果我们使用了视图机制,用一种方法来不断地检查我们的代码就可以直接判断出错误的原因。

package SetViews;import java.util.ArrayList;import java.util.Collections;import java.util.Date;import java.util.List;/**
 * 
 * @author QuinnNorris
 * 动态类型安全视图是如何避免错误的发生举例
 */public class CheckViews {

    /**
     * @param args
     */
    public static void main(String[] args) {        // TODO Auto-generated method stub

        //--------------------发生错误的例子-----------------------

        ArrayList<String> al = new ArrayList<>();        //创建ArrayList对象al
        ArrayList errList = al;        //声明一个没有泛型的ArrayList,并把al赋值给它
        errList.add(new Date());        //这个列表本应该是String类型的,但是现在增加一个Date类型数据在编译运行时都不报错。
        //只有当对内部的数据进行操作时,编译器才有可能发现这个错误并抛出异常

        //-----------------动态类型安全视图机制----------------------

        List<String> safeAl = Collections.checkedList(al, String.class); 
        //调用Collections的静态方法checkedList,第一个参数是列表名,第二个参数是类型
        //这个方法返回一个List视图对象,如果要进行错误的操作,会抛出异常
        List errSafeAl = safeAl;        //再用无泛型的列表去引用检查视图的对象,因为al原本是ArrayList
        errSafeAl.add(new Date());        //现在运行时会报错,Attempt to insert class java.util.Date element into collection 
        //with element type class java.lang.String

    }

}
로그인 후 복사

视图是个好机制,它为我们提供了很多方便快捷的转换,安全等方面的操作,让我们对整个集合类库的构架有了个初步的理解。那么谈完了宏观的概念,我们来看一下实际操作中集合框架的一些细节的特性和机制。

(三)批操作

集合就是许许多多的数据在一起构成的。在实际应用中,处理这些海量的数据让我们非常的头痛,而且一旦需要操作的复杂一些,几遍for循环的时空复杂度直接以次方形式增长,这是我们绝对不想看到的。为了方便集合类中数据的操作,集合框架提供类一种叫做批操作的概念。

package SetViews;import java.util.HashSet;import java.util.Set;/**
 * 
 * @author QuinnNorris
 * 批操作查找两个集合的交集
 */public class BulkViews {

    /**
     * @param args
     */
    public static void main(String[] args) {        // TODO Auto-generated method stub

        Set<String> setA = new HashSet<>();
        Set<String> setB = new HashSet<>();        //创建集合A和集合B这是我们要比较的两个集合

        Set<String> result = new HashSet<>(setA);        //声明集合result存放结果,先用集合A来实例化这个集合

        result.retainAll(setB);        //调用retainAll方法,将B中与A相同的所有元素留下,达到查找交集的目的
    }

}
로그인 후 복사

这是一个比较简单的例子,它省略了for循环,而是采用这种批操作的概念来完成交集的查找。但是方法毕竟有限,我们能处理的问题还是在少数,如果真的想广泛的运用批操作,那么结合视图机制是必须的。因为视图机制可以在一定程度上,可以非常简单的进行类型的转换,通过子视图等方法在复杂度较低的情况下达到自己的目的。

(四)集合类的类型转换

1.集合与数组之间的转换

由于java平台API中的大部分内容都是在集合框架创建之前设计的,所以,有的时候的确需要在传统的数组和现代的集合之间进行转换。我们可以通过asList方法和toArray方法来解决这个问题。

package SetViews;import java.util.Arrays;import java.util.HashSet;/**
 * 
 * @author QuinnNorris
 * 集合与数组之间的转换
 */public class TypeChange {

    /**
     * @param args
     */
    public static void main(String[] args) {        // TODO Auto-generated method stub

        //---------------------数组转集合---------------------------

        String[] values = {"a","b"};        //创建一个普通数组
        HashSet<String> hsValues = new HashSet<>(Arrays.asList(values));        
        //通过Arrays类中静态方法asList,将普通数组转换成List类型的视图对象并实例化

        //---------------------集合转数组---------------------------

        HashSet<String> hsValue = new HashSet<>();        //创建一个集合
        Object[] objValues = hsValue.toArray();        
        //如果调用无参toArray方法,则返回一个Object数组,此时不能类型强转否则出错
        String[] strValues = hsValue.toArray(new String[0]);        
        //有参数的toArray方法如果参数数组的长度小于所需长度的话,重新分配一个能放得下大小的数组
        //我们通过有参数的toArray方法巧妙的将集合对象转换成相应类型的
    }

}
로그인 후 복사

2.Map和Set之间的转换

集合框架并没有把Map作为一个集合来看待,然而,我们可以获得映射表的视图,这个视图是一组实现了Collection接口的对象。大家都知道,Collection和Map是两个不相干的接口,如今Map的视图是Collection的对象,无疑是更加方便了我们的操作。Map接口为我们提供了三个方法,这三个方法返回三个不同的视图:

Set> entrySet()
返回此映射中包含的映射关系的 Set 视图。
Set keySet()
返回此映射中包含的的 Set 视图。
Collection values()
返回此映射中包含的的 Collection 视图。

这其中值得注意的是Map.Entry,很显然这是一个静态内部接口,这个接口中有几个最简单的方法:

K getKey()
返回与此项对应的键。
V getValue()
返回与此项对应的值。
V setValue(V value)
用指定的值替换与此项对应的值。

看完了这些方法,我们也就知道如何去将Map转型为其他,或者如何用V来获取K。

package SetViews;import java.util.HashMap;import java.util.Map;import java.util.Set;/**
 * 
 * @author QuinnNorris
 * 集合和映射表之间的转换
 */public class SetMap {

    /**
     * @param args
     */
    public static void main(String[] args) {        // TODO Auto-generated method stub

        Map<String, Integer> map = new HashMap<String, Integer>();        //创建一个Map对象
        Set<String> set = map.keySet();        //set是包含着map中所有键的内容的集合

        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();        
        //entrySet是包含着Map中静态内部接口Entry的类对象的集合

        for (Map.Entry<String, Integer> es : entrySet)
            System.out.println(es.getKey() + ":" + es.getValue());        
            //循环输出,或者也可以进行,按照Key查Value的操作
    }

}
로그인 후 복사

(五)算法与Collections类

一提到算法大家想到的可能是c语言中各式各样的排序,查找,二分等算法。但是在java集合类库中,并不需要这么麻烦。我们在Collections这个类中实现了几乎所有的简单算法,为了让程序员不会因为每次要实用算法时,都要自己编写一遍而感到苦恼。Collections这个类在上面也多次出现过了,这个类全部都是静态方法,而且它也确实只是在做一些包装性质的工作。这个类和视图密不可分。我们就来看一下Collections中的方法。

static <T> Queue<T> 
 asLifoQueue(Deque<T> deque) 
//以后进先出 (Lifo) Queue 的形式返回某个 Deque 的视图。 static <T> int 
 binarySearch(List<? extends T> list, T key, Comparator<? super T> c) 
//使用二分搜索法搜索指定列表,以获得指定对象。 如果没有找到对象,则返回一个i,应该插入的位置是-i-1static <T> void 
 copy(List<? super T> dest, List<? extends T> src) 
//将第二个参数列表的内容复制到第一个参数列表中,要保证第一个参数列表的长度是足够的static boolean disjoint(Collection<?> c1, Collection<?> c2) 
//如果两个指定 collection 中没有相同的元素,则返回 true。 static <T> void 
 fill(List<? super T> list, T obj) 
//使用指定元素替换指定列表中的所有元素。 static int frequency(Collection<?> c, Object o) 
//返回指定 collection 中等于指定对象的元素数。 static int indexOfSubList(List<?> source, List<?> target) 
//返回指定源列表中第一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1。 static int lastIndexOfSubList(List<?> source, List<?> target) 
//返回指定源列表中最后一次出现指定目标列表的起始位置;如果没有出现这样的列表,则返回 -1。 static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp) 
//根据指定比较器产生的顺序,返回给定 collection 的最大元素。 static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) 
//根据指定比较器产生的顺序,返回给定 collection 的最小元素。 static <T> List<T> nCopies(int n, T o) 
//返回由指定对象的 n 个副本组成的不可变列表。 static <T> boolean replaceAll(List<T> list, T oldVal, T newVal) 
//使用另一个值替换列表中出现的所有某一指定值。 static void reverse(List<?> list) 
//反转指定列表中元素的顺序。 static <T> Comparator<T> reverseOrder(Comparator<T> cmp) 
//返回一个比较器,它强行逆转指定比较器的顺序。 static void shuffle(List<?> list) 
//使用默认随机源对指定列表进行置换。 static <T> void sort(List<T> list, Comparator<? super T> c) 
//根据指定比较器产生的顺序对指定列表进行排序。 static void swap(List<?> list, int i, int j) 
//在指定列表的指定位置处交换元素。
로그인 후 복사

(六)总结

两篇集合的文章,从前到后系统的总结了一遍java集合类库的林林总总,感觉学习了很多。尤其是java这种框架的概念,框架把原本分散的一些有关联的东西整合起来,并且给了很多实用的方法,整体上给人一种有血有肉很饱满的感觉。我们在日常使用框架的时候也很有必要去研究一下框架是如何搭建起来的,不仅可以让我们更加熟练的运用,而且能学到设计者的一些优秀的思想。

 以上就是java集合(二)—集合框架与算法详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!


원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿