찾다
Javajava지도 시간자바 프로그래밍 사고 학습 수업(4) 17장 - 컨테이너에 대한 심층 토론

1 집합 및 저장 순서

  • SetSet的元素必须定义equals()方法以确保对象的唯一性

  • hashCode()只有这个类被置于HashSet或者LinkedHashSet中时才是必需的。但是对于良好的编程风格而言,你应该在覆盖equals()方法时,总是同时覆盖hashCode()方法。

  • 如果一个对象被用于任何种类的排序容器中,例如SortedSetTreeSet是其唯一实现),那么它必须实现Comparable接口。

  • 注意,SortedSet的意思是“按对象的比较函数对元素排序”,而不是指“元素插入的次序”。插入顺序LinkedHashSet来保存。

2 队列

  • 队了并发应用,Queue在Java SE5中仅有的两个实现是LinkiedListPriorityQueue,它们仅有排序行为的差异,性能上没有差异。

  • 优先级队列PriorityQueue的排列顺序也是通过实现Comparable而进行控制的。

3 Map

  映射表(也称为关联数组Associative Array)。

3.1 性能

  HashMap使用了特殊的值,称作散列码(hash code),来取代对键的缓慢搜索。散列码是“相对唯一”的、用以代表对象的int值,它是通过将该对象的某些信息进行转换而生成的。
hashCode()是根类Object中的方法,因此所有对象都能产生散列码。

  对Map中使用的键的要求与对Set中的元素的要求一样:

  • 任何键都必须具有一个equals()方法;

  • 如果键被用于散列Map,那么它必须还具有恰当的hashCode()方法;

  • 如果键被用于TreeMap,那么它必须实现Comparable

4 散列与散列码

  HashMap使用equals()判断当前的键是否与表中存在的键相同。
  默认的Object.equals()只是比较对象的地址如果要使用自己的类作为HashMap的键,必须同时重写hashCode()equals()
  正确的equals()方法必须满足下列5个条件:

  • 自反性。

  • 对称性。

  • 传递性。

  • 一致性。

  • 对任何不是null的xx.equals(null)一定返回false

4.1 散列概念

  使用散列的目的在于:想要使用一个对象来查找另一个对象
  Map的实现类使用散列是为了提高查询速度

散列的价值在于速度散列使得查询得以快速进行。由于瓶颈位于查询速度,因此解决方案之一就是保持键的排序状态,然后使用Collections.binarySearch()에 추가된 요소는

equals()

메서드를 정의하여 객체가 독특한 섹스입니다.

🎜🎜🎜hashCode()🎜이 클래스는 HashSet 또는 LinkedHashSet에 배치된 경우에만 필요합니다. 그러나 좋은 프로그래밍 스타일의 문제로, equals() 메서드를 재정의할 때는 항상 hashCode() 메서드를 재정의해야 합니다. 🎜🎜🎜SortedSet(이 중 TreeSet이 유일한 구현임)과 같은 모든 종류의 🎜🎜정렬된 컨테이너🎜🎜에서 개체가 사용되는 경우 그런 다음 🎜Comparable🎜 인터페이스를 구현해야 합니다. 🎜🎜🎜 🎜SortedSet🎜은 "요소가 삽입되는 순서"가 아니라 "객체의 🎜🎜 비교 함수🎜🎜에 따라 요소를 정렬하는 것"을 의미합니다. 🎜🎜삽입 순서🎜🎜는 🎜LinkedHashSet🎜에 저장됩니다. 🎜

2 Queue

🎜🎜🎜Java SE5에서 Queue의 유일한 두 가지 구현은 🎜LinkedList🎜 및 🎜 입니다. PriorityQueue🎜는 🎜정렬 동작🎜만 다를 뿐 성능에는 차이가 없습니다. 🎜🎜🎜우선순위 큐 PriorityQueue의 순서도 🎜Comparable🎜 구현을 통해 제어됩니다. 🎜

3 맵

🎜 — 🎜🎜Map table🎜🎜(🎜associative array🎜🎜Associative Array🎜이라고도 함). 🎜

3.1 성능

