001/* 002 * Copyright (c) 2015-2020, Oracle and/or its affiliates. All rights reserved. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.tribuo.util.infotheory.impl; 018 019import java.io.Serializable; 020import java.util.Objects; 021import java.util.logging.Logger; 022 023/** 024 * A triple of things. The inner pairs are cached, as is the hashcode. 025 * <p> 026 * The cache is calculated on construction, and the objects inside the triple are thus expected to be immutable. 027 * If they aren't then the behaviour is undefined (and you shouldn't use this class). 028 * @param <T1> The type of the first object. 029 * @param <T2> The type of the second object. 030 * @param <T3> The type of the third object. 031 */ 032public class CachedTriple<T1, T2, T3> implements Serializable { 033 private static final long serialVersionUID = 1L; 034 035 private static final Logger logger = Logger.getLogger(CachedTriple.class.getName()); 036 037 private final CachedPair<T1,T2> ab; 038 private final CachedPair<T1,T3> ac; 039 private final CachedPair<T2,T3> bc; 040 041 /** 042 * The first element. 043 */ 044 protected final T1 a; 045 /** 046 * The second element. 047 */ 048 protected final T2 b; 049 /** 050 * The third element. 051 */ 052 protected final T3 c; 053 054 private final int cachedHash; 055 056 /** 057 * Constructs a CachedTriple. 058 * @param a The first element. 059 * @param b The second element. 060 * @param c The third element. 061 */ 062 public CachedTriple(T1 a, T2 b, T3 c) { 063 this.a = a; 064 this.b = b; 065 this.c = c; 066 this.ab = new CachedPair<>(a,b); 067 this.ac = new CachedPair<>(a,c); 068 this.bc = new CachedPair<>(b,c); 069 this.cachedHash = calculateHashCode(); 070 } 071 072 /** 073 * Gets the first element. 074 * @return The first element. 075 */ 076 public T1 getA() { 077 return a; 078 } 079 080 /** 081 * Gets the second element. 082 * @return The second element. 083 */ 084 public T2 getB() { 085 return b; 086 } 087 088 /** 089 * Gets the third element. 090 * @return The third element. 091 */ 092 public T3 getC() { 093 return c; 094 } 095 096 /** 097 * Gets the pair of the first and second elements. 098 * @return A pair of the first and second elements. 099 */ 100 public CachedPair<T1,T2> getAB() { 101 return ab; 102 } 103 104 /** 105 * Gets the pair of the first and third elements. 106 * @return A pair of the first and third elements. 107 */ 108 public CachedPair<T1,T3> getAC() { 109 return ac; 110 } 111 112 /** 113 * Gets the pair of the second and third elements. 114 * @return A pair of the second and third elements. 115 */ 116 public CachedPair<T2,T3> getBC() { 117 return bc; 118 } 119 120 /** 121 * Used to mix the integers in hashcode. 122 * Returns the 32 high bits of Stafford variant 4 mix64 function as int. 123 */ 124 private static int mix32(long z) { 125 z *= 0x62a9d9ed799705f5L; 126 return (int)(((z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L) >>> 32); 127 } 128 129 @Override 130 public int hashCode() { 131 return cachedHash; 132 } 133 134 /** 135 * Overridden hashcode. Checks to see if the types are ints or longs, and 136 * runs them through the mixing function if 137 * they are. Then XORs the two hashcodes together. 138 * @return A 32-bit integer. 139 */ 140 public int calculateHashCode() { 141 int aCode, bCode, cCode; 142 143 if (a instanceof Integer) { 144 aCode = mix32((Integer) a); 145 } else if (a instanceof Long) { 146 aCode = mix32((Long) a); 147 } else { 148 aCode = a.hashCode(); 149 } 150 151 if (b instanceof Integer) { 152 bCode = mix32((Integer) b); 153 } else if (b instanceof Long) { 154 bCode = mix32((Long) b); 155 } else { 156 bCode = b.hashCode(); 157 } 158 159 if (c instanceof Integer) { 160 cCode = mix32((Integer) c); 161 } else if (c instanceof Long) { 162 cCode = mix32((Long) c); 163 } else { 164 cCode = c.hashCode(); 165 } 166 167 return (aCode ^ bCode) ^ cCode; 168 } 169 170 @Override 171 public boolean equals(Object obj) { 172 if(obj == null) { 173 return false; 174 } 175 if(!(obj instanceof CachedTriple)) { 176 return false; 177 } 178 final CachedTriple<?,?,?> other = (CachedTriple<?,?,?>) obj; 179 if(!Objects.equals(this.a, other.a)) { 180 return false; 181 } 182 if(!Objects.equals(this.b, other.b)) { 183 return false; 184 } 185 return Objects.equals(this.c, other.c); 186 } 187 188 @Override 189 public String toString() { 190 return "Triple{" + "a=" + a + ", b=" + b + ", c=" + c + '}'; 191 } 192}