001/* 002 * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.compiler.common.util; 024 025import java.util.*; 026 027/** 028 * Creates an array of T objects order by the occurrence frequency of each object. The most 029 * frequently used object is the first one, the least frequently used the last one. If {@code null} 030 * is added, it is always the first element. 031 * 032 * Either object {@link #createIdentityEncoder() identity} or object 033 * {@link #createEqualityEncoder() equality} can be used to build the array and count the frequency. 034 */ 035public class FrequencyEncoder<T> { 036 037 static class Entry<T> { 038 protected final T object; 039 protected int frequency; 040 protected int index; 041 042 protected Entry(T object) { 043 this.object = object; 044 this.index = -1; 045 } 046 } 047 048 protected final Map<T, Entry<T>> map; 049 protected boolean containsNull; 050 051 /** 052 * Creates an encoder that uses object identity. 053 */ 054 public static <T> FrequencyEncoder<T> createIdentityEncoder() { 055 return new FrequencyEncoder<>(new IdentityHashMap<>()); 056 } 057 058 /** 059 * Creates an encoder that uses {@link Object#equals(Object) object equality}. 060 */ 061 public static <T> FrequencyEncoder<T> createEqualityEncoder() { 062 return new FrequencyEncoder<>(new HashMap<>()); 063 } 064 065 protected FrequencyEncoder(Map<T, Entry<T>> map) { 066 this.map = map; 067 } 068 069 /** 070 * Adds an object to the array. 071 */ 072 public void addObject(T object) { 073 if (object == null) { 074 containsNull = true; 075 return; 076 } 077 078 Entry<T> entry = map.get(object); 079 if (entry == null) { 080 entry = new Entry<>(object); 081 map.put(object, entry); 082 } 083 entry.frequency++; 084 } 085 086 /** 087 * Returns the index of an object in the array. The object must have been 088 * {@link #addObject(Object) added} before. 089 */ 090 public int getIndex(Object object) { 091 if (object == null) { 092 assert containsNull; 093 return 0; 094 } 095 Entry<T> entry = map.get(object); 096 assert entry != null && entry.index >= 0; 097 return entry.index; 098 } 099 100 /** 101 * Returns the number of distinct objects that have been added, i.e., the length of the array. 102 */ 103 public int getLength() { 104 return map.size() + (containsNull ? 1 : 0); 105 } 106 107 /** 108 * Fills the provided array with the added objects. The array must have the {@link #getLength() 109 * correct length}. 110 */ 111 public T[] encodeAll(T[] allObjects) { 112 assert allObjects.length == getLength(); 113 List<Entry<T>> sortedEntries = new ArrayList<>(map.values()); 114 sortedEntries.sort((e1, e2) -> -Integer.compare(e1.frequency, e2.frequency)); 115 116 int offset = 0; 117 if (containsNull) { 118 allObjects[0] = null; 119 offset = 1; 120 } 121 for (int i = 0; i < sortedEntries.size(); i++) { 122 Entry<T> entry = sortedEntries.get(i); 123 int index = i + offset; 124 entry.index = index; 125 allObjects[index] = entry.object; 126 assert entry.object != null; 127 } 128 return allObjects; 129 } 130}