🎜 HashMap은 🎜🎜hash code🎜🎜(해시 코드)라는 특수 값을 사용하여 느린 키 검색을 대체합니다. 해시 코드는 객체를 나타내는 데 사용되는 🎜🎜 "상대적으로 고유한" 🎜🎜 int 값으로, 객체에 대한 🎜특정 정보를 변환하여 생성됩니다. 🎜hashCode()는 루트 클래스 Object의 메소드이므로 모든 객체가 해시 코드를 생성할 수 있습니다. 🎜🎜 Map에 사용되는 키에 대한 요구 사항은 Set의 요소에 대한 요구 사항과 동일합니다. 🎜🎜🎜🎜모든 키에는 🎜equals()🎜 메서드가 있어야 합니다. 🎜🎜 🎜If 키가 해시된 맵에 사용되는 경우 적절한 🎜hashCode()🎜메서드도 있어야 합니다. 🎜🎜🎜키가 🎜TreeMap에 사용되는 경우 code>🎜인 경우 🎜<code>Comparable🎜을 구현해야 합니다. 🎜

4 해시 및 해시 코드

🎜 HashMap은 🎜equals()🎜를 사용하여 현재 키가 존재하는 키와 동일한지 확인합니다. 테이블에. 🎜 🎜기본 Object.equals()는 객체의 주소만 비교합니다 🎜. 🎜자신의 클래스를 HashMap🎜의 키로 사용하려면 🎜🎜hashCode()equals()🎜🎜을 동시에 재정의해야 합니다. 🎜 올바른 equals() 메서드는 다음 5가지 조건을 충족해야 합니다: 🎜🎜🎜🎜재귀성. 🎜🎜🎜대칭. 🎜🎜🎜전환성. 🎜🎜🎜일관성. 🎜🎜🎜null이 아닌 모든 x의 경우 x.equals(null)false를 반환해야 합니다. 🎜

4.1 해시 개념

🎜 해싱을 사용하는 목적은 다음과 같습니다. 🎜한 객체를 사용하여 다른 객체를 찾고 싶습니다🎜. 🎜 Map의 구현 클래스는 해싱을 사용하여 🎜🎜쿼리 속도를 높입니다🎜🎜. 🎜
🎜🎜해싱의 가치는 속도입니다🎜: 🎜해싱은 쿼리를 빠르게 만듭니다🎜. 병목 현상이 🎜🎜쿼리 속도🎜🎜에 있으므로 해결 방법 중 하나는 키를 정렬된 상태로 유지한 다음 Collections.binarySearch()를 사용하여 쿼리하는 것입니다. 🎜🎜🎜🎜해싱은 한 단계 더 발전하여 키를 빠르게 찾을 수 있도록 어딘가에 키를 저장합니다🎜🎜. 요소 집합을 저장하는 가장 빠른 데이터 구조는 배열이므로 이를 사용하여 키 정보를 나타냅니다(키 자체가 아니라 키 정보를 의미한다는 점에 주의하세요). 하지만 배열이 용량을 조정할 수 없기 때문에 문제가 있습니다. 불확실한 수의 값을 맵에 저장하고 싶지만 배열의 용량에 따라 키 수가 제한되면 어떻게 될까요? 🎜

答案就是:数组并不保存键本身。而是通过键对象生成一个数字,将其作为数组的下标。这个数字就是散列码,由定义在Object中的、且可能由你的类覆盖的hashCode()方法(在计算机科学的术语中称为散列函数)生成。

为解决数组容量固定的问题,不同的键可以产生相同的下标。也就是说,可能会有冲突,即散列码不必是独一无二的。因此,数组多大就不重要了,任何键总能在数组中找到它的位置。

4.2 理解散列

  综上,散列就是将一个对象生成一个数字保存下来(作为数组的下标),然后在查找这个对象时直接找到这个数字就可以了,所以散列的目的是为了提高查找速度,而手段是将一个对象生成的数字与其关联并保存下来(通过数组,称为散列表)。这个生成的数字就是散列码。而生成这个散列码的方法称为散列函数hashCode())。

4.3 HashMap查询过程(快速原因)

  因此,HashMap中查询一个key的过程就是:

  • 首先计算散列码

  • 然后使用散列码查询数组(散列码作变数组下标)

  • 如果没有冲突,即生成这个散列码的对象只有一个,则散列码对应的数组下标的位置就是这个要查找的元素

  • 如果有冲突,则散列码对应的下标所在数组元素保存的是一个list,然后对list中的值使用equals()方法进行线性查询。

  因此,不是查询整个list,而是快速地跳到数组的某个位置,只对很少的元素进行比较。这便是HashMap会如此快速的原因

