001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.function; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022 023import org.apache.juneau.commons.lang.*; 024import org.apache.juneau.commons.utils.*; 025 026/** 027 * Represents an immutable tuple containing two values. 028 * 029 * <p> 030 * This class provides a simple wrapper for two values that properly implements 031 * {@link #equals(Object)} and {@link #hashCode()} based on content rather than identity. 032 * This is particularly useful for HashMap composite keys when you need to combine multiple 033 * values into a single key. 034 * 035 * <h5 class='section'>Features:</h5> 036 * <ul class='spaced-list'> 037 * <li>Immutable - values cannot be changed after construction 038 * <li>Content-based equality - uses {@link Shorts#eq(Object, Object)} for comparison 039 * <li>Content-based hashing - uses {@link HashCode#of(Object...)} for hash code computation 040 * <li>Array support - properly handles arrays with content-based equality and hashing 041 * </ul> 042 * 043 * <h5 class='section'>Array Support:</h5> 044 * <p> 045 * Unlike using arrays directly as HashMap keys (which use identity-based equality), this class properly 046 * handles arrays by using content-based equality and hashing via {@link HashCode#of(Object...)} which 047 * internally uses {@link Arrays#hashCode(Object[])} for arrays. This means two Tuple2 instances 048 * containing arrays with the same content will be considered equal. 049 * 050 * <h5 class='section'>Use Cases:</h5> 051 * <ul class='spaced-list'> 052 * <li>Composite keys for HashMap lookups (e.g., combining two identifiers) 053 * <li>Using arrays in composite keys with proper content-based equality 054 * <li>Returning multiple values from methods (alternative to creating a custom class) 055 * <li>Grouping related values together for processing 056 * </ul> 057 * 058 * <h5 class='section'>Usage:</h5> 059 * <p class='bjava'> 060 * <jc>// Using arrays in composite keys</jc> 061 * Map<Tuple2<String,int[]>,Result> <jv>map</jv> = <jk>new</jk> HashMap<>(); 062 * <jv>map</jv>.put(Tuple2.<jsm>of</jsm>(<js>"key"</js>, <jk>new</jk> <jk>int</jk>[]{1,2,3}), <jv>result1</jv>); 063 * Result <jv>r</jv> = <jv>map</jv>.get(Tuple2.<jsm>of</jsm>(<js>"key"</js>, <jk>new</jk> <jk>int</jk>[]{1,2,3})); <jc>// Works correctly!</jc> 064 * 065 * <jc>// Simple composite key</jc> 066 * Map<Tuple2<String,Integer>,String> <jv>cache</jv> = <jk>new</jk> HashMap<>(); 067 * <jv>cache</jv>.put(Tuple2.<jsm>of</jsm>(<js>"user"</js>, 12345), <js>"John Doe"</js>); 068 * String <jv>name</jv> = <jv>cache</jv>.get(Tuple2.<jsm>of</jsm>(<js>"user"</js>, 12345)); <jc>// Returns "John Doe"</jc> 069 * </p> 070 * 071 * <h5 class='section'>Thread Safety:</h5> 072 * <p> 073 * This class is immutable and therefore thread-safe. Multiple threads can safely access a Tuple2 instance 074 * concurrently without synchronization. 075 * 076 * <h5 class='section'>See Also:</h5><ul> 077 * <li class='jc'>{@link Tuple1} - Single-value tuple 078 * <li class='jc'>{@link Tuple3} - Three-value tuple 079 * <li class='jc'>{@link Tuple4} - Four-value tuple 080 * <li class='jc'>{@link Tuple5} - Five-value tuple 081 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommons">juneau-commons</a> 082 * </ul> 083 * 084 * @param <A> The type of the first value in this tuple. 085 * @param <B> The type of the second value in this tuple. 086 */ 087public class Tuple2<A,B> { 088 089 /** 090 * Creates a new tuple containing the specified two values. 091 * 092 * <h5 class='section'>Example:</h5> 093 * <p class='bjava'> 094 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"name"</js>, 42); 095 * Tuple2<String[],int[]> <jv>arrayTuple</jv> = Tuple2.<jsm>of</jsm>( 096 * <jk>new</jk> String[]{<js>"a"</js>, <js>"b"</js>}, 097 * <jk>new</jk> <jk>int</jk>[]{1, 2} 098 * ); 099 * </p> 100 * 101 * @param <A> The type of the first value. 102 * @param <B> The type of the second value. 103 * @param a The first value. Can be <jk>null</jk>. 104 * @param b The second value. Can be <jk>null</jk>. 105 * @return A new tuple containing the specified values. 106 */ 107 public static <A,B> Tuple2<A,B> of(A a, B b) { 108 return new Tuple2<>(a, b); 109 } 110 111 private final A a; 112 private final B b; 113 private final int hashCode; 114 115 /** 116 * Constructor. 117 * 118 * @param a Object 1. Can be <jk>null</jk>. 119 * @param b Object 2. Can be <jk>null</jk>. 120 */ 121 public Tuple2(A a, B b) { 122 this.a = a; 123 this.b = b; 124 this.hashCode = h(a, b); 125 } 126 127 @Override /* Overridden from Object */ 128 public boolean equals(Object o) { 129 return o instanceof Tuple2<?,?> o2 && eq(this, o2, (x, y) -> eq(x.a, y.a) && eq(x.b, y.b)); 130 } 131 132 /** 133 * Returns the first value in this tuple. 134 * 135 * <h5 class='section'>Example:</h5> 136 * <p class='bjava'> 137 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, 42); 138 * String <jv>first</jv> = <jv>tuple</jv>.getA(); <jc>// Returns "hello"</jc> 139 * </p> 140 * 141 * @return The first value in this tuple. 142 */ 143 public A getA() { return a; } 144 145 /** 146 * Returns the second value in this tuple. 147 * 148 * <h5 class='section'>Example:</h5> 149 * <p class='bjava'> 150 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, 42); 151 * Integer <jv>second</jv> = <jv>tuple</jv>.getB(); <jc>// Returns 42</jc> 152 * </p> 153 * 154 * @return The second value in this tuple. 155 */ 156 public B getB() { return b; } 157 158 /** 159 * Returns the first value in this tuple wrapped in an {@link Optional}. 160 * 161 * <h5 class='section'>Example:</h5> 162 * <p class='bjava'> 163 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, 42); 164 * Optional<String> <jv>first</jv> = <jv>tuple</jv>.optA(); <jc>// Returns Optional.of("hello")</jc> 165 * 166 * Tuple2<String,Integer> <jv>tuple2</jv> = Tuple2.<jsm>of</jsm>(<jk>null</jk>, 42); 167 * Optional<String> <jv>first2</jv> = <jv>tuple2</jv>.optA(); <jc>// Returns Optional.empty()</jc> 168 * </p> 169 * 170 * @return The first value wrapped in an Optional, or Optional.empty() if the value is null. 171 */ 172 public Optional<A> optA() { 173 return o(a); 174 } 175 176 /** 177 * Returns the second value in this tuple wrapped in an {@link Optional}. 178 * 179 * <h5 class='section'>Example:</h5> 180 * <p class='bjava'> 181 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, 42); 182 * Optional<Integer> <jv>second</jv> = <jv>tuple</jv>.optB(); <jc>// Returns Optional.of(42)</jc> 183 * 184 * Tuple2<String,Integer> <jv>tuple2</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, <jk>null</jk>); 185 * Optional<Integer> <jv>second2</jv> = <jv>tuple2</jv>.optB(); <jc>// Returns Optional.empty()</jc> 186 * </p> 187 * 188 * @return The second value wrapped in an Optional, or Optional.empty() if the value is null. 189 */ 190 public Optional<B> optB() { 191 return o(b); 192 } 193 194 @Override /* Overridden from Object */ 195 public int hashCode() { 196 return hashCode; 197 } 198}