4.4 简单散列Map的实现

  • 散列表中的槽位(slot)通常称为桶位(bucket)

  • 为使散列均匀,桶的数量通常使用质数JDK5中是质数,JDK7中已经是2的整数次方了)。

    事实证明,质数实际上并不是散列桶的理想容量。近来,(通过广泛的测试)Java的散列函数都使用2的整数次方。对现代处理器来说,除法与求余数是最慢的操作。使用2的整数次方长度的散列表,可用掩码代替除法。因为get()是使用最多的操作,求余数的%操作是其开销最大的部分,而使用2的整数次方可以消除此开销(也可能对hashCode()有些影响)。
  • get()方法按照与put()方法相同的方式计算在buckets数组中的索引,这很重要,因为这样可以保证两个方法可以计算出相同的位置

package net.mrliuli.containers;

import java.util.*;public class SimpleHashMap<K, V> extends AbstractMap<K, V> {    // Choose a prime number for the hash table size, to achieve a uniform distribution:
    static final int SIZE = 997;    // You can&#39;t have a physical array of generics, but you can upcast to one:
    @SuppressWarnings("unchecked")
    LinkedList<MapEntry<K,V>>[] buckets = new LinkedList[SIZE];

    @Override    public V put(K key, V value){        int index = Math.abs(key.hashCode()) % SIZE;        if(buckets[index] == null){
            buckets[index] = new LinkedList<MapEntry<K,V>>();
        }

        LinkedList<MapEntry<K,V>> bucket = buckets[index];
        MapEntry<K,V> pair = new MapEntry<K,V>(key, value);

        boolean found = false;
        V oldValue = null;
        ListIterator<MapEntry<K,V>> it = bucket.listIterator();        while(it.hasNext()){
            MapEntry<K,V> iPair = it.next();            if(iPair.equals(key)){
                oldValue = iPair.getValue();
                it.set(pair); // Replace old with new
                found = true;                break;
            }
        }        if(!found){
            buckets[index].add(pair);
        }        return oldValue;
    }

    @Override    public V get(Object key){        int index = Math.abs(key.hashCode()) % SIZE;        if(buckets[index] == null) return null;        for(MapEntry<K,V> iPair : buckets[index]){            if(iPair.getKey().equals(key)){                return iPair.getValue();
            }
        }        return null;
    }

    @Override    public Set<Map.Entry<K,V>> entrySet(){
        Set<Map.Entry<K,V>> set = new HashSet<Map.Entry<K, V>>();        for(LinkedList<MapEntry<K,V>> bucket : buckets){            if(bucket == null) continue;            for(MapEntry<K,V> mpair : bucket){                set.add(mpair);
            }
        }        return set;
    }    public static void main(String[] args){
        SimpleHashMap<String, String> m = new SimpleHashMap<String, String>();        for(String s : "to be or not to be is a question".split(" ")){
            m.put(s, s);
            System.out.println(m);
        }
        System.out.println(m);
        System.out.println(m.get("be"));
        System.out.println(m.entrySet());
    }
}

4.5 覆盖hashCode()

  设计`hashCode()`时要考虑的因素:

  • 最重要的因素:无论何时,对同一相对象调用hashCode()都应该生成同样的值

  • 此外,不应该使hashCode()依赖于具有唯一性的对象信息,尤其是使用this的值,这只能产生很糟糕的hashCode()。因为这样做无法生成一个新的键,使之与put()中原始的键值对中的键相同。即应该使用对象内有意义的识别信息。也就是说,它必须基于对象的内容生成散列码。

  • 但是,通过hashCode() equals()必须能够完全确定对象的身份。

  • 因为在生成桶的下标前,hashCode()还需要进一步处理,所以散列码的生成范围并不重要,只要是int即可。

  • 好的hashCode()应该产生分布均匀的散列码。

《Effective Java™ Programming Language Guide (Addison-Wesley, 2001)》为怎样写出一个像样的hashCode()给出了一个基本的指导:

  1. int变量result赋予一个非零值常量,如17

  2. 为对象内每个有意义的域f(即每个可以做equals()操作的域)计算出一个int散列码c

域类型 计算
boolean c=(f?0:1)
byte、char、short或int c=(int)f
long c=(int)(f^(f>>>32))
float c=Float.floatToIntBits(f);
double long l = Double.doubleToLongBits(f);
Object,其equals()调用这个域的equals() c=f.hashCode()
数组 对每个元素应用上述规则

3. 合并计算散列码:result = 37 * result + c;
4. 返回result。
5. 检查hashCode()最后生成的结果,确保相同的对象有相同的散列码。

5 选择不同接口的实现

5.1 微基准测试的危险(Microbenchmarking dangers)

已证明0.0是包含在Math.random()的输出中的,按照数学术语,即其范围是[0,1)

5.2 HashMap的性能因子

  HashMap中的一些术语:

  • 容量(Capacity):表中的桶位数(The number of buckets in the table)。

  • 初始容量(Initial capacity):表在创建时所拥有的桶位数。HashMapHashSet都具有允许你指定初始容量的构造器。

  • 尺寸(Size):表中当前存储的项数。

  • 负载因子(Loadfactor):尺寸/容量。空表的负载因子是0,而半满表的负载因子是0.5,依此类推。负载轻的表产生冲突的可能性小,因此对于插入和查找都是最理想的(但是会减慢使用迭代器进行遍历的过程)。HashMapHashSet都具有允许你指定负载因子的构造器,表示当负载情况达到该负载的水平时,容器将自动增加其容量(桶位数),实现方式是使容量大致加倍,并重新将现有对象分布到新的桶位集中(这被称为再散列)。

HashMap使用的默认负载因子是0.75(只有当表达到四分之三满时,才进行再散列),这个因子在时间和空间代价之间达到了平衡。更高的负载因子可以降低表所需的空间,但会增加查找代价,这很重要,因为查找是我们在大多数时间里所做的操作(包括get()put())。

6 Collection或Map的同步控制

  Collections类有办法能够自动同步整个容器。其语法与“不可修改的”方法相似:

package net.mrliuli.containers;

import java.util.*;public class Synchronization {    public static void main(String[] args){
        Collection<String> c = Collections.synchronizedCollection(new ArrayList<String>());
        List<String> list = Collections.synchronizedList(new ArrayList<String>());        Set<String> s = Collections.synchronizedSet(new HashSet<String>());        Set<String> ss = Collections.synchronizedSortedSet(new TreeSet<String>());
        Map<String, String> m = Collections.synchronizedMap(new HashMap<String, String>());
        Map<String, String> sm = Collections.synchronizedSortedMap(new TreeMap<String, String>());
    }
}

6.1 快速报错(fail-fast)

  Java容器有一种保护机制能够防止多个进行同时修改同一个容器的内容。Java容器类类库采用快速报错(fail-fast)机制。它会探查容器上的任何除了你的进程所进行的操作以外的所有变化,一旦它发现其他进程修改了容器,就会立刻抛出ConcurrentModificationException异常。这就是“快速报错”的意思——即,不是使用复杂的算法在事后来检查问题。

package net.mrliuli.containers;
import java.util.*;public class FailFast {    public static void main(String[] args){
        Collection<String> c = new ArrayList<>();
        Iterator<String> it = c.iterator();
        c.add("An Object");        try{
            String s = it.next();
        }catch(ConcurrentModificationException e){
            System.out.println(e);
        }
    }
}

相关文章:

Java编程思想学习课时(三)第15章-泛型

Java编程思想学习课时(五)第18章-Java IO系统

위 내용은 자바 프로그래밍 사고 학습 수업(4) 17장 - 컨테이너에 대한 심층 토론의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
고급 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 또는 Gradle을 어떻게 사용합니까?고급 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 또는 Gradle을 어떻게 사용합니까?Mar 17, 2025 pm 05:46 PM

이 기사에서는 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 및 Gradle을 사용하여 접근 방식과 최적화 전략을 비교합니다.

적절한 버전 및 종속성 관리로 Custom Java 라이브러리 (JAR Files)를 작성하고 사용하려면 어떻게해야합니까?적절한 버전 및 종속성 관리로 Custom Java 라이브러리 (JAR Files)를 작성하고 사용하려면 어떻게해야합니까?Mar 17, 2025 pm 05:45 PM

이 기사에서는 Maven 및 Gradle과 같은 도구를 사용하여 적절한 버전 및 종속성 관리로 사용자 정의 Java 라이브러리 (JAR Files)를 작성하고 사용하는 것에 대해 설명합니다.

카페인 또는 구아바 캐시와 같은 라이브러리를 사용하여 자바 애플리케이션에서 다단계 캐싱을 구현하려면 어떻게해야합니까?카페인 또는 구아바 캐시와 같은 라이브러리를 사용하여 자바 애플리케이션에서 다단계 캐싱을 구현하려면 어떻게해야합니까?Mar 17, 2025 pm 05:44 PM

이 기사는 카페인 및 구아바 캐시를 사용하여 자바에서 다단계 캐싱을 구현하여 응용 프로그램 성능을 향상시키는 것에 대해 설명합니다. 구성 및 퇴거 정책 관리 Best Pra와 함께 설정, 통합 및 성능 이점을 다룹니다.

캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA (Java Persistence API)를 어떻게 사용하려면 어떻게해야합니까?캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA (Java Persistence API)를 어떻게 사용하려면 어떻게해야합니까?Mar 17, 2025 pm 05:43 PM

이 기사는 캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA를 사용하는 것에 대해 설명합니다. 잠재적 인 함정을 강조하면서 성능을 최적화하기위한 설정, 엔티티 매핑 및 모범 사례를 다룹니다. [159 문자]

Java의 클래스로드 메커니즘은 다른 클래스 로더 및 대표 모델을 포함하여 어떻게 작동합니까?Java의 클래스로드 메커니즘은 다른 클래스 로더 및 대표 모델을 포함하여 어떻게 작동합니까?Mar 17, 2025 pm 05:35 PM

Java의 클래스 로딩에는 부트 스트랩, 확장 및 응용 프로그램 클래스 로더가있는 계층 적 시스템을 사용하여 클래스로드, 링크 및 초기화 클래스가 포함됩니다. 학부모 위임 모델은 핵심 클래스가 먼저로드되어 사용자 정의 클래스 LOA에 영향을 미치도록합니다.

분산 컴퓨팅에 Java의 RMI (원격 메소드 호출)를 어떻게 사용할 수 있습니까?분산 컴퓨팅에 Java의 RMI (원격 메소드 호출)를 어떻게 사용할 수 있습니까?Mar 11, 2025 pm 05:53 PM

이 기사에서는 분산 응용 프로그램을 구축하기위한 Java의 원격 메소드 호출 (RMI)에 대해 설명합니다. 인터페이스 정의, 구현, 레지스트리 설정 및 클라이언트 측 호출을 자세히 설명하여 네트워크 문제 및 보안과 같은 문제를 해결합니다.

네트워크 통신에 Java의 Sockets API를 어떻게 사용합니까?네트워크 통신에 Java의 Sockets API를 어떻게 사용합니까?Mar 11, 2025 pm 05:53 PM

이 기사는 네트워크 통신을위한 Java의 소켓 API, 클라이언트 서버 설정, 데이터 처리 및 리소스 관리, 오류 처리 및 보안과 같은 중요한 고려 사항에 대해 자세히 설명합니다. 또한 성능 최적화 기술, i

Java에서 사용자 정의 네트워킹 프로토콜을 어떻게 만들 수 있습니까?Java에서 사용자 정의 네트워킹 프로토콜을 어떻게 만들 수 있습니까?Mar 11, 2025 pm 05:52 PM

이 기사에서는 맞춤형 Java 네트워킹 프로토콜을 작성합니다. 프로토콜 정의 (데이터 구조, 프레임, 오류 처리, 버전화), 구현 (소켓 사용), 데이터 직렬화 및 모범 사례 (효율성, 보안, Mainta를 포함합니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

안전한 시험 브라우저

안전한 시험 브라우저

안전한 시험 브라우저는 온라인 시험을 안전하게 치르기 위한 보안 브라우저 환경입니다. 이 소프트웨어는 모든 컴퓨터를 안전한 워크스테이션으로 바꿔줍니다. 이는 모든 유틸리티에 대한 액세스를 제어하고 학생들이 승인되지 않은 리소스를 사용하는 것을 방지합니다.

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

MinGW - Windows용 미니멀리스트 GNU

MinGW - Windows용 미니멀리스트 GNU

이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

PhpStorm 맥 버전

PhpStorm 맥 버전

최신(2018.2.1) 전문 PHP 통합 개발 도구